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 java.text.NumberFormat;
import static x30_pkg.x30_util.DynamicObject;
import java.nio.file.Path;
import java.text.*;
import java.nio.charset.Charset;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
class main {
static List uniquifyBinaryImages(Iterable l) {
return new UniquifyUsingHasher(__1 -> hashBinaryImage(__1), (__2, __3) -> binaryImagesIdentical(__2, __3)).addAll(l).get();
}
static long hashBinaryImage(IBinaryImage img) {
return new HashBinaryImage(img).get();
}
static boolean binaryImagesIdentical(IBinaryImage img1, IBinaryImage img2) {
if (img1 == img2) return true;
if (img1 == null) return img2 == null;
if (img2 == null) return false;
int w = img1.getWidth(), h = img1.getHeight();
if (w != img2.getWidth() || h != img2.getHeight()) return false;
return binaryImagesIdentical(img1.toImage2B(), img2.toImage2B());
}
static boolean binaryImagesIdentical(Image2B img1, Image2B img2) {
if (img1 == img2) return true;
if (img1 == null) return img2 == null;
if (img2 == null) return false;
int w = img1.getWidth(), h = img1.getHeight();
if (w != img2.getWidth() || h != img2.getHeight()) return false;
byte[] pixels1 = img1.pixels, pixels2 = img2.pixels;
return Arrays.equals(pixels1, pixels2);
}
// A: object type
// B: hash type (e.g. Long)
static class UniquifyUsingHasher {
final public UniquifyUsingHasher setHasher(IF1 hasher){ return hasher(hasher); }
public UniquifyUsingHasher hasher(IF1 hasher) { this.hasher = hasher; return this; } final public IF1 getHasher(){ return hasher(); }
public IF1 hasher() { return hasher; }
IF1 hasher;
final public UniquifyUsingHasher setEqualsPredicate(IF2 equalsPredicate){ return equalsPredicate(equalsPredicate); }
public UniquifyUsingHasher equalsPredicate(IF2 equalsPredicate) { this.equalsPredicate = equalsPredicate; return this; } final public IF2 getEqualsPredicate(){ return equalsPredicate(); }
public IF2 equalsPredicate() { return equalsPredicate; }
IF2 equalsPredicate = (__1, __2) -> eq(__1, __2);
UniquifyUsingHasher(IF1 hasher) {
this.hasher = hasher;}
UniquifyUsingHasher(IF1 hasher, IF2 equalsPredicate) {
this.equalsPredicate = equalsPredicate;
this.hasher = hasher;}
MultiMap byHash = new MultiMap();
final public List getList(){ return list(); }
public List list() { return list; }
List list = new ArrayList();
boolean add(A a) {
B hash = hasher.get(a);
if (any(byHash.get(hash), x -> equalsPredicate.get(x, a)))
return false;
byHash.put(hash, a);
list.add(a);
return true;
}
UniquifyUsingHasher addAll(Iterable extends A> l) {
for (A a : unnullForIteration(l))
add(a);
return this;
}
List get() { return list; }
int nHashCollisions() { return l(keysWithMultipleValues(byHash)); }
}
static class HashBinaryImage implements IFieldsToList{
IBinaryImage img;
HashBinaryImage() {}
HashBinaryImage(IBinaryImage img) {
this.img = img;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + img + ")"; }public Object[] _fieldsToList() { return new Object[] {img}; }
long hash;
long get() {
if (img == null) return hash;
var img = this.img.toImage2B();
int w = img.getWidth(), h = img.getHeight();
int n = w*h;
byte[] pixels = img.pixels;
addToHash(w);
addToHash(h);
for (int i = 0; i < n; i++)
addToHash((pixels[i/8] & (1 << (i & 7))) != 0 ? 0xFFFFFFFF : 0xFF000000);
return hash;
}
void addToHash(long x) {
hash = boostHashCombine64(hash, x);
}
}
static class Image2B implements IBinaryImage {
Image2B() {}
int w, h;
byte[] pixels;
Image2B(int w, int h, byte[] pixels) {
this.pixels = pixels;
this.h = h;
this.w = w;}
Image2B(Image2B img) {
w = img.getWidth();
h = img.getHeight();
pixels = cloneByteArray(img.pixels);
}
Image2B(RGBImage img) {
w = img.getWidth();
h = img.getHeight();
pixels = new byte[(w*h+7)/8];
for (int y = 0; y < h; y++) for (int x = 0; x < w; x++)
if (img.getPixel(x, y).getBrightness() >= 0.5f) {
int i = y*w+x;
pixels[i/8] |= 1 << (i & 7);
}
}
Image2B(BWImage img) {
this(img, 128);
}
// >= threshold
Image2B(BWImage img, int threshold) {
w = img.w(); h = img.h();
int n = w*h;
int nOut = (n+7)/8;
byte[] pixels = this.pixels = new byte[nOut];
byte[] bwPixels = img.pixels;
int iIn = 0;
// do the bulk
for (int iOut = 0; iOut < nOut-1; iOut++) {
int value = 0;
for (int bit = 0; bit < 8; bit++) {
value >>= 1;
if (ubyteToInt(bwPixels[iIn++]) >= threshold)
value |= 0x80;
}
pixels[iOut] = (byte) value;
}
// do last (up to 7) bits
for (; iIn < n; iIn++)
if (ubyteToInt(bwPixels[iIn]) >= threshold)
pixels[nOut-1] |= 1 << (iIn & 7);
}
Image2B(BufferedImage img) {
this(img, 128);
}
Image2B(BufferedImage img, int threshold) {
this(new BWImage(img), threshold);
}
// initializes with black
Image2B(int w, int h) {
this.h = h;
this.w = w;
pixels = new byte[(w*h+7)/8];
}
RGBImage toRGB() {
RGBImage img = new RGBImage(w, h, Color.black);
for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) {
int i = y*w+x;
if ((pixels[i/8] & (1 << (i & 7))) != 0)
img.setPixel(x, y, Color.white);
}
return img;
}
BWImage toBW() {
BWImage img = new BWImage(w, h, 0f);
for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) {
int i = y*w+x;
if ((pixels[i/8] & (1 << (i & 7))) != 0)
img.setPixel(x, y, 1f);
}
return img;
}
public BufferedImage getBufferedImage() { return toBW().getBufferedImage(); }
// x and y must be inside the image
final public boolean getBoolPixel(int x, int y){ return getPixel(x, y); }
public boolean getPixel(int x, int y) {
int i = y*w+x;
return (pixels[i/8] & (1 << (i & 7))) != 0;
}
// defaultColor is color outside of image
final public boolean getBoolPixel(int x, int y, boolean defaultColor){ return getPixel(x, y, defaultColor); }
public boolean getPixel(int x, int y, boolean defaultColor) {
if (x < 0 || y < 0 || x >= w || y >= h) return defaultColor;
return getPixel(x, y);
}
public void setPixel(int x, int y, boolean b) {
int i = y*w+x;
byte val = pixels[i/8], shifted = (byte) (1 << (i & 7));
val = (byte) (b ? val | shifted : val & ~shifted);
pixels[i/8] = val;
}
void setPixel(int x, int y) {
int i = y*w+x;
pixels[i/8] |= 1 << (i & 7);
}
public int getWidth() { return w; }
public int getHeight() { return h; }
public String toString() {
return "Image2B " + str_px(w, h);
}
}
static interface IBinaryImage extends MakesBufferedImage {
boolean getBoolPixel(int x, int y);
default void setPixel(int x, int y, boolean b) { throw unimplemented(); }
public default BufferedImage getBufferedImage() {
return toImage2B().getBufferedImage();
}
default Image2B toImage2B() {
return this instanceof Image2B ? (Image2B) this : iBinaryImageToImage2B(this);
}
}
// records its full size (total value count) in a field now
static class MultiMap implements IMultiMap {
Map> data = new HashMap>();
int fullSize;
MultiMap() {}
MultiMap(boolean useTreeMap) { if (useTreeMap) data = new TreeMap(); }
MultiMap(MultiMap map) { putAll(map); }
MultiMap(Map> data) {
this.data = data;}
void put(A key, B value) { synchronized(data) {
List list = data.get(key);
if (list == null)
data.put(key, list = _makeEmptyList());
list.add(value);
++fullSize;
}}
void add(A key, B value) { put(key, value); }
void addAll(A key, Collection values) { putAll(key, values); }
void addAllIfNotThere(A key, Collection values) { synchronized(data) {
for (B value : values)
setPut(key, value);
}}
void setPut(A key, B value) { synchronized(data) {
if (!containsPair(key, value))
put(key, value);
}}
boolean containsPair(A key, B value) { synchronized(data) {
return get(key).contains(value);
}}
void putAll(Collection keys, B value) { synchronized(data) {
for (A key : unnullForIteration(keys))
put(key, value);
}}
void putAll(A key, Collection values) { synchronized(data) {
if (nempty(values)) getActual(key).addAll(values);
}}
void putAll(Iterable> pairs) { synchronized(data) {
for (Pair p : unnullForIteration(pairs))
put(p.a, p.b);
}}
void removeAll(A key, Collection values) { synchronized(data) {
for (B value : values)
remove(key, value);
}}
public List get(A key) { synchronized(data) {
List list = data.get(key);
return list == null ? Collections. emptyList() : list;
}}
List getOpt(A key) { synchronized(data) {
return data.get(key);
}}
List getAndClear(A key) { synchronized(data) {
List l = cloneList(data.get(key));
remove(key);
return l;
}}
// returns actual mutable live list
// creates the list if not there
List getActual(A key) { synchronized(data) {
List list = data.get(key);
if (list == null)
data.put(key, list = _makeEmptyList());
return list;
}}
void clean(A key) { synchronized(data) {
List list = data.get(key);
if (list != null && list.isEmpty()) {
fullSize -= l(list);
data.remove(key);
}
}}
final public Set keys(){ return keySet(); }
public Set keySet() { synchronized(data) {
return data.keySet();
}}
void remove(A key) { synchronized(data) {
fullSize -= l(this.getOpt(key));
data.remove(key);
}}
final void remove(Pair p){ removePair(p); }
void removePair(Pair p) {
if (p != null) remove(p.a, p.b);
}
void remove(A key, B value) { synchronized(data) {
List list = data.get(key);
if (list != null) {
if (list.remove(value))
fullSize--;
if (list.isEmpty())
data.remove(key);
}
}}
void clear() { synchronized(data) {
data.clear();
}}
boolean containsKey(A key) { synchronized(data) {
return data.containsKey(key);
}}
B getFirst(A key) { synchronized(data) {
List list = get(key);
return list.isEmpty() ? null : list.get(0);
}}
void addAll(MultiMap map) { putAll(map); }
void putAll(MultiMap map) { synchronized(data) {
for (A key : map.keySet())
putAll(key, map.get(key));
}}
void putAll(Map map) { synchronized(data) {
if (map != null) for (Map.Entry e : map.entrySet())
put(e.getKey(), e.getValue());
}}
final public int keyCount(){ return keysSize(); }
public int keysSize() { synchronized(data) { return l(data); }}
final public int fullSize(){ return size(); }
public int size() { synchronized(data) {
return fullSize;
}}
// expensive operation
List reverseGet(B b) { synchronized(data) {
List l = new ArrayList();
for (A key : data.keySet())
if (data.get(key).contains(b))
l.add(key);
return l;
}}
Map> asMap() { synchronized(data) {
return cloneMap(data);
}}
boolean isEmpty() { synchronized(data) { return data.isEmpty(); }}
// override in subclasses
List _makeEmptyList() {
return new ArrayList();
}
// returns live lists
Collection> allLists() {
synchronized(data) {
return new ArrayList(data.values());
}
}
Collection> values() { return allLists(); }
List allValues() {
return concatLists(data.values());
}
Object mutex() { return data; }
public String toString() { return "mm" + str(data); }
}
static interface IFieldsToList {
Object[] _fieldsToList();
}
static interface IF2 {
C get(A a, B b);
}
static interface IF1 {
B get(A a);
}
static class RGBImage implements MakesBufferedImage, IRGBImage {
transient BufferedImage bufferedImage;
int width, height;
int[] pixels;
RGBImage() {}
RGBImage(BufferedImage image) {
bufferedImage = image;
width = image.getWidth();
height = image.getHeight();
pixels = new int[width*height];
var gp = grabbableIntPixels_fastOrSlow(image);
if (gp.scanlineStride == width && gp.offset == 0)
pixels = gp.data;
else {
pixels = new int[width*height];
int iIn = 0, iOut = 0;
for (int y = 0; y < height; y++) {
arrayCopy(gp.data, iIn, pixels, iOut, width);
iIn += gp.scanlineStride;
iOut += width;
}
}
cleanPixels(); // set upper byte to 0
}
RGBImage(Dimension size, Color color) {
this(size.width, size.height, color);
}
RGBImage(Dimension size, RGB color) {
this(size.width, size.height, color);
}
private void cleanPixels() {
var pixels = this.pixels;
for (int i = 0; i < pixels.length; i++)
pixels[i] &= 0xFFFFFF;
}
RGBImage(int width, int height, int[] pixels) {
this.width = width;
this.height = height;
this.pixels = pixels;
}
RGBImage(int w, int h, RGB[] pixels) {
this.width = w;
this.height = h;
this.pixels = asInts(pixels);
}
public static int[] asInts(RGB[] pixels) {
int[] ints = new int[pixels.length];
for (int i = 0; i < pixels.length; i++)
ints[i] = pixels[i] == null ? 0 : pixels[i].getColor().getRGB();
return ints;
}
public RGBImage(int w, int h) {
this(w, h, Color.black);
}
RGBImage(int w, int h, RGB rgb) {
this.width = w;
this.height = h;
this.pixels = new int[w*h];
int col = rgb.asInt();
if (col != 0)
for (int i = 0; i < pixels.length; i++)
pixels[i] = col;
}
RGBImage(RGBImage image) {
this(image.width, image.height, copyPixels(image.pixels));
}
RGBImage(int width, int height, Color color) {
this(width, height, new RGB(color));
}
RGBImage(MakesBufferedImage img) {
this(toBufferedImage(img));
}
private static int[] copyPixels(int[] pixels) {
int[] copy = new int[pixels.length];
System.arraycopy(pixels, 0, copy, 0, pixels.length);
return copy;
}
public int getIntPixel(int x, int y) {
if (inRange(x, y))
return pixels[y * width + x];
else
return 0xFFFFFF;
}
public static RGB asRGB(int packed) {
int r = (packed >> 16) & 0xFF;
int g = (packed >> 8) & 0xFF;
int b = packed & 0xFF;
return new RGB(r / 255f, g / 255f, b / 255f);
}
public RGB getRGB(int x, int y) {
if (inRange(x, y))
return asRGB(pixels[y * width + x]);
else
return new RGB(0xFFFFFF);
}
/** alias of getRGB - I kept typing getPixel instead of getRGB all the time, so I finally created it */
RGB getPixel(int x, int y) {
return getRGB(x, y);
}
RGB getPixel(Pt p) { return getPixel(p.x, p.y); }
public int getWidth() { return width; }
public int getHeight() { return height; }
public int w() { return width; }
public int h() { return height; }
/** Attention: cached, i.e. does not change when image itself changes */
/** @NotNull */
public BufferedImage getBufferedImage() {
if (bufferedImage == null) {
bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
//bufferedImage.setData(Raster.createRaster(new SampleModel()));
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
bufferedImage.setRGB(x, y, pixels[y*width+x]);
}
return bufferedImage;
}
RGBImage clip(Rect r) {
return r == null ? null : clip(r.getRectangle());
}
RGBImage clip(Rectangle r) {
r = fixClipRect(r);
if (r.x == 0 && r.y == 0 && r.width == width && r.height == height) return this;
int[] newPixels;
try {
newPixels = new int[r.width*r.height];
} catch (RuntimeException e) {
System.out.println(r);
throw e;
}
for (int y = 0; y < r.height; y++) {
System.arraycopy(pixels, (y+r.y)*width+r.x, newPixels, y*r.width, r.width);
}
return new RGBImage(r.width, r.height, newPixels);
}
private Rectangle fixClipRect(Rectangle r) {
r = r.intersection(new Rectangle(0, 0, width, height));
if (r.isEmpty())
r = new Rectangle(r.x, r.y, 0, 0);
return r;
}
public int getInt(int x, int y) {
return pixels[y * width + x];
}
public void save(File file) {
saveImage(file, getBufferedImage());
}
public static RGBImage dummyImage() {
return new RGBImage(1, 1, new int[] {0xFFFFFF});
}
public int[] getPixels() {
return pixels;
}
void setPixel(int x, int y, int r, int g, int b) {
if (x >= 0 && y >= 0 && x < width && y < height)
pixels[y*width+x] = (limitToUByte(r) << 16) | (limitToUByte(g) << 8) | limitToUByte(b);
}
public void setPixel(int x, int y, RGB rgb) {
if (x >= 0 && y >= 0 && x < width && y < height)
pixels[y*width+x] = rgb.asInt();
}
final public void set(int x, int y, Color color){ setPixel(x, y, color); }
public void setPixel(int x, int y, Color color) {
setPixel(x, y, new RGB(color));
}
void setInt(int x, int y, int rgb) {
setPixel(x, y, rgb);
}
public void setPixel(int x, int y, int rgb) {
if (x >= 0 && y >= 0 && x < width && y < height)
pixels[y*width+x] = rgb;
}
void setPixel(Pt p, RGB rgb) { setPixel(p.x, p.y, rgb); }
void setPixel(Pt p, Color color) { setPixel(p.x, p.y, color); }
public RGBImage copy() {
return new RGBImage(this);
}
public boolean inRange(int x, int y) {
return x >= 0 && y >= 0 && x < width && y < height;
}
public Dimension getSize() {
return new Dimension(width, height);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RGBImage rgbImage = (RGBImage) o;
if (height != rgbImage.height) return false;
if (width != rgbImage.width) return false;
if (!Arrays.equals(pixels, rgbImage.pixels)) return false;
return true;
}
@Override
public int hashCode() {
int result = width;
result = 31 * result + height;
result = 31 * result + Arrays.hashCode(pixels);
return result;
}
public String getHex(int x, int y) {
return getPixel(x, y).getHexString();
}
public RGBImage clip(int x, int y, int width, int height) {
return clip(new Rectangle(x, y, width, height));
}
public RGBImage clipLine(int y) {
return clip(0, y, width, 1);
}
public int numPixels() {
return width*height;
}
RGBImage uncacheBufferedImage() {
bufferedImage = null;
return this;
}
}
static interface MakesBufferedImage extends WidthAndHeight {
BufferedImage getBufferedImage();
public default void drawAt(Graphics2D g, int x, int y) {
g.drawImage(getBufferedImage(), x, y, null);
}
}
static final class BWImage extends Meta implements MakesBufferedImage, IBWImage {
int width, height;
byte[] pixels;
// color returned when getPixel is called with a position outside the actual image
float borderColor = 0.0f;
// for unstructure()
BWImage() {}
// BLACK!
BWImage(int width, int height) {
this.height = height;
this.width = width;
pixels = new byte[width*height];
}
BWImage(int width, int height, float brightness) {
this.height = height;
this.width = width;
pixels = new byte[width*height];
fillArrayUnlessZero(pixels, _toByte(brightness));
}
BWImage(int width, int height, float[] pixels) {
this.pixels = new byte[pixels.length];
this.height = height;
this.width = width;
for (int i = 0; i < pixels.length; i++)
this.pixels[i] = _toByte(pixels[i]);
}
public BWImage(int width, int height, byte[] pixels) {
this.height = height;
this.width = width;
this.pixels = pixels;
}
public BWImage(BWImage image) {
width = image.getWidth();
height = image.getHeight();
byte[] pixels = this.pixels = new byte[width*height];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
pixels[y*width+x] = image.getByte(x, y);
}
// TODO: optimize!
BWImage(RGBImage image) {
width = image.getWidth();
height = image.getHeight();
byte[] pixels = this.pixels = new byte[height*width];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++) {
RGB rgb = image.getRGB(x, y);
pixels[y*width+x] = BWImage._toByte(rgb.getBrightness());
}
}
/*public BWImage(BufferedImage image) {
this(new RGBImage(image));
}*/
BWImage(BufferedImage image) { try {
width = image.getWidth();
height = image.getHeight();
int[] pixels = new int[width*height];
byte[] bytePixels = this.pixels = new byte[width*height];
PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width);
if (!pixelGrabber.grabPixels())
throw fail("Could not grab pixels");
int n = width*height;
for (int i = 0; i < n; i++) {
//bytePixels[i] = pixelToByte(pixels[i]);
int packed = pixels[i];
/*float r = ((packed >> 16) & 0xFF)/255f;
float g = ((packed >> 8) & 0xFF)/255f;
float b = (packed & 0xFF)/255f;
bytePixels[i] = (byte) iround((r+g+b)/3.0f*255f);*/
int r = ((packed >> 16) & 0xFF);
int g = ((packed >> 8) & 0xFF);
int b = (packed & 0xFF);
bytePixels[i] = (byte) ((r+g+b+1)/3);
}
} catch (Exception __e) { throw rethrow(__e); } }
// TODO: does it exactly match the other method? (asRGB+getBrightness+_toByte)
static byte pixelToByte(int packed) {
/*int r = (packed >> 16) & 0xFF;
int g = (packed >> 8) & 0xFF;
int b = packed & 0xFF;
ret (byte) ((r+g+b)/3.0f);*/
float r = ((packed >> 16) & 0xFF)/255f;
float g = ((packed >> 8) & 0xFF)/255f;
float b = (packed & 0xFF)/255f;
return (byte) ((r+g+b)/3.0f*255f);
}
public byte getByte(int x, int y) {
return inRange(x, y) ? getByte_noRangeCheck(x, y) : _toByte(borderColor);
}
// pretty bad function name
// gets brightness (0 to 255) at pixel
public int getInt(int x, int y) {
return ubyteToInt(getByte(x, y));
}
// idx = index in pixel array
public int getInt_noRangeCheck(int idx) {
return ubyteToInt(pixels[idx]);
}
// gets brightness (0 to 255) at pixel with default if out of image
public int getInt(int x, int y, int defaultValue) {
return inRange(x, y) ? getInt(x, y) : defaultValue;
}
public double averageBrightness() {
double sum = 0;
int n = width*height;
for (int i = 0; i < n; i++)
sum += getInt_noRangeCheck(i);
return sum/n;
}
public float minimumBrightness() {
float min = 1;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
min = Math.min(min, getPixel(x, y));
return min;
}
public float maximumBrightness() {
float max = 0;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
max = Math.max(max, getPixel(x, y));
return max;
}
float getPixel(int x, int y) {
return inRange(x, y) ? _toFloat(getByte(x,y )) : borderColor;
}
public float getFloatPixel(int x, int y) { return getPixel(x, y); }
float getPixel(Pt p) { return getPixel(p.x, p.y); }
static byte _toByte(float pixel) {
return (byte) (pixel*255f);
}
static float _toFloat(byte pixel) {
return (((int) pixel) & 255)/255f;
}
private boolean inRange(int x, int y) {
return x >= 0 && x < width && y >= 0 && y < height;
}
public int getWidth() { return width; }
public int getHeight() { return height; }
public RGBImage toRGB() {
int[] rgbs = new int[width*height];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++) {
int b = getByte(x, y) & 0xFF;
rgbs[y*width+x] = 0xFF000000 | b*0x010101;
}
return new RGBImage(width, height, rgbs);
}
public RGBImage toRGB_slow() {
RGB[] rgbs = new RGB[width*height];
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++) {
float p = getPixel(x, y);
rgbs[y*width+x] = new RGB(p, p, p);
}
return new RGBImage(width, height, rgbs);
}
public BWImage clip(int x, int y, int w, int h) {
return clip(new Rectangle(x, y, w, h));
}
private Rectangle fixClipRect(Rectangle r) {
return r.intersection(new Rectangle(0, 0, width, height));
}
BWImage clip(Rect r) {
return clip(r.getRectangle());
}
/** this should be multithread-safe */
public BWImage clip(Rectangle r) {
r = fixClipRect(r);
byte[] newPixels = new byte[r.height*r.width];
for (int y = 0; y < r.height; y++)
for (int x = 0; x < r.width; x++)
newPixels[y*r.width+x] = getByte(r.x+x, r.y+y);
return new BWImage(r.width, r.height, newPixels);
}
public void setPixel(int x, int y, float brightness) {
setByte(x, y, _toByte(fixPixel(brightness)));
}
// i = 0 to 255
public void setInt(int x, int y, int i) {
setByte(x, y, (byte) limitToUByte(i));
}
public void setInt(Pt p, int i) {
setInt(p.x, p.y, i);
}
public void setByte(int x, int y, byte b) {
if (x >= 0 && x < width && y >= 0 && y < height)
pixels[y*width+x] = b;
}
byte getByte_noRangeCheck(int x, int y) {
return pixels[y*width+x];
}
public void setByte(int x, int y, int brightness) {
setByte(x, y, (byte) brightness);
}
private float fixPixel(float pixel) {
return Math.max(0, Math.min(1, pixel));
}
public float getBorderColor() {
return borderColor;
}
public void setBorderColor(float borderColor) {
this.borderColor = borderColor;
}
public boolean anyPixelBrighterThan(double threshold) {
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
if (getPixel(x, y) > threshold)
return true;
return false;
}
int[] getRGBPixels() {
int n = width*height;
int[] out = new int[n];
for (int i = 0; i < n; i++) {
var b = ubyteToInt(pixels[i]);
b |= (b << 8) | (b << 16);
out[i] = b | 0xFF000000;
}
return out;
}
public BufferedImage getBufferedImage() {
return bufferedImage(getRGBPixels(), width, height);
}
byte[] getBytes() {
return pixels;
}
}
static interface IRGBImage extends MakesBufferedImage {
int getIntPixel(int x, int y);
}
// Meta - a "minimal" approach to adding meta-level to Java objects
static class Meta implements IMeta {
// Meta - a "minimal" approach to adding meta-level to Java objects
// (implementing the interface IMeta)
// We allocate one extra field for each Java object to make it
// reasoning-compatible (reasoning-compatible = extensible with
// fields of any name at runtime).
//
// We couldn't go for 0 extra fields (meta values must be linked
// directly from the object) and there are no half fields in
// Java... so there you go.
//
// Also, if you don't use any meta data, you are probably not
// reasoning about anything. The point of reasoning in JavaX is
// to attach information to objects directly used in the program.
// Possible information contained in the meta field:
// Origin, destination, security level, sender, cost center,
// purpose, list of reifications, ...
// So here it is. THE FIELD YOU HAVE BEEN WAITING FOR!
// [We also have IMeta to retrofit foreign classes (rare but
// probably useful).]
//////////////////////
// The "meta" field //
//////////////////////
// Generic meta value of any kind, but the typical case is it's a
// Map with extra field values for the object etc.
// "meta" is volatile to avoid synchronization; but you can also synchronize on
// _tempMetaMutex() which is usually the object itself. Collections
// and maps are exempt from using the collections's monitor as the meta
// mutex because their monitor tends to be held for long operations
// (e.g. cloneList). For those we use a substantially more complex
// algorithm using a weakMap. Probably overkill. I may reconsider.
volatile Object meta;
// The meta field is not transient, thus by default it will be
// persisted like anything else unless you customize your object
// to suppress or modulate this.
// ...and the interface methods
public void _setMeta(Object meta) { this.meta = meta; }
public Object _getMeta() { return meta; }
// MOST functions are implemented in IMeta (default implementations)
// Scaffolding convenience functions
final boolean scaffolding(){ return scaffoldingEnabled(); }
boolean scaffoldingEnabled() { return main.scaffoldingEnabled(this); }
boolean scaffoldingEnabled(Object o) { return main.scaffoldingEnabled(o); }
}
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 interface IBWImage extends MakesBufferedImage, IRGBImage {
float getFloatPixel(int x, int y); // usually between 0 and 1
default int getInt(int x, int y) { return iround(getFloatPixel(x, y)*255f); }
// implementing IRGBImage
default int getIntPixel(int x, int y) { return rgbIntFromGrayscale(getInt(x, y)); }
default float getFloatPixel(Pt p) { return getFloatPixel(p.x, p.y); }
public default BufferedImage getBufferedImage() {
return grayImageFromIBWImage(this);
}
default BWImage toBWImage() {
return this instanceof BWImage ? (BWImage) this : iBWImageToBWImage(this);
}
}
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; }
}
interface IMultiMap {
public Set keySet();
public Collection get(A a);
public int size();
public int keyCount();
}
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; }
}
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 class Pair implements Comparable> {
final public Pair setA(A a){ return a(a); }
public Pair a(A a) { this.a = a; return this; } final public A getA(){ return a(); }
public A a() { return a; }
A a;
final public Pair setB(B b){ return b(b); }
public Pair b(B b) { this.b = b; return this; } final public B getB(){ return b(); }
public B b() { return b; }
B b;
Pair() {}
Pair(A a, B b) {
this.b = b;
this.a = a;}
public int hashCode() {
return hashCodeFor(a) + 2*hashCodeFor(b);
}
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Pair)) return false;
Pair t = (Pair) o;
return eq(a, t.a) && eq(b, t.b);
}
public String toString() {
return "<" + a + ", " + b + ">";
}
public int compareTo(Pair p) {
if (p == null) return 1;
int i = ((Comparable) a).compareTo(p.a);
if (i != 0) return i;
return ((Comparable) b).compareTo(p.b);
}
}
static interface IMeta {
// see class "Meta" for the bla bla
public void _setMeta(Object meta);
public Object _getMeta();
default public IAutoCloseableF0 _tempMetaMutex() {
return new IAutoCloseableF0() {
public Object get() { return IMeta.this; }
public void close() {}
};
}
// actually query another object
default public Object getMeta(Object obj, Object key){ return metaGet(obj, key); }
default public Object metaGet(Object obj, Object key) {
// call global function
return metaMapGet(obj, key);
}
default public Object metaGet(String key, Object obj) {
// call global function
return metaMapGet(obj, key);
}
default public Object getMeta(Object key){ return metaGet(key); }
default public Object metaGet(Object key) {
if (key == null) return null;
Object meta = _getMeta();
if (meta instanceof Map) return ((Map) meta).get(key);
return null;
}
default public void metaSet(IMeta obj, Object key, Object value){ metaPut(obj, key, value); }
default public void metaPut(IMeta obj, Object key, Object value) {
// call global function
metaMapPut(obj, key, value);
}
default public void metaSet(Object key, Object value){ metaPut(key, value); }
default public void metaPut(Object key, Object value) {
if (key == null) return;
Map map = convertObjectMetaToMap(this);
syncMapPutOrRemove(map, key, value);
}
}
interface IDoublePt {
public double x_double();
public double y_double();
}
static interface IAutoCloseableF0 extends IF0, AutoCloseable {}
public static interface IF0 {
A get();
}
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 getList(Map map, Object key) {
return map == null ? null : (List) map.get(key);
}
static List getList(List l, int idx) {
return (List) get(l, idx);
}
static List getList(Object o, Object key) {
if (o instanceof Map) return getList((Map) o, key);
if (key instanceof String)
return (List) getOpt(o, (String) key);
throw fail("Not a string key: " + getClassName(key));
}
static List getList(String name, Object o) {
return getList(o, name);
}
static List list(Class type) { return list(type, db_mainConcepts()); }
static List list(Class type, Concepts cc) {
return cc.list(type);
}
static List list(Concepts concepts, Class type) {
return concepts.list(type);
}
static List list(String type) {
return db_mainConcepts().list(type);
}
static List list(Concepts concepts, String type) {
return concepts.list(type);
}
static List list(Concepts concepts) {
return asList(concepts.allConcepts());
}
static boolean any(Object pred, Iterable l) {
if (l != null) for (A a : l)
if (isTrue(callF(pred, a)))
return true;
return false;
}
static boolean any(IF1 pred, Iterable l) {
if (l != null) for (A a : l)
if (pred.get(a))
return true;
return false;
}
static boolean any(Iterable l, IF1 pred) {
return any(pred, l);
}
static boolean any(A[] l, IF1 pred) {
if (l != null) for (A a : l)
if (pred.get(a))
return true;
return false;
}
static boolean any(Iterable l) {
if (l != null) for (Boolean a : l)
if (isTrue(a))
return true;
return false;
}
static void addAll(Collection c, Iterable b) {
if (c != null && b != null) for (A a : b) c.add(a);
}
static boolean addAll(Collection c, Collection b) {
return c != null && b != null && c.addAll(b);
}
static boolean addAll(Collection c, B... b) {
return c != null && b != null && c.addAll(Arrays.asList(b));
}
static Map addAll(Map a, Map extends A,? extends B> b) {
if (a != null && b != null) a.putAll(b);
return a;
}
static A addAll(A c, Collection extends Component> components) {
return addComponents(c, components);
}
static A addAll(A c, Component... components) {
return addComponents(c, components);
}
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 void add(BitSet bs, int i) {
bs.set(i);
}
static boolean add(Collection c, A a) {
return c != null && c.add(a);
}
static void add(Container c, Component x) {
addToContainer(c, x);
}
static long add(AtomicLong l, long b) {
return l.addAndGet(b);
}
// 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 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(MultiSet ms) { return ms == null ? 0 : ms.size(); }
static int l(IMultiMap mm) { return mm == null ? 0 : mm.size(); }
static int l(IntSize o) { return o == null ? 0 : o.size(); }
static List keysWithMultipleValues(IMultiMap mm) {
List l = new ArrayList();
for (A key : mm.keySet())
if (l(mm.get(key)) > 1)
l.add(key);
return l;
}
static String shortClassName_dropNumberPrefix(Object o) {
return dropNumberPrefix(shortClassName(o));
}
static long boostHashCombine64(long a, long b) {
return a ^ (b + 0x9e3779b97f4a7c15L + (a << 6) + (a >>> 2));
}
static byte[] cloneByteArray(byte[] a) {
if (a == null) return null;
byte[] b = new byte[a.length];
System.arraycopy(a, 0, b, 0, a.length);
return b;
}
static int ubyteToInt(byte b) {
return b & 0x0FF;
}
static int ubyteToInt(char c) {
return c & 0x0FF;
}
static BWImage toBW(RGBImage img) {
return img == null ? null : new BWImage(img);
}
static int getPixel(BufferedImage img, int x, int y) {
return img.getRGB(x, y);
}
static int getPixel(BufferedImage img, Pt p) {
return img.getRGB(p.x, p.y);
}
static String str_px(int w, int h) {
return n2(w) + "*" + n2(h) + "px";
}
static String str_px(WidthAndHeight image) {
return image == null ? null : str_px(image.getWidth(), image.getHeight());
}
static RuntimeException unimplemented() {
throw fail("TODO");
}
static RuntimeException unimplemented(String msg) {
throw fail("TODO: " + msg);
}
static RuntimeException unimplemented(Object obj) {
throw fail("TODO: implement method in " + className(obj));
}
static Image2B toImage2B(BufferedImage img) {
return img == null ? null : new Image2B(img);
}
static Image2B toImage2B(MakesBufferedImage img) {
if (img == null) return null;
if (img instanceof Image2B) return ((Image2B) img);
if (img instanceof BWImage) return new Image2B((BWImage) img);
if (img instanceof IBinaryImage) return iBinaryImageToImage2B((IBinaryImage) img);
return new Image2B(toBufferedImage(img));
}
static Image2B iBinaryImageToImage2B(IBinaryImage img) {
if (img == null) return null;
if (img instanceof Image2B) return ((Image2B) img);
int w = img.getWidth(), h = img.getHeight();
byte[] pixels = new byte[(w*h+7)/8];
int i = 0;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
if (img.getBoolPixel(x, y))
pixels[i/8] |= 1 << (i & 7);
i++;
}
return new Image2B(w, h, pixels);
}
static Map putAll(Map a, Map extends A,? extends B> b) {
if (a != null && b != null) a.putAll(b);
return a;
}
static MultiMap putAll(MultiMap a, Map extends A,? extends B> b) {
if (a != null) a.putAll((Map) b);
return a;
}
static Map putAll(Map a, Object... b) {
if (a != null)
litmap_impl(a, b);
return a;
}
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 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(Rect r) { return r != null && r.w != 0 && r.h != 0; }
static boolean nempty(MultiSet ms) { return ms != null && !ms.isEmpty(); }
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 ArrayList emptyList() {
return new ArrayList();
//ret Collections.emptyList();
}
static ArrayList emptyList(int capacity) {
return new ArrayList(max(0, capacity));
}
// Try to match capacity
static ArrayList emptyList(Iterable l) {
return l instanceof Collection ? emptyList(((Collection) l).size()) : emptyList();
}
static ArrayList emptyList(Object[] l) {
return emptyList(l(l));
}
// get correct type at once
static ArrayList emptyList(Class c) {
return new ArrayList();
}
static 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 A getAndClear(IVar v) {
A a = v.get();
v.set(null);
return a;
}
static ArrayList cloneList(Iterable l) {
return l instanceof Collection ? cloneList((Collection) l) : asList(l);
}
static ArrayList cloneList(Collection l) {
if (l == null) return new ArrayList();
synchronized(collectionMutex(l)) {
return new ArrayList(l);
}
}
static Set keys(Map map) {
return map == null ? new HashSet() : map.keySet();
}
// convenience shortcut for keys_gen
static Set keys(Object map) {
return keys((Map) map);
}
static Set keys(MultiSet ms) {
return ms.keySet();
}
static Set keys(IMultiMap mm) {
return mm.keySet();
}
static Set keySet(Map map) {
return map == null ? new HashSet() : map.keySet();
}
static Set keySet(Object map) {
return keys((Map) map);
}
static Set keySet(MultiSet ms) {
return ms.keySet();
}
static Set keySet(MultiMap mm) {
return mm.keySet();
}
static int keysSize(MultiMap mm) {
return lKeys(mm);
}
static A reverseGet(List l, int idx) {
if (l == null || idx < 0) return null;
int n = l(l);
return idx < n ? l.get(n-1-idx) : null;
}
static Map cloneMap(Map map) {
if (map == null) return new HashMap();
// assume mutex is equal to map
synchronized(map) {
return map instanceof TreeMap ? new TreeMap((TreeMap) map) // copies comparator
: map instanceof LinkedHashMap ? new LinkedHashMap(map)
: new HashMap(map);
}
}
static List cloneMap(Iterable l, IF1 f) {
List x = emptyList(l);
if (l != null) for (A o : cloneList(l))
x.add(f.get(o));
return x;
}
static Collection values(Map map) {
return map == null ? emptyList() : map.values();
}
// convenience shortcut for values_gen
static Collection values(Object map) {
return values((Map) map);
}
static Collection values(MultiMap mm) {
return mm == null ? emptyList() : concatLists(values(mm.data));
}
static > List allValues(Map map) {
List out = new ArrayList();
for (var l : values(map))
addAll(out, l);
return out;
}
static List concatLists(Iterable... lists) {
List l = new ArrayList();
if (lists != null) for (Iterable list : lists)
addAll(l, list);
return l;
}
static List concatLists(Collection extends Iterable> lists) {
List l = new ArrayList();
if (lists != null) for (Iterable list : lists)
addAll(l, list);
return l;
}
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 GrabbableIntPixels grabbableIntPixels_fastOrSlow(BufferedImage image) { try {
try {
GrabbableIntPixels gp = grabbableIntPixels(image);
if (gp != null) return gp;
} catch (Throwable __e) { printStackTrace(__e); }
// Use pixelGrabber if quick method fails
int w = image.getWidth(), h = image.getHeight();
int[] data = new int[w*h];
PixelGrabber pixelGrabber = new PixelGrabber(image, 0, 0, w, h, data, 0, w);
if (!pixelGrabber.grabPixels())
throw fail("Could not grab pixels");
return new GrabbableIntPixels(data, w, h, 0, w);
} catch (Exception __e) { throw rethrow(__e); } }
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 BufferedImage toBufferedImage(Object o) {
return toBufferedImageOpt(o);
}
static boolean inRange(int x, int n) {
return x >= 0 && x < n;
}
static boolean inRange(int x, List l) {
return inRange(x, l(l));
}
static boolean inRange(int x, int a, int b) {
return x >= a && x < b;
}
static void saveImage(File f, BufferedImage img) {
if (hasJPEGExtension(f))
saveJPG(f, img);
else
savePNG(f, img);
}
static void saveImage(BufferedImage img, File f) {
saveImage(f, img);
}
static int limitToUByte(int i) {
return max(0, min(255, i));
}
static void setPixel(BufferedImage img, int p_x, int p_y, Color color) { setPixel(img, pt(p_x, p_y), color); }
static void setPixel(BufferedImage img, Pt p, Color color) {
{ if (img != null) img.setRGB(p.x, p.y, colorToIntOpaque(color)); }
}
static void setPixel(Graphics2D g, int p_x, int p_y, Color color) { setPixel(g, pt(p_x, p_y), color); }
static void setPixel(Graphics2D g, Pt p, Color color) {
g.setColor(color);
g.drawRect(p.x, p.y, 1, 1);
}
static Class> getClass(String name) {
return _getClass(name);
}
static Class getClass(Object o) {
return _getClass(o);
}
static Class getClass(Object realm, String name) {
return _getClass(realm, name);
}
static void fillArrayUnlessZero(byte[] a, byte value) {
if (value != 0) Arrays.fill(a, value);
}
static void fillArrayUnlessZero(int[] a, int value) {
if (value != 0) Arrays.fill(a, value);
}
static void fillArrayUnlessZero(float[] a, float value) {
if (value != 0) Arrays.fill(a, value);
}
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 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 int getInt(List c, int i) {
return toInt(get(c, i));
}
static int getInt(Map map, Object key) {
return toInt(mapGet(map, key));
}
static int getInt(Object o, String field) {
return toInt(getOpt(o, (String) field));
}
static int getInt(String field, Object o) {
return getInt(o, field);
}
static BufferedImage bufferedImage(int[] pixels, int w, int h) {
return intArrayToBufferedImage(pixels, w, h);
}
static BufferedImage bufferedImage(int[] pixels, WidthAndHeight size) {
return bufferedImage(pixels, size.getWidth(), size.getHeight());
}
static BufferedImage bufferedImage(int w, int h, int[] pixels) {
return intArrayToBufferedImage(pixels, w, h);
}
// undefined color, seems to be all black in practice
static BufferedImage bufferedImage(int w, int h) {
return newBufferedImage(w, h);
}
static BufferedImage bufferedImage(int w, int h, RGB rgb) {
return newBufferedImage(w, h, rgb);
}
static BufferedImage bufferedImage(int w, Color color) { return bufferedImage(w, w, color); }
static BufferedImage bufferedImage(int w, int h, Color color) {
return newBufferedImage(w, h, color);
}
static BufferedImage bufferedImage(Pt p, Color color) {
return newBufferedImage(p, color);
}
static BufferedImage bufferedImage(WidthAndHeight size, Color color) {
return newBufferedImage(size.w(), size.h(), color);
}
static BufferedImage bufferedImage(Color color, WidthAndHeight size) {
return bufferedImage(size, color);
}
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 int _hashCode(Object a) {
return a == null ? 0 : a.hashCode();
}
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 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 iround(double d) {
return (int) Math.round(d);
}
static int iround(Number n) {
return iround(toDouble(n));
}
// brightness: 0 to 255
static int rgbIntFromGrayscale(int brightness) {
brightness = clampToUByte(brightness);
return brightness | (brightness << 8) | (brightness << 16);
}
// brightness: 0 to 1
static int rgbIntFromGrayscale(double brightness) {
return rgbIntFromGrayscale(zeroToOneToZeroTo255(brightness));
}
static BufferedImage grayImageFromIBWImage(IBWImage img) {
int w = img.getWidth(), h = img.getHeight();
byte[] pixels = new byte[w*h];
int i = 0;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
int pixel = iround(img.getFloatPixel(x, y)*255); // tODO: is it round or floor?
pixels[i++] = clampToUByte(pixel);
}
return newGrayBufferedImage(w, h, pixels);
}
static BWImage iBWImageToBWImage(IBWImage img) {
int w = img.getWidth(), h = img.getHeight();
BWImage out = new BWImage(w, h);
for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) out.setPixel(x, y, img.getFloatPixel(x, y));
return out;
}
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 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 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 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 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 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 String a(String noun) {
if (eq(noun, "")) return "?";
return ("aeiou".indexOf(noun.charAt(0)) >= 0 ? "an " : "a ") + noun;
}
static String a(String contents, Object... params) {
return hfulltag("a", contents, params);
}
static String b(Object contents, Object... params) {
return tag("b", contents, params);
}
static int hashCodeFor(Object a) {
return a == null ? 0 : a.hashCode();
}
static Object metaGet(IMeta o, Object key) {
return metaMapGet(o, key);
}
static Object metaGet(Object o, Object key) {
return metaMapGet(o, key);
}
static Object metaGet(String key, IMeta o) {
return metaMapGet(o, key);
}
static Object metaGet(String key, Object o) {
return metaMapGet(o, key);
}
static Object metaMapGet(IMeta o, Object key) {
return o == null ? null : o.metaGet(key); // We now let the object itself do it (overridable!)
}
static Object metaMapGet(Object o, Object key) {
return metaMapGet(toIMeta(o), key);
}
static void metaPut(IMeta o, Object key, Object value) {
metaMapPut(o, key, value);
}
static void metaPut(Object o, Object key, Object value) {
metaMapPut(o, key, value);
}
static void metaMapPut(IMeta o, Object key, Object value) {
{ if (o != null) o.metaPut(key, value); }
}
static void metaMapPut(Object o, Object key, Object value) {
var meta = initIMeta(o);
{ if (meta != null) meta.metaPut(key, value); }
}
static Map convertObjectMetaToMap(IMeta o) { return convertObjectMetaToMap(o, () -> makeObjectMetaMap()); }
static Map convertObjectMetaToMap(IMeta o, IF0