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;
import java.awt.geom.*;
import static x30_pkg.x30_util.DynamicObject;
import java.text.*;
import java.text.NumberFormat;
import java.util.TimeZone;
class main {
static int totalSSILines(Iterable extends AbstractSSI> l) {
int sum = 0;
for (var ssi : unnullForIteration(l)) if (ssi != null) sum += ssi.height();
return sum;
}
static String unnullForIteration(String s) {
return s == null ? "" : s;
}
static Collection unnullForIteration(Collection l) {
return l == null ? immutableEmptyList() : l;
}
static List unnullForIteration(List l) { return l == null ? immutableEmptyList() : l; }
static byte[] unnullForIteration(byte[] l) { return l == null ? emptyByteArray() : l; }
static int[] unnullForIteration(int[] l) { return l == null ? emptyIntArray() : l; }
static char[] unnullForIteration(char[] l) { return l == null ? emptyCharArray() : l; }
static double[] unnullForIteration(double[] l) { return l == null ? emptyDoubleArray() : l; }
static short[] unnullForIteration(short[] l) { return l == null ? emptyShortArray() : l; }
static Map unnullForIteration(Map l) {
return l == null ? immutableEmptyMap() : l;
}
static Iterable unnullForIteration(Iterable i) {
return i == null ? immutableEmptyList() : i;
}
static A[] unnullForIteration(A[] a) {
return a == null ? (A[]) emptyObjectArray() : a;
}
static BitSet unnullForIteration(BitSet b) {
return b == null ? new BitSet() : b;
}
static Pt unnullForIteration(Pt p) {
return p == null ? new Pt() : p;
}
//ifclass Symbol
static Symbol unnullForIteration(Symbol s) {
return s == null ? emptySymbol() : s;
}
//endif
static Pair unnullForIteration(Pair p) {
return p != null ? p : new Pair(null, null);
}
static long unnullForIteration(Long l) { return l == null ? 0L : l; }
static List immutableEmptyList() {
return Collections.emptyList();
}
static byte[] emptyByteArray_a = new byte[0];
static byte[] emptyByteArray() { return emptyByteArray_a; }
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 short[] emptyShortArray = new short[0];
static short[] emptyShortArray() { return emptyShortArray; }
static Map immutableEmptyMap() {
return Collections.emptyMap();
}
static Object[] emptyObjectArray_a = new Object[0];
static Object[] emptyObjectArray() { return emptyObjectArray_a; }
static Symbol emptySymbol_value;
static Symbol emptySymbol() {
if (emptySymbol_value == null) emptySymbol_value = symbol("");
return emptySymbol_value;
}
static WeakHasherMap symbol_map = new WeakHasherMap(new Hasher() {
public int hashCode(Symbol symbol) { return symbol.text.hashCode(); }
public boolean equals(Symbol a, Symbol b) {
if (a == null) return b == null;
return b != null && eq(a.text, b.text);
}
});
static Symbol symbol(String s) {
if (s == null) return null;
synchronized(symbol_map) {
// TODO: avoid object creation by passing the string to findKey
Symbol symbol = new Symbol(s, true);
Symbol existingSymbol = symbol_map.findKey(symbol);
if (existingSymbol == null)
symbol_map.put(existingSymbol = symbol, true);
return existingSymbol;
}
}
static Symbol symbol(CharSequence s) {
if (s == null) return null;
if (s instanceof Symbol) return (Symbol) s;
if (s instanceof String) return symbol((String) s);
return symbol(str(s));
}
static Symbol symbol(Object o) {
return symbol((CharSequence) o);
}
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 String str(Object o) {
return o == null ? "null" : o.toString();
}
static String str(char[] c) {
return new String(c);
}
static String str(char[] c, int offset, int count) {
return new String(c, offset, count);
}
static interface Hasher {
int hashCode(A a);
boolean equals(A a, A b);
}
// for the version with MasterSymbol (used WAY back in "Smart Bot"!) see #1010608
static class Symbol implements CharSequence {
String text;
Symbol() {}
Symbol(String text, boolean dummy) {
this.text = text;} // weird signature to prevent accidental calling
public int hashCode() { return _hashCode(text); }
public String toString() { return text; }
public boolean equals(Object o) {
return this == o;
}
// implementation of CharSequence methods
public int length() { return text.length(); }
public char charAt(int index) { return text.charAt(index); }
public CharSequence subSequence(int start, int end) {
return text.substring(start, end);
}
}
/*
* @(#)WeakHashMap.java 1.5 98/09/30
*
* Copyright 1998 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
*/
// From https://github.com/mernst/plume-lib/blob/df0bfafc3c16848d88f4ea0ef3c8bf3367ae085e/java/src/plume/WeakHasherMap.java
static final class WeakHasherMap extends AbstractMap implements Map {
private Hasher hasher = null;
/*@Pure*/
private boolean keyEquals(Object k1, Object k2) {
return (hasher==null ? k1.equals(k2)
: hasher.equals(k1, k2));
}
/*@Pure*/
private int keyHashCode(Object k1) {
return (hasher==null ? k1.hashCode()
: hasher.hashCode(k1));
}
// The WeakKey class can't be static because it depends on the hasher.
// That in turn means that its methods can't be static.
// However, I need to be able to call the methods such as create() that
// were static in the original version of this code.
// This finesses that.
private /*@Nullable*/ WeakKey WeakKeyCreate(K k) {
if (k == null) return null;
else return new WeakKey(k);
}
private /*@Nullable*/ WeakKey WeakKeyCreate(K k, ReferenceQueue super K> q) {
if (k == null) return null;
else return new WeakKey(k, q);
}
// Cannot be a static class: uses keyHashCode() and keyEquals()
private final class WeakKey extends WeakReference {
private int hash; /* Hashcode of key, stored here since the key
may be tossed by the GC */
private WeakKey(K k) {
super(k);
hash = keyHashCode(k);
}
private /*@Nullable*/ WeakKey create(K k) {
if (k == null) return null;
else return new WeakKey(k);
}
private WeakKey(K k, ReferenceQueue super K> q) {
super(k, q);
hash = keyHashCode(k);
}
private /*@Nullable*/ WeakKey create(K k, ReferenceQueue super K> q) {
if (k == null) return null;
else return new WeakKey(k, q);
}
/* A WeakKey is equal to another WeakKey iff they both refer to objects
that are, in turn, equal according to their own equals methods */
/*@Pure*/
@Override
public boolean equals(/*@Nullable*/ Object o) {
if (o == null) return false; // never happens
if (this == o) return true;
// This test is illegal because WeakKey is a generic type,
// so use the getClass hack below instead.
// if (!(o instanceof WeakKey)) return false;
if (!(o.getClass().equals(WeakKey.class))) return false;
Object t = this.get();
@SuppressWarnings("unchecked")
Object u = ((WeakKey)o).get();
if ((t == null) || (u == null)) return false;
if (t == u) return true;
return keyEquals(t, u);
}
/*@Pure*/
@Override
public int hashCode() {
return hash;
}
}
/* Hash table mapping WeakKeys to values */
private HashMap hash;
/* Reference queue for cleared WeakKeys */
private ReferenceQueue super K> queue = new ReferenceQueue();
/* Remove all invalidated entries from the map, that is, remove all entries
whose keys have been discarded. This method should be invoked once by
each public mutator in this class. We don't invoke this method in
public accessors because that can lead to surprising
ConcurrentModificationExceptions. */
@SuppressWarnings("unchecked")
private void processQueue() {
WeakKey wk;
while ((wk = (WeakKey)queue.poll()) != null) { // unchecked cast
hash.remove(wk);
}
}
/* -- Constructors -- */
/**
* Constructs a new, empty WeakHashMap
with the given
* initial capacity and the given load factor.
*
* @param initialCapacity the initial capacity of the
* WeakHashMap
*
* @param loadFactor the load factor of the WeakHashMap
*
* @throws IllegalArgumentException If the initial capacity is less than
* zero, or if the load factor is
* nonpositive
*/
public WeakHasherMap(int initialCapacity, float loadFactor) {
hash = new HashMap(initialCapacity, loadFactor);
}
/**
* Constructs a new, empty WeakHashMap
with the given
* initial capacity and the default load factor, which is
* 0.75
.
*
* @param initialCapacity the initial capacity of the
* WeakHashMap
*
* @throws IllegalArgumentException If the initial capacity is less than
* zero
*/
public WeakHasherMap(int initialCapacity) {
hash = new HashMap(initialCapacity);
}
/**
* Constructs a new, empty WeakHashMap
with the default
* capacity and the default load factor, which is 0.75
.
*/
public WeakHasherMap() {
hash = new HashMap();
}
/**
* Constructs a new, empty WeakHashMap
with the default
* capacity and the default load factor, which is 0.75
.
* The WeakHashMap
uses the specified hasher for hashing
* keys and comparing them for equality.
* @param h the Hasher to use when hashing values for this map
*/
public WeakHasherMap(Hasher h) {
hash = new HashMap();
hasher = h;
}
/* -- Simple queries -- */
/**
* Returns the number of key-value mappings in this map.
* Note: In contrast to most implementations of the
* Map
interface, the time required by this operation is
* linear in the size of the map.
*/
/*@Pure*/
@Override
public int size() {
return entrySet().size();
}
/**
* Returns true
if this map contains no key-value mappings.
*/
/*@Pure*/
@Override
public boolean isEmpty() {
return entrySet().isEmpty();
}
/**
* Returns true
if this map contains a mapping for the
* specified key.
*
* @param key the key whose presence in this map is to be tested
*/
/*@Pure*/
@Override
public boolean containsKey(Object key) {
@SuppressWarnings("unchecked")
K kkey = (K) key;
return hash.containsKey(WeakKeyCreate(kkey));
}
/* -- Lookup and modification operations -- */
/**
* Returns the value to which this map maps the specified key
.
* If this map does not contain a value for this key, then return
* null
.
*
* @param key the key whose associated value, if any, is to be returned
*/
/*@Pure*/
@Override
public /*@Nullable*/ V get(Object key) { // type of argument is Object, not K
@SuppressWarnings("unchecked")
K kkey = (K) key;
return hash.get(WeakKeyCreate(kkey));
}
/**
* Updates this map so that the given key
maps to the given
* value
. If the map previously contained a mapping for
* key
then that mapping is replaced and the previous value is
* returned.
*
* @param key the key that is to be mapped to the given
* value
* @param value the value to which the given key
is to be
* mapped
*
* @return the previous value to which this key was mapped, or
* null
if if there was no mapping for the key
*/
@Override
public V put(K key, V value) {
processQueue();
return hash.put(WeakKeyCreate(key, queue), value);
}
/**
* Removes the mapping for the given key
from this map, if
* present.
*
* @param key the key whose mapping is to be removed
*
* @return the value to which this key was mapped, or null
if
* there was no mapping for the key
*/
@Override
public V remove(Object key) { // type of argument is Object, not K
processQueue();
@SuppressWarnings("unchecked")
K kkey = (K) key;
return hash.remove(WeakKeyCreate(kkey));
}
/**
* Removes all mappings from this map.
*/
@Override
public void clear() {
processQueue();
hash.clear();
}
/* -- Views -- */
/* Internal class for entries */
// This can't be static, again because of dependence on hasher.
@SuppressWarnings("TypeParameterShadowing")
private final class Entry implements Map.Entry {
private Map.Entry ent;
private K key; /* Strong reference to key, so that the GC
will leave it alone as long as this Entry
exists */
Entry(Map.Entry ent, K key) {
this.ent = ent;
this.key = key;
}
/*@Pure*/
@Override
public K getKey() {
return key;
}
/*@Pure*/
@Override
public V getValue() {
return ent.getValue();
}
@Override
public V setValue(V value) {
return ent.setValue(value);
}
/*@Pure*/
private boolean keyvalEquals(K o1, K o2) {
return (o1 == null) ? (o2 == null) : keyEquals(o1, o2);
}
/*@Pure*/
private boolean valEquals(V o1, V o2) {
return (o1 == null) ? (o2 == null) : o1.equals(o2);
}
/*@Pure*/
@SuppressWarnings("NonOverridingEquals")
public boolean equals(Map.Entry e /* Object o*/) {
// if (! (o instanceof Map.Entry)) return false;
// Map.Entry e = (Map.Entry)o;
return (keyvalEquals(key, e.getKey())
&& valEquals(getValue(), e.getValue()));
}
/*@Pure*/
@Override
public int hashCode() {
V v;
return (((key == null) ? 0 : keyHashCode(key))
^ (((v = getValue()) == null) ? 0 : v.hashCode()));
}
}
/* Internal class for entry sets */
private final class EntrySet extends AbstractSet> {
Set> hashEntrySet = hash.entrySet();
@Override
public Iterator> iterator() {
return new Iterator>() {
Iterator> hashIterator = hashEntrySet.iterator();
Map.Entry next = null;
@Override
public boolean hasNext() {
while (hashIterator.hasNext()) {
Map.Entry ent = hashIterator.next();
WeakKey wk = ent.getKey();
K k = null;
if ((wk != null) && ((k = wk.get()) == null)) {
/* Weak key has been cleared by GC */
continue;
}
next = new Entry(ent, k);
return true;
}
return false;
}
@Override
public Map.Entry next() {
if ((next == null) && !hasNext())
throw new NoSuchElementException();
Map.Entry e = next;
next = null;
return e;
}
@Override
public void remove() {
hashIterator.remove();
}
};
}
/*@Pure*/
@Override
public boolean isEmpty() {
return !(iterator().hasNext());
}
/*@Pure*/
@Override
public int size() {
int j = 0;
for (Iterator> i = iterator(); i.hasNext(); i.next()) j++;
return j;
}
@Override
public boolean remove(Object o) {
processQueue();
if (!(o instanceof Map.Entry,?>)) return false;
@SuppressWarnings("unchecked")
Map.Entry e = (Map.Entry)o; // unchecked cast
Object ev = e.getValue();
WeakKey wk = WeakKeyCreate(e.getKey());
Object hv = hash.get(wk);
if ((hv == null)
? ((ev == null) && hash.containsKey(wk)) : hv.equals(ev)) {
hash.remove(wk);
return true;
}
return false;
}
/*@Pure*/
@Override
public int hashCode() {
int h = 0;
for (Iterator> i = hashEntrySet.iterator(); i.hasNext(); ) {
Map.Entry ent = i.next();
WeakKey wk = ent.getKey();
Object v;
if (wk == null) continue;
h += (wk.hashCode()
^ (((v = ent.getValue()) == null) ? 0 : v.hashCode()));
}
return h;
}
}
private /*@Nullable*/ Set> entrySet = null;
/**
* Returns a Set
view of the mappings in this map.
*/
/*@SideEffectFree*/
@Override
public Set> entrySet() {
if (entrySet == null) entrySet = new EntrySet();
return entrySet;
}
// find matching key
K findKey(Object key) {
processQueue();
K kkey = (K) key;
// TODO: use replacement for HashMap to avoid reflection
WeakKey wkey = WeakKeyCreate(kkey);
WeakKey found = hashMap_findKey(hash, wkey);
return found == null ? null : found.get();
}
}
abstract static class AbstractSSI implements G2Drawable {
AbstractSSI() {}
// Color in 15 bit
final public AbstractSSI setHi15color(short hi15color){ return hi15color(hi15color); }
public AbstractSSI hi15color(short hi15color) { this.hi15color = hi15color; return this; } final public short getHi15color(){ return hi15color(); }
public short hi15color() { return hi15color; }
short hi15color;
Color color() {
return main.hi15color(hi15color);
}
AbstractSSI color(RGB color) {
return hi15color(rgbToHi15(color));
}
final SSI render(){ return toSSI(); }
abstract SSI toSSI();
void copyAbstractSSI(AbstractSSI dest) {
dest.hi15color(hi15color);
}
// size estimate after compression
abstract int sizeInInts();
}
// SSI - Simple Scanline Image
// A partial image consisting of exactly one horizontal line per row.
// All pixels are of the same color.
// The important values are y1 and data
static class SSI extends AbstractSSI implements HasBounds, ByteIO {
SSI() {}
// core value (mandatory): first row occupied
final public SSI setY1(short y1){ return y1(y1); }
public SSI y1(short y1) { this.y1 = y1; return this; } final public short getY1(){ return y1(); }
public short y1() { return y1; }
short y1;
// core value (mandatory): x coordinates per row
// x_left is inclusive, x_right is exclusive
// all x coordinates are absolute in image
//
// Order in array:
// x_left at y1, x_right at y1,
// x_left at y1+1, x_right at y1+1, ,,,
final public SSI setData(short[] data){ return data(data); }
public SSI data(short[] data) { this.data = data; return this; } final public short[] getData(){ return data(); }
public short[] data() { return data; }
short[] data;
SSI(int y1, int y2) {
init(y1, y2);
}
void init(int y1, int y2) {
this.y1 = toShort_enforce(y1);
data = newShortArrayOrNull((y2-y1)*2);
}
// all coordinates passed in are absolute
void set(int y, int xLeft, int xRight) {
data[(y-y1)*2] = toShort_enforce(xLeft);
data[(y-y1)*2+1] = toShort_enforce(xRight);
}
// all coordinates passed in and out are absolute
int getLeft (int y) { return data[(y-y1)*2]; }
int getRight(int y) { return data[(y-y1)*2+1]; }
// bounds can be calculated quickly from core values & are then cached
public Rect bounds_cache;
public Rect bounds() { if (bounds_cache == null) bounds_cache = bounds_load(); return bounds_cache;}
public Rect bounds_load() {
int x1 = Integer.MAX_VALUE, x2 = 0;
for (int i = 0; i < l(data); i += 2) {
x1 = min(x1, data[i]);
x2 = max(x2, data[i+1]);
}
return rectFromPoints(x1, y1, x2, y2());
}
// same with number of pixels
int numberOfPixels_cache = -1;
public int numberOfPixels() {
if (numberOfPixels_cache < 0) {
int n = 0;
for (int i = 0; i < l(data); i += 2)
n += max(0, data[i+1]-data[i]);
numberOfPixels_cache = n;
}
return numberOfPixels_cache;
}
public int getHeight() { return l(data)/2; }
int y2() { return y1+height(); }
boolean contains(Pt p) { return contains(p.x, p.y); }
boolean contains(int x, int y) {
if (y < y1) return false;
int i = (y-y1)*2;
if (i >= l(data)) return false;
return x >= data[i] && x < data[i+1];
}
SSI color(RGB color) { super.color(color); return this; }
public SSI hi15color(short hi15color) { super.hi15color(hi15color); return this; }
public void drawOn(Graphics2D g) {
g.setColor(color());
int h = height();
for (int y = 0; y < h; y++) {
int x1 = data[y*2], x2 = data[y*2+1];
if (x1 < x2)
g.drawLine(x1, y1+y, x2-1, y1+y);
}
}
// doesn't set a color
// TODO
public void drawOutline(Graphics2D g) {
int h = height();
IntRange last = intRange(0, 0);
for (int y = 0; y < h; y++) {
IntRange r = intRange(data[y*2], data[y*2+1]);
// anything to draw in this line?
if (nempty(r)) {
if (empty(last))
// draw full line if just starting
g.drawLine(r.start, y1+y, r.end-1, y1+y);
else {
// We make a line from the left boundary that if possible goes only to last.start but is at least 1 pixel wide
int xleft = max(r.start, last.start-1);
// Same from the right boundary
int xright = min(r.end-1, last.end);
g.drawLine(r.start, y1+y, xleft, y1+y);
g.drawLine(xright, y1+y, r.end-1, y1+y);
}
}
last = r;
}
}
SSI topPart(int nLines) {
SSI ssi = new SSI(y1, toShort_enforce(y1+nLines)).hi15color(hi15color);
arrayCopy(data, ssi.data, 0, l(ssi.data));
return ssi;
}
// an SSI is coherent if all scanlines are non-empty
// and the SSI is one region
boolean coherent() { return coherent(false); }
boolean coherent(boolean withDiagonals) {
int h = height();
for (int y = 0; y < h; y++) {
int i = y*2;
int x1 = data[i], x2 = data[i+1];
if (x1 >= x2) return false;
if (y > 0) {
int lastx1 = data[i-2], lastx2 = data[i-1];
if (withDiagonals) { lastx1--; lastx2++; }
if (!intRangesOverlap(x1, x2, lastx1, lastx2))
return false;
}
}
return true;
}
public void readWrite(ByteHead head) {
head.exchangeShort(() -> hi15color, color -> hi15color = color);
head.exchangeShort(() -> y1, y1 -> this.y1 = y1);
head.exchangeShort(() -> toShort_enforce(y2()), y2 -> init(y1, y2));
for (int i = 0; i < l(data); i++) {
var _i_1 = i;
head.exchangeShort(() -> data[_i_1], x -> data[_i_1] = x);
}
}
SSI toSSI() { return this; }
int sizeInInts() {
// y1, height, data
return 2+height()*2;
}
}
interface G2Drawable {
void drawOn(Graphics2D g);
default void drawOn(BufferedImage img) {
drawOn(img.createGraphics());
}
}
static class RGB {
// usually in range [0, 1]
public float r, g, b; // can't be final cause persistence
RGB() {}
public RGB(float r, float g, float b) {
this.r = r;
this.g = g;
this.b = b;
}
public RGB(double r, double g, double b) {
this.r = (float) r;
this.g = (float) g;
this.b = (float) b;
}
public RGB(double[] rgb) {
this(rgb[0], rgb[1], rgb[2]);
}
public RGB(int rgb) {
r = rgbRed(rgb)/255f;
g = rgbGreen(rgb)/255f;
b = rgbBlue(rgb)/255f;
}
public RGB(double brightness) {
this.r = this.g = this.b = max(0f, min(1f, (float) brightness));
}
public RGB(Color color) {
r = color.getRed()/255f;
g = color.getGreen()/255f;
b = color.getBlue()/255f;
}
// TODO: 3-char version
public RGB(String hex) {
int i = l(hex)-6;
r = Integer.parseInt(hex.substring(i, i+2), 16)/255f;
g = Integer.parseInt(hex.substring(i+2, i+4), 16)/255f;
b = Integer.parseInt(hex.substring(i+4, i+6), 16)/255f;
}
public float getComponent(int i) {
return i == 0 ? r : i == 1 ? g : b;
}
public int getInt(int i) {
return i == 0 ? redInt() : i == 1 ? greenInt() : blueInt();
}
public Color getColor() {
return new Color(r, g, b);
}
public static RGB newSafe(float r, float g, float b) {
return new RGB(Math.max(0, Math.min(1, r)), Math.max(0, Math.min(1, g)), Math.max(0, Math.min(1, b)));
}
int asInt() { return getColor().getRGB() & 0xFFFFFF; }
int getInt() { return getColor().getRGB() & 0xFFFFFF; }
int asIntWithAlpha() { return rgbInt(redInt(), greenInt(), blueInt()) | 0xFF000000; }
public float getBrightness() {
return (r+g+b)/3.0f;
}
public String getHexString() {
return Integer.toHexString(asInt() | 0xFF000000).substring(2).toUpperCase();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof RGB)) return false;
RGB rgb = (RGB) o;
if (Float.compare(rgb.b, b) != 0) return false;
if (Float.compare(rgb.g, g) != 0) return false;
if (Float.compare(rgb.r, r) != 0) return false;
return true;
}
@Override
public int hashCode() {
int result = (r != +0.0f ? Float.floatToIntBits(r) : 0);
result = 31 * result + (g != +0.0f ? Float.floatToIntBits(g) : 0);
result = 31 * result + (b != +0.0f ? Float.floatToIntBits(b) : 0);
return result;
}
public boolean isBlack() {
return r == 0f && g == 0f && b == 0f;
}
public boolean isWhite() {
return r == 1f && g == 1f && b == 1f;
}
public String toString() {
return getHexString();
}
int redInt() { return iround(r*255); }
int greenInt() { return iround(g*255); }
int blueInt() { return iround(b*255); }
static float brightnessToFloat(int brightness) { return brightness/255f; }
}
interface ByteIO {
void readWrite(ByteHead head);
default byte[] saveAsByteArray(){ return saveToByteArray(); }
default byte[] toByteArray(){ return saveToByteArray(); }
default byte[] saveToByteArray() { return saveToByteArray(new ByteHead()); }
default byte[] saveAsByteArray(ByteHead head){ return saveToByteArray(head); }
default byte[] toByteArray(ByteHead head){ return saveToByteArray(head); }
default byte[] saveToByteArray(ByteHead head) {
var baos = byteArrayOutputStream();
head.outputStream(baos);
readWrite(head);
head.finish();
return baos.toByteArray();
}
default String toHexString() {
return main.toHexString(toByteArray());
}
default File saveToFile(File file) {
OutputStream out = bufferedFileOutputStream(file); try {
var head = new ByteHead(out);
readWrite(head);
head.finish();
return file;
} finally { _close(out); }}
default ByteIO fromByteArray(byte[] data){ return load(data); }
default ByteIO load(byte[] data) {
readWrite(new ByteHead(new ByteArrayInputStream(data)));
return this;
}
default ByteIO load(File file) {
InputStream in = bufferedInputStream(file); try {
readWrite(new ByteHead(in));
return this;
} finally { _close(in); }}
}
static class Rect implements WidthAndHeight , IFieldsToList{
static final String _fieldOrder = "x y w h";
int x;
int y;
int w;
int h;
Rect() {}
Rect(int x, int y, int w, int h) {
this.h = h;
this.w = w;
this.y = y;
this.x = x;}
public boolean equals(Object o) {
if (!(o instanceof Rect)) return false;
Rect __1 = (Rect) o;
return x == __1.x && y == __1.y && w == __1.w && h == __1.h;
}
public int hashCode() {
int h = 2543108;
h = boostHashCombine(h, _hashCode(x));
h = boostHashCombine(h, _hashCode(y));
h = boostHashCombine(h, _hashCode(w));
h = boostHashCombine(h, _hashCode(h));
return h;
}
public Object[] _fieldsToList() { return new Object[] {x, y, w, h}; }
Rect(Rectangle r) {
x = r.x;
y = r.y;
w = r.width;
h = r.height;
}
Rect(Pt p, int w, int h) {
this.h = h;
this.w = w; x = p.x; y = p.y; }
Rect(Rect r) { x = r.x; y = r.y; w = r.w; h = r.h; }
final Rectangle getRectangle() {
return new Rectangle(x, y, w, h);
}
public String toString() {
return x + "," + y + " / " + w + "," + h;
}
final int x1() { return x; }
final int y1() { return y; }
final int x2() { return x + w; }
final int y2() { return y + h; }
final boolean contains(Pt p) {
return contains(p.x, p.y);
}
final boolean contains(int _x, int _y) {
return _x >= x && _y >= y && _x < x+w && _y < y+h;
}
final boolean contains(Rectangle r) {
return rectContains(this, r);
}
final boolean empty() { return w <= 0 || h <= 0; }
final public int getWidth() { return w; }
final public int getHeight() { return h; }
final public int area() { return w*h; }
WidthAndHeight widthAndHeight() { return main.widthAndHeight(w, h); }
}
static class Pt implements Comparable, IDoublePt {
int x, y;
Pt() {}
Pt(Point p) {
x = p.x;
y = p.y;
}
Pt(int x, int y) {
this.y = y;
this.x = x;}
Point getPoint() {
return new Point(x, y);
}
public boolean equals(Object o) {
return o instanceof Pt && x == ((Pt) o).x && y == ((Pt) o).y;
}
public int hashCode() {
return boostHashCombine(x, y);
}
// compare in scan order
public int compareTo(Pt p) {
if (y != p.y) return cmp(y, p.y);
return cmp(x, p.x);
}
public String toString() {
return x + ", " + y;
}
double length() { return sqrt(x*x+y*y); }
public Pt minus(Pt p) { return ptMinus(this, p); }
public double x_double() { return x; }
public double y_double() { return y; }
}
// it's unclear whether the end is inclusive or exclusive
// (usually exclusive I guess)
static class IntRange {
int start, end;
IntRange() {}
IntRange(int start, int end) {
this.end = end;
this.start = start;}
IntRange(IntRange r) { start = r.start; end = r.end; }
public boolean equals(Object o) { return stdEq2(this, o); }
public int hashCode() { return stdHash2(this); }
final int length() { return end-start; }
final boolean empty() { return start >= end; }
final boolean isEmpty() { return start >= end; }
static String _fieldOrder = "start end";
public String toString() { return "[" + start + ";" + end + "]"; }
}
// We use big-endian as DataOutputStream does
// (TODO: decide on an endianness!)
static class ByteHead /*is DataOutput*/ {
final public ByteHead setReadMode(boolean readMode){ return readMode(readMode); }
public ByteHead readMode(boolean readMode) { this.readMode = readMode; return this; } final public boolean getReadMode(){ return readMode(); }
public boolean readMode() { return readMode; }
boolean readMode = false;
final public ByteHead setWriteMode(boolean writeMode){ return writeMode(writeMode); }
public ByteHead writeMode(boolean writeMode) { this.writeMode = writeMode; return this; } final public boolean getWriteMode(){ return writeMode(); }
public boolean writeMode() { return writeMode; }
boolean writeMode = false;
final public InputStream getInputStream(){ return inputStream(); }
public InputStream inputStream() { return inputStream; }
InputStream inputStream;
final public OutputStream getOutputStream(){ return outputStream(); }
public OutputStream outputStream() { return outputStream; }
OutputStream outputStream;
final public ByteHead setByteCounter(long byteCounter){ return byteCounter(byteCounter); }
public ByteHead byteCounter(long byteCounter) { this.byteCounter = byteCounter; return this; } final public long getByteCounter(){ return byteCounter(); }
public long byteCounter() { return byteCounter; }
long byteCounter;
ByteHead() {}
ByteHead(InputStream inputStream) { inputStream(inputStream); }
ByteHead(OutputStream outputStream) { outputStream(outputStream); }
ByteHead inputStream(InputStream inputStream) { this.inputStream = inputStream; readMode(true); return this; }
ByteHead outputStream(OutputStream outputStream) { this.outputStream = outputStream; writeMode(true); return this; }
void write(byte[] data) { try {
ensureWriteMode();
{ if (outputStream != null) outputStream.write(data); }
byteCounter += data.length;
} catch (Exception __e) { throw rethrow(__e); } }
void writeLong(long l) {
writeInt((int) (l >> 32));
writeInt((int) l);
}
void writeInt(int i) {
write(i >> 24);
write(i >> 16);
write(i >> 8);
write(i);
}
void writeShort(int i) {
write(i >> 8);
write(i);
}
final void write(int i){ writeByte(i); }
void writeByte(int i) { try {
ensureWriteMode();
{ if (outputStream != null) outputStream.write(i); }
byteCounter++;
} catch (Exception __e) { throw rethrow(__e); } }
void writeASCII(char c) {
write(toASCII(c));
}
void writeASCII(String s) {
write(toASCII(s));
}
void exchangeASCII(String s) {
exchangeConstantBytes(toASCII(s));
}
void exchangeConstantBytes(byte[] data) {
for (int i = 0; i < l(data); i++)
exchangeByte(data[i]);
}
long readLong() {
long i = readInt() << 32;
return i | (readInt() & 0xFFFFFFFFL);
}
int readInt() {
int i = read() << 24;
i |= read() << 16;
i |= read() << 8;
return i | read();
}
short readShort() {
int i = read() << 8;
return (short) (i | read());
}
// -1 for EOF
final int read(){ return readByte(); }
int readByte() { try {
ensureReadMode();
++byteCounter;
return inputStream.read();
} catch (Exception __e) { throw rethrow(__e); } }
void ensureReadMode() {
if (!readMode) throw fail("Not in read mode");
}
void ensureWriteMode() {
if (!writeMode) throw fail("Not in write mode");
}
// exchange = read or write depending on mode
void exchangeByte(byte getter, IVF1 setter) {
exchangeByte(() -> getter, setter);
}
void exchangeByte(IF0 getter, IVF1 setter) {
if (writeMode())
writeByte(getter.get());
if (readMode())
setter.get(toUByte(readByte()));
}
void exchangeShort(IF0 getter, IVF1 setter) {
if (writeMode())
writeShort(getter.get());
if (readMode())
setter.get(readShort());
}
void exchangeLong(IVar var) {
exchangeLong(var.getter(), var.setter());
}
void exchangeLong(IF0 getter, IVF1 setter) {
if (writeMode())
writeLong(getter.get());
if (readMode())
setter.get(readLong());
}
void exchangeByte(byte i) {
exchangeByte(() -> i, j -> assertEquals(i, j));
}
void exchangeInt(int i) {
exchangeInt(() -> i, j -> assertEquals(i, j));
}
void exchangeInt(IF0 getter, IVF1 setter) {
if (writeMode())
writeInt(getter.get());
if (readMode())
setter.get(readInt());
}
void exchange(ByteIO writable) {
if (writable != null) writable.readWrite(this);
}
void exchangeAll(Iterable extends ByteIO> writables) {
if (writables != null)
for (var writable : writables)
exchange(writable);
}
// write size in bytes of element first (as int),
// then the element itself.
// upon reading, size is actually ignored.
void exchangeWithSize(ByteIO writable) {
if (writeMode()) {
byte[] data = writable.saveToByteArray();
writeInt(l(data));
write(data);
}
if (readMode()) {
int n = readInt();
writable.readWrite(this);
}
}
void finish() {}
}
interface HasBounds extends WidthAndHeight {
Rect bounds();
default int getWidth() { return bounds().h; }
default int getHeight() { return bounds().w; }
}
public static interface IF0 {
A get();
}
static interface IFieldsToList {
Object[] _fieldsToList();
}
static interface IVF1 {
void get(A a);
}
interface IDoublePt {
public double x_double();
public double y_double();
}
static interface IVar extends IF0 {
void set(A a);
A get();
// reified type of value (if available)
default Class getType() { return null; }
default IF0 getter() { return () -> get(); }
default IVF1 setter() { return __1 -> set(__1); }
default boolean has() { return get() != null; }
default void clear() { set(null); }
}
static interface WidthAndHeight {
default int w(){ return getWidth(); }
default int width(){ return getWidth(); }
int getWidth();
default int h(){ return getHeight(); }
default int height(){ return getHeight(); }
int getHeight();
public default Rect bounds() { return rect(0, 0, getWidth(), getHeight()); }
default int area() { return toInt(areaAsLong()); }
default long areaAsLong() { return longMul(w(), h()); }
}
static int _hashCode(Object a) {
return a == null ? 0 : a.hashCode();
}
static Set> entrySet(Map map) {
return _entrySet(map);
}
// 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 void put(Map map, A a, B b) {
if (map != null) map.put(a, b);
}
static void put(List l, int i, A a) {
if (l != null && i >= 0 && i < l(l)) l.set(i, a);
}
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 Iterator iterator(Iterable c) {
return c == null ? emptyIterator() : c.iterator();
}
static Method hashMap_findKey_method;
static A hashMap_findKey(HashMap map, Object key) { try {
if (hashMap_findKey_method == null)
hashMap_findKey_method = findMethodNamed(HashMap.class, "getNode");
Map.Entry entry = (Map.Entry) hashMap_findKey_method.invoke(map, hashMap_internalHash(key), key);
// java.util.Map.Entry entry = (java.util.Map.Entry) call(hash, 'getNode, hashMap_internalHash(key), wkey);
return entry == null ? null : entry.getKey();
} catch (Exception __e) { throw rethrow(__e); } }
static Color hi15color(short hi15) {
return hi15ToColor_clean(hi15);
}
static java.awt.Color color(String hex) {
return awtColor(hex);
}
static short rgbToHi15(RGB rgb) {
return rgbIntToHi15(rgb.asInt());
}
static short toShort_enforce(long l) {
if (l != (short) l) throw fail("Too large for short: " + l);
return (short) l;
}
static short[] newShortArrayOrNull(int n) {
return n <= 0 ? null : new short[n];
}
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(IntRange r) { return r == null ? 0 : r.length(); }
static int l(AppendableChain a) { return a == null ? 0 : a.size; }
static int min(int a, int b) {
return Math.min(a, b);
}
static long min(long a, long b) {
return Math.min(a, b);
}
static float min(float a, float b) { return Math.min(a, b); }
static float min(float a, float b, float c) { return min(min(a, b), c); }
static double min(double a, double b) {
return Math.min(a, b);
}
static double min(double[] c) {
double x = Double.MAX_VALUE;
for (double d : c) x = Math.min(x, d);
return x;
}
static float min(float[] c) {
float x = Float.MAX_VALUE;
for (float d : c) x = Math.min(x, d);
return x;
}
static byte min(byte[] c) {
byte x = 127;
for (byte d : c) if (d < x) x = d;
return x;
}
static short min(short[] c) {
short x = 0x7FFF;
for (short d : c) if (d < x) x = d;
return x;
}
static int min(int[] c) {
int x = Integer.MAX_VALUE;
for (int d : c) if (d < x) x = d;
return x;
}
static int max(int a, int b) { return Math.max(a, b); }
static int max(int a, int b, int c) { return max(max(a, b), c); }
static long max(int a, long b) { return Math.max((long) a, b); }
static long max(long a, long b) { return Math.max(a, b); }
static double max(int a, double b) { return Math.max((double) a, b); }
static float max(float a, float b) { return Math.max(a, b); }
static double max(double a, double b) { return Math.max(a, b); }
static > A max (Iterable l) {
A max = null;
var it = iterator(l);
if (it.hasNext()) {
max = it.next();
while (it.hasNext()) {
A a = it.next();
if (cmp(a, max) > 0)
max = a;
}
}
return max;
}
/*Nah.
static int max(Collection c) {
int x = Integer.MIN_VALUE;
for (int i : c) x = max(x, i);
ret x;
}*/
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;
}
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;
}
static byte max(byte[] c) {
byte x = -128;
for (byte d : c) if (d > x) x = d;
return x;
}
static short max(short[] c) {
short x = -0x8000;
for (short d : c) if (d > x) x = d;
return x;
}
static int max(int[] c) {
int x = Integer.MIN_VALUE;
for (int d : c) if (d > x) x = d;
return x;
}
static > A max(A a, A b) {
return cmp(a, b) >= 0 ? a : b;
}
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);
}
static int y2(Rectangle r) {
return r.y+r.height;
}
static boolean contains(Collection c, Object o) {
return c != null && c.contains(o);
}
static boolean contains(Iterable it, Object a) {
if (it != null)
for (Object o : it)
if (eq(a, o))
return true;
return false;
}
static boolean contains(Object[] x, Object o) {
if (x != null)
for (Object a : x)
if (eq(a, o))
return true;
return false;
}
static boolean contains(String s, char c) {
return s != null && s.indexOf(c) >= 0;
}
static boolean contains(String s, String b) {
return s != null && s.indexOf(b) >= 0;
}
static boolean contains(BitSet bs, int i) {
return bs != null && bs.get(i);
}
static boolean contains(Producer p, A a) {
if (p != null && a != null) while (true) {
A x = p.next();
if (x == null) break;
if (eq(x, a)) return true;
}
return false;
}
static boolean contains(Rect r, Pt p) { return rectContains(r, p); }
static IntRange intRange(int start, int end) {
return new IntRange(start, end);
}
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(BitSet bs) { return !empty(bs); }
static boolean nempty(Map m) {
return !empty(m);
}
static boolean nempty(Iterator i) {
return i != null && i.hasNext();
}
static boolean nempty(IMultiMap mm) { return mm != null && mm.size() != 0; }
static boolean nempty(Object o) { return !empty(o); }
static boolean nempty(IntRange r) { return !empty(r); }
static boolean nempty(Rect r) { return r != null && r.w != 0 && r.h != 0; }
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(BitSet bs) { return bs == null || bs.isEmpty(); }
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(IMultiMap mm) { return mm == null || mm.size() == 0; }
static boolean empty(File f) { return getFileSize(f) == 0; }
static boolean empty(IntRange r) { return r == null || r.empty(); }
static boolean empty(Rect r) { return !(r != null && r.w != 0 && r.h != 0); }
static boolean empty(Chain c) { return c == null; }
static boolean empty(AppendableChain c) { return c == null; }
static void arrayCopy(Object[] a, Object[] b) {
arraycopy(a, b);
}
static void arrayCopy(Object src, Object dest, int destPos, int n) { arrayCopy(src, 0, dest, destPos, n); }
static void arrayCopy(Object src, int srcPos, Object dest, int destPos, int n) {
arraycopy(src, srcPos, dest, destPos, n);
}
static boolean intRangesOverlap(IntRange a, IntRange b) {
return intersectIntRanges(a, b) != null;
}
static boolean intRangesOverlap(int a1, int a2, int b1, int b2) {
return intRangesOverlap(intRange(a1, a2), intRange(b1, b2));
}
static int rgbRed(int rgb) {
return (rgb >> 16) & 0xFF;
}
static int rgbGreen(int rgb) {
return (rgb >> 8) & 0xFF;
}
static int rgbBlue(int rgb) {
return rgb & 0xFF;
}
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 int rgbInt(int r, int g, int b) {
return (clamp(r, 0, 255) << 16) | (clamp(g, 0, 255) << 8) | clamp(b, 0, 255);
}
static int rgbInt(byte r, byte g, byte b) {
return (ubyteToInt(r) << 16) | (ubyteToInt(g) << 8) | ubyteToInt(b);
}
static int asInt(Object o) {
return toInt(o);
}
static int iround(double d) {
return (int) Math.round(d);
}
static int iround(Number n) {
return iround(toDouble(n));
}
static ByteArrayOutputStream byteArrayOutputStream() {
return new ByteArrayOutputStream();
}
static String toHexString(byte[] a) {
return bytesToHex(a);
}
static String toHexString(List a) {
return bytesToHex(byteListToArray(a));
}
static byte[] toByteArray(ByteArrayOutputStream baos) {
return baos == null ? null : baos.toByteArray();
}
static byte[] toByteArray(Iterator extends Number> it) {
ByteBuffer buf = new ByteBuffer();
while (it.hasNext())
buf.add((byte) it.next().intValue());
return buf.toByteArray();
}
static byte[] toByteArray(Collection extends Number> it) {
int n = l(it), i = 0;
byte[] a = new byte[n];
for (var x : it)
a[i++] = (byte) x.intValue();
return a;
}
static byte[] toByteArray(Object o) {
if (o == null) return null;
if (o instanceof byte[]) return ((byte[]) o);
if (o instanceof Iterator)
return toByteArray((Iterator) o);
if (o instanceof Collection)
return toByteArray((Collection) ((Collection) o));
// not sure what else to put here
throw fail("toByteArray", o);
}
static BufferedOutputStream bufferedFileOutputStream(File f) { try {
return bufferedOutputStream(newFileOutputStream(f));
} catch (Exception __e) { throw rethrow(__e); } }
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 Object load(String varName) {
readLocally(varName);
return get(mc(), varName);
}
static Object load(String progID, String varName) {
readLocally(progID, varName);
return get(mc(), varName);
}
static int bufferedInputStream_bufferSize = 65536;
static BufferedInputStream bufferedInputStream(int bufSize, File f) { try {
return bufferedInputStream(bufSize, newFileInputStream(f));
} catch (Exception __e) { throw rethrow(__e); } }
static BufferedInputStream bufferedInputStream(File f) { try {
return bufferedInputStream(newFileInputStream(f));
} catch (Exception __e) { throw rethrow(__e); } }
static BufferedInputStream bufferedInputStream(InputStream in) {
return new BufferedInputStream(in, bufferedInputStream_bufferSize);
}
static BufferedInputStream bufferedInputStream(int bufSize, InputStream in) {
return new BufferedInputStream(in, bufSize);
}
static int boostHashCombine(int a, int b) {
return a ^ (b + 0x9e3779b9 + (a << 6) + (a >>> 2));
// OLD (changed) 2022/3/10: ret a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2));
}
static boolean rectContains(int x1, int y1, int w, int h, Pt p) {
return p.x >= x1 && p.y >= y1 && p.x < x1+w && p.y < y1+h;
}
static boolean rectContains(Rect a, Rect b) {
return b.x >= a.x && b.y >= a.y && b.x2() <= a.x2() && b.y2() <= a.y2();
}
static boolean rectContains(Rect a, Rectangle b) {
return rectContains(a, toRect(b));
}
static boolean rectContains(Rect a, int x, int y) {
return a != null && a.contains(x, y);
}
static boolean rectContains(Rect a, Pt p) {
return a != null && p != null && a.contains(p);
}
static WidthAndHeight widthAndHeight(BufferedImage image) {
return image == null ? null : widthAndHeight(image.getWidth(), image.getHeight());
}
static WidthAndHeightImpl widthAndHeight(int w, int h) {
return new WidthAndHeightImpl(w, h);
}
static int cmp(Number a, Number b) {
return a == null ? b == null ? 0 : -1 : cmp(a.doubleValue(), b.doubleValue());
}
static int cmp(double a, double b) {
return a < b ? -1 : a == b ? 0 : 1;
}
static int cmp(int a, int b) {
return a < b ? -1 : a == b ? 0 : 1;
}
static int cmp(long a, long b) {
return a < b ? -1 : a == b ? 0 : 1;
}
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);
}
static double sqrt(double x) {
return Math.sqrt(x);
}
static Pt ptMinus(Pt a, Pt b) {
if (b == null) return a;
return new Pt(a.x-b.x, a.y-b.y);
}
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;
}
static int stdHash2(Object a) {
if (a == null) return 0;
return stdHash(a, toStringArray(allFields(a)));
}
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 byte[] toASCII(String s) {
return s.getBytes(java.nio.charset.StandardCharsets.US_ASCII);
}
static byte toASCII(char c) {
return toASCII(str(new char[] {c}))[0];
}
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 byte toUByte(int i) {
if (!isUByte(i))
throw fail("Not a u-byte: " + i);
return (byte) i;
}
static A assertEquals(Object x, A y) {
return assertEquals("", x, y);
}
static 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;
}
static String getType(Object o) {
return getClassName(o);
}
static A set(A o, String field, Object value) {
if (o == null) return null;
if (o instanceof Class) set((Class) o, field, value);
else try {
Field f = set_findField(o.getClass(), field);
makeAccessible(f);
smartSet(f, o, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
return o;
}
static void set(Class c, String field, Object value) {
if (c == null) return;
try {
Field f = set_findStaticField(c, field);
makeAccessible(f);
smartSet(f, null, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
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());
}
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());
}
static void set(BitSet bs, int idx) {
{ if (bs != null) bs.set(idx); }
}
static int getWidth(Component c) {
return c == null ? 0 : (int) swingCall(c, "getWidth");
}
static int getHeight(Component c) {
return c == null ? 0 : (int) swingCall(c, "getHeight");
}
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 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);
if (o instanceof Boolean)
return boolToInt((Boolean) o);
throw fail("woot not int: " + getClassName(o));
}
static int toInt(long l) {
if (l != (int) l) throw fail("Too large for int: " + l);
return (int) l;
}
static long longMul(long a, long b) {
return a*b;
}
static Set> _entrySet(Map map) {
return map == null ? Collections.EMPTY_SET : map.entrySet();
}
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);
}
static RuntimeException asRuntimeException(Throwable t) {
if (t instanceof Error)
_handleError((Error) t);
return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}
static Iterator emptyIterator() {
return Collections.emptyIterator();
}
// This is a bit rough... finds static and non-static methods.
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);
}
static Method findMethodNamed(Class c, String method) {
while (c != null) {
for (Method m : c.getDeclaredMethods())
if (m.getName().equals(method)) {
makeAccessible(m);
return m;
}
c = c.getSuperclass();
}
return null;
}
static int hashMap_internalHash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
static Color hi15ToColor_clean(short hi15) {
return intToColorOpaque(hi15ToRGBInt_clean(hi15));
}
static java.awt.Color awtColor(String hex) {
byte[] b = bytesFromHex(dropPrefix("#", hex));
return new Color(ubyteToInt(b[0]), ubyteToInt(b[1]), ubyteToInt(b[2]));
}
static short rgbIntToHi15(int rgb) {
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
r >>= 3;
g >>= 3;
b >>= 3;
return (short) ((r << 10) | (g << 5) | b);
}
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 Rect pointsRect(int x1, int y1, int x2, int y2) {
return new Rect(x1, y1, x2-x1, y2-y1);
}
static long getFileSize(String path) {
return path == null ? 0 : new File(path).length();
}
static long getFileSize(File f) {
return f == null ? 0 : f.length();
}
static void arraycopy(Object[] a, Object[] b) {
if (a != null && b != null)
arraycopy(a, 0, b, 0, Math.min(a.length, b.length));
}
static void arraycopy(Object src, int srcPos, int destPos, int n) { arraycopy(src, srcPos, src, destPos, n); }
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int n) {
if (n != 0)
System.arraycopy(src, srcPos, dest, destPos, n);
}
static IntRange intersectIntRanges(IntRange a, IntRange b) {
int start = max(a.start, b.start);
int end = min(a.end, b.end);
return start <= end ? new IntRange(start, end) : null;
}
static List intersectIntRanges(Iterable l, IntRange b) {
return map(l, r -> intersectIntRanges(r, b));
}
static Color colorFromRGBA(int rgba) {
return new Color(rgba, true);
}
static float clamp(float x, float a, float b) {
return x < a ? a : x > b ? b : x;
}
static double clamp(double x, double a, double b) {
return x < a ? a : x > b ? b : x;
}
static int clamp(int x, int a, int b) {
return x < a ? a : x > b ? b : x;
}
static long clamp(long x, long a, long b) {
return x < a ? a : x > b ? b : x;
}
static int ubyteToInt(byte b) {
return b & 0x0FF;
}
static int ubyteToInt(char c) {
return c & 0x0FF;
}
static double toDouble(Object o) {
if (o instanceof Number)
return ((Number) o).doubleValue();
if (o instanceof BigInteger)
return ((BigInteger) o).doubleValue();
if (o instanceof String)
return parseDouble((String) o);
if (o == null) return 0.0;
throw fail(o);
}
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 BufferedOutputStream bufferedOutputStream(OutputStream out) {
if (out == null) return null;
if (out instanceof BufferedOutputStream) return ((BufferedOutputStream) out);
return new BufferedOutputStream(out, defaultBufferedOutputStreamSize());
}
static FileOutputStream newFileOutputStream(File path) throws IOException {
return newFileOutputStream(path.getPath());
}
static FileOutputStream newFileOutputStream(String path) throws IOException {
return newFileOutputStream(path, false);
}
static FileOutputStream newFileOutputStream(File path, boolean append) throws IOException {
return newFileOutputStream(path.getPath(), append);
}
static FileOutputStream newFileOutputStream(String path, boolean append) throws IOException {
mkdirsForFile(path);
FileOutputStream f = new FileOutputStream(path, append);
_registerIO(f, path, true);
return f;
}
static void readLocally(String progID, String varNames) {
readLocally2(mc(), progID, varNames);
}
static void readLocally(String varNames) {
readLocally2(mc(), programID(), varNames);
}
static void readLocally2(Object obj, String varNames) {
readLocally2(obj, programID(), varNames);
}
static int readLocally_stringLength;
static ThreadLocal readLocally2_allDynamic = new ThreadLocal();
static ThreadLocal readLocally2_classFinder = new ThreadLocal();
// read a string variable from standard storage
// does not overwrite variable contents if there is no file
static void readLocally2(Object obj, String progID, String varNames) { try {
boolean allDynamic = isTrue(getAndClearThreadLocal(readLocally2_allDynamic));
for (String variableName : javaTokC(varNames)) {
File textFile = new File(programDir(progID), variableName + ".text");
String value = loadTextFile(textFile);
if (value != null)
set(main.class, variableName, value);
else {
File structureFile = new File(programDir(progID), variableName + ".structure");
value = loadTextFile(structureFile);
if (value == null) {
File structureGZFile = new File(programDir(progID), variableName + ".structure.gz");
if (!structureGZFile.isFile()) return;
//value = loadGZTextFile(structureGZFile);
InputStream fis = new FileInputStream(structureGZFile); try {
GZIPInputStream gis = newGZIPInputStream(fis);
InputStreamReader reader = new InputStreamReader(gis, "UTF-8");
BufferedReader bufferedReader = new BufferedReader(reader);
//O o = unstructure_reader(bufferedReader);
Object o = unstructure_tok(javaTokC_noMLS_onReader(bufferedReader), allDynamic, readLocally2_classFinder.get());
readLocally_set(obj, variableName, o);
return;
} finally { _close(fis); }}
readLocally_stringLength = l(value);
if (nempty(value))
readLocally_set(obj, variableName, unstructure(value, allDynamic, readLocally2_classFinder.get()));
}
}
} catch (Exception __e) { throw rethrow(__e); } }
static void readLocally_set(Object c, String varName, Object value) {
Object oldValue = get(c, varName);
if (oldValue instanceof List && !(oldValue instanceof ArrayList) && value != null) {
// Assume it's a synchroList.
value = synchroList((List) value);
}
set(c, varName, value);
}
static Class mc() {
return main.class;
}
static FileInputStream newFileInputStream(File path) throws IOException {
return newFileInputStream(path.getPath());
}
static FileInputStream newFileInputStream(String path) throws IOException {
FileInputStream f = new FileInputStream(path);
_registerIO(f, path, true);
return f;
}
static Rect toRect(Rectangle r) {
return r == null ? null : new Rect(r);
}
static Rect toRect(RectangularShape r) {
return r == null ? null : toRect(r.getBounds());
}
static Rect toRect(Rect r) { return r; }
static Map> allFields_cache = weakHashMap();
static Set allFields(Object o) {
if (o == null) return emptySet();
Class _c = _getClass(o);
Set fields = allFields_cache.get(_c);
if (fields == null)
allFields_cache.put(_c, fields = asTreeSet(keys(getOpt_getFieldMap(o))));
return fields;
}
static boolean neq(Object a, Object b) {
return !eq(a, b);
}
static Object getOpt(Object o, String field) {
return getOpt_cached(o, field);
}
static Object getOpt(String field, Object o) {
return getOpt_cached(o, field);
}
static Object getOpt_raw(Object o, String field) { try {
Field f = getOpt_findField(o.getClass(), field);
if (f == null) return null;
makeAccessible(f);
return f.get(o);
} catch (Exception __e) { throw rethrow(__e); } }
// access of static fields is not yet optimized
static Object getOpt(Class c, String field) { try {
if (c == null) return null;
Field f = getOpt_findStaticField(c, field);
if (f == null) return null;
makeAccessible(f);
return f.get(null);
} catch (Exception __e) { throw rethrow(__e); } }
static Field getOpt_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);
return null;
}
static int stdHash(Object a, String... fields) {
if (a == null) return 0;
int hash = getClassName(a).hashCode();
for (String field : fields)
hash = boostHashCombine(hash, hashCode(getOpt(a, field)));
return hash;
}
static String[] toStringArray(Collection c) {
String[] a = new String[l(c)];
Iterator it = c.iterator();
for (int i = 0; i < l(a); i++)
a[i] = it.next();
return a;
}
static String[] toStringArray(Object o) {
if (o instanceof String[])
return (String[]) o;
else if (o instanceof Collection)
return toStringArray((Collection) o);
else
throw fail("Not a collection or array: " + getClassName(o));
}
static void _handleError(Error e) {
//call(javax(), '_handleError, e);
}
static boolean isUByte(int i) {
return (i & 0xFF) == i;
}
static ThreadLocal assertVerbose_value = new ThreadLocal();
static void assertVerbose(boolean b) {
assertVerbose_value.set(b);
}
static boolean assertVerbose() { return isTrue(assertVerbose_value.get()); }
static A assertEqualsVerbose(Object x, A y) {
assertEqualsVerbose((String) null, x, y);
return y;
}
// x = expected, y = actual
static A assertEqualsVerbose(String msg, Object x, A y) {
if (!eq(x, y)) {
throw fail((nempty(msg) ? msg + ": " : "") + "expected: "+ x + ", got: " + y);
} else
print("OK" + (empty(msg) ? "" : " " + msg) + ": " + /*sfu*/(x));
return y;
}
static String nullIfEmpty(String s) {
return isEmpty(s) ? null : s;
}
static Map nullIfEmpty(Map map) {
return isEmpty(map) ? null : map;
}
static List nullIfEmpty(List l) {
return isEmpty(l) ? null : l;
}
static String getClassName(Object o) {
return o == null ? "null" : o instanceof Class ? ((Class) o).getName() : o.getClass().getName();
}
static void smartSet(Field f, Object o, Object value) throws Exception {
try {
f.set(o, value);
} catch (Exception e) {
Class type = f.getType();
// take care of common case (long to int)
if (type == int.class && value instanceof Long)
{ f.set(o, ((Long) value).intValue()); return; }
if (type == boolean.class && value instanceof String)
{ f.set(o, isTrueOrYes(((String) value))); return; }
if (type == LinkedHashMap.class && value instanceof Map)
{ f.set(o, asLinkedHashMap((Map) value)); return; }
throw e;
}
}
static Object swingCall(final Object o, final String method, final Object... args) {
return swing(new F0