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.text.*;
import java.util.TimeZone;
class main {
static class AbstractFastRegions_ShuffledScan implements Steppable {
AbstractFastRegions regionMaker;
WeightlessShuffledIterator iterator;
AbstractFastRegions_ShuffledScan(AbstractFastRegions regionMaker) {
this.regionMaker = regionMaker;
regionMaker.init();
iterator = new WeightlessShuffledIterator(regionMaker.size);
}
public boolean step() {
return findNextRegion() > 0;
}
int findNextRegion() {
var regionMatrix = regionMaker.regionMatrix;
int pixel;
while ((pixel = iterator.nextIndex()) >= 0)
if (regionMatrix[pixel] == 0) {
regionMaker.runner = pixel;
return regionMaker.makeRegion();
}
return 0;
}
// null if there are no more regions
IImageRegion nextRegion() {
int iRegion = findNextRegion();
return iRegion > 0 ? regionMaker.getRegion(iRegion) : null;
}
}
// Deterministic shuffled list iterator using an XORSHIFT RNG
// Each step is O(1), iterator is OSPACE(1)
// This iterator doesn't have a seed parameter and is completely
// deterministic with respect to a given list size.
// If you want to pass a seed, use WeightlessShuffledIteratorWithSeed.
// Performance: Takes between 5 and 18 ns per nextIndex() on a laptop
// and between 2 and 11 ns on a desktop CPU.
// (these numbers are actually for WeightlessShuffledIteratorWithSeed,
// WeightlessShuffledIterator itself is a tiny bit faster.)
static class WeightlessShuffledIterator extends IterableIterator implements IndexIterator {
final List list;
final int n;
final int bits, cycleLength, a, b, c;
int i; // step counter
// now doing the TripletLSFR stuff directly in here
int value;
// initialize only with a length (i.e. list containing 0, 1, ...)
// if you are going to be calling nextIndex() instead of next()
WeightlessShuffledIterator(int n) {
this((List) iotaZeroList(n));
}
// initialize with a list to shuffle
WeightlessShuffledIterator(List list) {
this.list = list;
n = l(list);
if (n == 0) { a = b = c = bits = cycleLength = 0; return; }
bits = numberOfBitsNeededToRepresentNOptions(n+1);
var lsfr = new TripletLSFR(bits);
a = lsfr.a;
b = lsfr.b;
c = lsfr.c;
value = lsfr.value;
cycleLength = lsfr.cycleLength;
}
public boolean hasNext() { return i < n; }
public A next() {
return list.get(nextIndex());
}
// you can call nextIndex() without having checked hasNext(),
// then it will just return -1 when the iteration is complete.
public int nextIndex() {
if (i >= n) return -1;
++i;
int raw;
do {
int x = value;
x ^= (x << a) & cycleLength;
x ^= x >>> b;
x ^= (x << c) & cycleLength;
value = x;
raw = postProcessLSFRValue(x-1);
} while (raw >= n);
return raw;
};
int bits() { return bits; }
// overridable in subclasses
int postProcessLSFRValue(int i) { return i; }
}
// Abstract base class for finding all connected regions of same color
// in an image using flood filling
abstract static class AbstractFastRegions implements Runnable, IImageRegions , IFieldsToList{
Img image;
AbstractFastRegions() {}
AbstractFastRegions(Img image) {
this.image = image;}public Object[] _fieldsToList() { return new Object[] {image}; }
int w, h; // Image size
int runner; // Position in image currently being scanned
final public int getSize(){ return size(); }
public int size() { return size; }
int size; // =w*h
IntBuffer stack = new IntBuffer(); // locations as y*w+x
final public int[] getRegionMatrix(){ return regionMatrix(); }
public int[] regionMatrix() { return regionMatrix; }
int[] regionMatrix; // for each pixel: region index (starting at 1)
IntBuffer regionPixels = new IntBuffer(); // collect all pixels for regions
final public AbstractFastRegions setWithDiagonals(boolean withDiagonals){ return withDiagonals(withDiagonals); }
public AbstractFastRegions withDiagonals(boolean withDiagonals) { this.withDiagonals = withDiagonals; return this; } final public boolean getWithDiagonals(){ return withDiagonals(); }
public boolean withDiagonals() { return withDiagonals; }
boolean withDiagonals = false; // also walk diagonally?
// initialize these to use them
IntBuffer regionFirstPixel = new IntBuffer(); // for each region: index of first pixel found in regionPixels
IntBuffer regionSize = new IntBuffer(); // for each region: number of pixels
IntBuffer regionBounds = new IntBuffer(); // for each region: bounds (x1, y1, x2, y2)
// index = dual log of region size, value = region index
List regionsBySize = new ArrayList();
int regionCounter;
boolean verbose = false;
// temporary
int currentColor;
double regionStep = .1; // for rendering in regionsImage
int x(int pos) { return pos % w; }
int y(int pos) { return pos / w; }
int pos(int x, int y) { return y*w+x; }
Pt pt(int pos) { return new Pt(x(pos), y(pos)); }
boolean validPos(int x, int y) { return x >= 0 && y >= 0 && x < w && y < h; }
abstract int getColor(int pos);
boolean initialized() { return regionMatrix != null; }
void init() {
if (initialized()) return;
w = image.getWidth(); h = image.getHeight();
size = w*h;
regionMatrix = new int[size];
// 0 entries are unused
{ if (regionFirstPixel != null) regionFirstPixel.add(0); }
{ if (regionSize != null) regionSize.add(0); }
{ if (regionPixels != null) regionPixels.setSize(size); }
}
// Search for all regions in scanline order
public void run() { try {
if (initialized()) return;
init();
while (runner < size) {
if (regionMatrix[runner] == 0)
makeRegion();
++runner;
}
} catch (Exception __e) { throw rethrow(__e); } }
// returns index of new region
int makeRegion() {
// make a new region, get color
int region = ++regionCounter;
{ if (regionFirstPixel != null) regionFirstPixel.add(regionPixels != null ? l(regionPixels) : runner); }
currentColor = getColor(runner);
stack.add(runner);
int rsize = 0, x1 = w, y1 = h, x2 = 0, y2 = 0;
// flood-fill region
while (nempty(stack)) {
int pos = stack.popLast();
if (regionMatrix[pos] != 0) continue; // check again if set in the meantime (color has been checked before)
// new pixel found!
int x = x(pos), y = y(pos);
// march to the left
int lineStart = pos-x;
int xLeft = x;
while (xLeft > 0 && addable(lineStart+xLeft-1))
--xLeft;
// march to the right
int xRight = x+1;
while (xRight < w && addable(lineStart+xRight))
++xRight;
// Now we know [xLeft; xRight) are our pixels
// mark them in matrix
for (x = xLeft; x < xRight; x++) {
regionMatrix[lineStart+x] = region;
{ if (regionPixels != null) regionPixels.add(lineStart+x); }
}
// increase region size
rsize += xRight-xLeft;
// update bounds
if (xLeft < x1) x1 = xLeft;
if (xRight-1 > x2) x2 = xRight-1;
if (y < y1) y1 = y;
if (y > y2) y2 = y;
// explore above & below
int xLeft2 = withDiagonals ? max(0, xLeft-1) : xLeft;
int xRight2 = withDiagonals ? min(w, xRight+1) : xRight;
if (y > 0)
addStreaks(lineStart-w, xLeft2, xRight2);
if (y < h-1)
addStreaks(lineStart+w, xLeft2, xRight2);
}
{ if (regionSize != null) regionSize.add(rsize); }
{ if (regionBounds != null) regionBounds.addAll(x1, y1, x2+1, y2+1); }
if (regionsBySize != null) {
int iBucket = dualLog(rsize);
var buffer = listGetOrCreate(regionsBySize, iBucket, () -> new IntBuffer());
buffer.add(region);
}
return region;
}
boolean addable(int pos) {
if (regionMatrix[pos] != 0) return false; // touching myself (or someone else)
if (getColor(pos) != currentColor) return false; // wrong color
return true;
}
boolean addToStack(int pos) {
if (!addable(pos)) return false;
stack.add(pos);
return true;
}
void addStreaks(int lineStart, int xLeft, int xRight) {
int x = xLeft;
while (x < xRight) {
if (addToStack(lineStart+x))
while (x+1 < xRight && addable(lineStart+x+1))
++x;
++x;
}
}
IBWImage regionsImage() {
return iBWImageFromFunction((x, y) -> {
var region = regionMatrix[pos(x, y)];
return ((region-1)*regionStep) % (1.0+regionStep-0.0001);
}, w, h);
}
final public int nRegions(){ return regionCount(); }
public int regionCount() { return regionCounter; }
abstract class RegionIterator {
int pos;
abstract boolean next();
int pos() { return pos; }
int x() { return AbstractFastRegions.this.x(pos); }
int y() { return AbstractFastRegions.this.y(pos); }
int[] pixelsAsIntArray() { throw todo(); }
}
// returns points in no particular order
class FloodRegionIterator extends RegionIterator {
int region;
IntBuffer stack = new IntBuffer(); // locations as y*w+x
BitSet seen = new BitSet(size);
FloodRegionIterator(int region) {
this.region = region;
int pos = regionFirstPixel.get(region);
printVars("region", region, "pos", pos);
seen.set(pos);
stack.add(pos);
}
// flood-fill region
boolean next() {
if (empty(stack)) return false;
pos = stack.popLast();
// explore neighborhood
int x = x(), y = y();
if (x > 0) tryPosition(pos-1);
if (x < w-1) tryPosition(pos+1);
if (y > 0) tryPosition(pos-w);
if (y < h-1) tryPosition(pos+w);
return true;
}
private void tryPosition(int p) {
if (!seen.get(p) && regionMatrix[p] == region) {
seen.set(p);
stack.add(p);
}
}
}
class CachedRegionIterator extends RegionIterator {
int i, to;
CachedRegionIterator(int region) {
i = regionFirstPixel.get(region);
to = region+1 < l(regionFirstPixel) ? regionFirstPixel.get(region+1) : l(regionPixels);
}
boolean next() {
if (i >= to) return false;
pos = regionPixels.get(i++);
return true;
}
int[] pixelsAsIntArray() {
return regionPixels.subArray(i, to);
}
}
int regionSize(int iRegion) { return regionSize.get(iRegion); }
final Pt firstPixel(int iRegion){ return samplePixel(iRegion); }
Pt samplePixel(int iRegion) {
return pt(firstPixelPos(iRegion));
}
int firstPixelPos(int iRegion) {
int i = regionFirstPixel.get(iRegion);
return regionPixels != null ? regionPixels.get(i) : i;
}
boolean inRegion(int iRegion, int x, int y) {
return validPos(x, y) && regionMatrix[pos(x, y)] == iRegion;
}
Rect regionBounds(int iRegion) { return rectFromPoints(
regionBounds.get((iRegion-1)*4),
regionBounds.get((iRegion-1)*4+1),
regionBounds.get((iRegion-1)*4+2),
regionBounds.get((iRegion-1)*4+3)); }
int regionAt(Pt p) { return regionAt(p.x, p.y); }
int regionAt(int x, int y) {
return !validPos(x, y) ? 0 : regionMatrix[pos(x, y)];
}
int regionAt(int pos) {
return regionMatrix[pos];
}
RegionIterator regionIterator(int iRegion) {
return regionPixels != null
? new CachedRegionIterator(iRegion)
: new FloodRegionIterator(iRegion);
}
List regionPixels(int iRegion) {
var it = regionIterator(iRegion);
List l = new ArrayList();
while (it.next())
l.add(new Pt(it.x(), it.y()));
return l;
}
// select extra features before regions are made
// (not necessary anymore)
void collectFirstPixels() { /*regionFirstPixel = new IntBuffer;*/ }
void collectBounds() { /*regionBounds = new IntBuffer;*/ }
Matrix regionBitMatrix(int iRegion) {
return new AbstractMatrix(w, h) {
public Boolean get(int x, int y) {
return inRegion(iRegion, x, y);
}
};
}
void markRegionInPixelArray(int[] pixels, int iRegion, int rgba) {
if (iRegion <= 0) return;
for (int i = 0; i < l(pixels); i++)
if (regionAt(i) == iRegion)
pixels[i] = rgba;
}
List regionIndices() { return virtualCountList(1, nRegions()+1); }
IterableIterator regionsRoughlyByDecreasingSize() {
return nestedIterator(countIterator_inclusive_backwards(regionsBySize.size()-1, 0),
iBucket -> iterator(regionsBySize.get(iBucket)));
}
final public IImageRegion get(int iRegion){ return getRegion(iRegion); }
public IImageRegion getRegion(int iRegion) {
return new ImageRegion(iRegion);
}
final public List> get(){ return regions(); }
public List> regions() {
run();
return listFromFunction(i -> getRegion(i+1), nRegions());
}
class ImageRegion implements IImageRegion , IFieldsToList{
int iRegion;
ImageRegion() {}
ImageRegion(int iRegion) {
this.iRegion = iRegion;}public Object[] _fieldsToList() { return new Object[] {iRegion}; }
public int hashCode() { return iRegion; }
public boolean equals(Object o) {
if (!(o instanceof AbstractFastRegions.ImageRegion)) return false;
return ((AbstractFastRegions.ImageRegion) o).creator() == creator()
&& ((AbstractFastRegions.ImageRegion) o).iRegion == iRegion;
}
public Img image() { return image; }
public Object creator() { return AbstractFastRegions.this; }
public int indexInCreator() { return iRegion; }
public Boolean createdWithDiagonals() { return withDiagonals; }
public Rect bounds() { return regionBounds(iRegion); }
public int numberOfPixels() { return regionSize(iRegion); }
public Pt firstPixel() { return pt(firstPixelPos()); }
public int firstPixelPos() { return AbstractFastRegions.this.firstPixelPos(iRegion); }
public IterableIterator pixelIterator() {
var it = regionIterator(iRegion);
return iteratorFromFunction(() -> it.next() ? pt(it.pos()) : null);
}
public int[] pixelsAsIntArray() {
return regionIterator(iRegion).pixelsAsIntArray();
}
public boolean contains(int x, int y) { return inRegion(iRegion, x, y); }
public Color color() {
return toColor(rgbForRegion(this));
}
final public int firstPixelLogicalColor(){ return brightness(); }
public int brightness() {
return getColor(firstPixelPos());
}
public String toString() {
return renderRecordVars("Region", "brightness", brightness(), "color", color(), "pixels" , numberOfPixels(), "bounds", bounds());
}
}
abstract RGB rgbForRegion(ImageRegion r);
public Img image() { return image; }
// called after scan was performed externally
void markDone() { runner = size; }
}
// Note for implementors: It is allowed to call step() again
// after it returned false (usually it should just return false again)
static interface Steppable {
public boolean step(); // return false if done
}
interface IImageRegion extends INumberOfPixels, IPixelSet {
// REQUIRED METHODS
// smallest rectangle that all of the region's pixels are contained in
public Rect bounds();
public IterableIterator pixelIterator();
public boolean contains(int x, int y);
// OPTIONAL METHODS
// get whole image that the region refers to
public default Img image() { return null; }
// which object made this
public default Object creator() { return null; }
public default int indexInCreator() { return 0; }
// was diagonal walking enabled?
public default Boolean createdWithDiagonals() { return null; }
public default int nPixels(){ return numberOfPixels(); }
public default int numberOfPixels() { return l(pixelIterator()); }
public default Pt firstPixel() { return first(pixelIterator()); }
// gets the region's color (what exactly this means is defined
// by the creator of this object)
public default Color color() { return null; }
public default int brightness() { return -1; }
public default OnePathWithOrigin outline() {
return g22_regionOutline(this);
}
default Image2B toImage2B() { return new RegionToImage2B(this).get(); }
default int[] pixelsAsIntArray() { throw unimplemented(); }
// CONVENIENCE/ORTHOGONALITY METHODS
default IPixelSet pixelSet() { return this; }
}
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()); }
}
interface IImageRegions {
public List> regions();
// for each pixel: region index (starting at 1)
int[] regionMatrix();
Img image();
int nRegions();
IImageRegion getRegion(int i);
}
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; }
final public int x() { return x; }
final public int y() { return y; }
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); }
default float getFloatPixel(int index) {
int w = w();
return getFloatPixel(index % w, index / w);
}
default float[] toFloatArray() {
float[] data = new float[w()*h()];
for (int i = 0; i < l(data); i++)
data[i] = getFloatPixel(i);
return data;
}
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; }
}
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; cleanPixelArray(); }
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);
}
// clear unused bits in pixel array after we received
// a possibly dirty array
void cleanPixelArray() {
int n = w*h;
if ((n & 7) != 0)
pixels[n/8] &= (1 << (n & 7))-1;
}
public double averageBrightness() {
return doubleRatio(bitCount(pixels), w*h);
}
}
// result image is as large as the region's bounds
// with black pixels inside and white pixels outside the region
static class RegionToImage2B implements IFieldsToList{
IPixelSet region;
RegionToImage2B() {}
RegionToImage2B(IPixelSet region) {
this.region = region;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + region + ")"; }public Object[] _fieldsToList() { return new Object[] {region}; }
final public RegionToImage2B setBounds(Rect bounds){ return bounds(bounds); }
public RegionToImage2B bounds(Rect bounds) { this.bounds = bounds; return this; } final public Rect getBounds(){ return bounds(); }
public Rect bounds() { return bounds; }
Rect bounds;
Image2B image;
public void run() { try {
if (bounds == null) bounds = region.bounds();
int x1 = bounds.x, y1 = bounds.y, w = bounds.w, h = bounds.h, i = 0;
byte[] pixels = new byte[(w*h+7)/8];
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
if (!region.contains(x1+x, y1+y))
pixels[i/8] |= 1 << (i & 7);
i++;
}
image = new Image2B(w, h, pixels);
} catch (Exception __e) { throw rethrow(__e); } }
Image2B get() {
if (image == null) run();
return image;
}
}
interface IndexIterator {
// return next index or -1 if finished
public int nextIndex();
}
// you still need to implement hasNext() and next()
static abstract class IterableIterator implements Iterator, Iterable {
public Iterator iterator() {
return this;
}
public void remove() {
unsupportedOperation();
}
}
static class RGB {
// usually in range [0, 1]
public float r, g, b; // can't be final cause persistence
RGB() {}
public RGB(float brightness) {
r = g = b = brightness;
}
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((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(clampZeroToOne(r), clampZeroToOne(g), clampZeroToOne(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; }
final public float brightness(){ return getBrightness(); }
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;
}
// This is hyperprecise
boolean isGrayscale() {
return r == g && g == b;
}
public String toString() {
//return getHexString();
return isGrayscale() ? formatChannel(r)
: spaceCombine("RGB", formatChannel(r), formatChannel(g), formatChannel(b));
}
String formatChannel(float value) {
return formatDouble(value, 2);
}
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; }
RGB cloneMe() { return new RGB(r, g, b); }
}
interface IPixelSet extends HasBounds {
public boolean contains(int x, int y);
default boolean contains(Pt p) { return contains(p.x, p.y); }
default public IterableIterator pixelIterator() {
return filterI(main.pixelIterator(bounds()), p -> contains(p));
}
}
interface INumberOfPixels {
int numberOfPixels();
}
static class OnePathWithOrigin extends OnePath {
final public OnePathWithOrigin setOrigin(Pt origin){ return origin(origin); }
public OnePathWithOrigin origin(Pt origin) { this.origin = origin; return this; } final public Pt getOrigin(){ return origin(); }
public Pt origin() { return origin; }
Pt origin = pt(0, 0);
OnePathWithOrigin() {}
OnePathWithOrigin(String path) { super(path); }
OnePathWithOrigin(Pt origin, String path) { super(path);
this.origin = origin; }
OnePathWithOrigin(int originX, int originY, String path) { super(path); origin = pt(originX, originY); }
OnePathWithOrigin(Iterable points, boolean close) {
var l = asList(points);
origin = first(l);
fromPoints(l, close);
}
OnePathWithOrigin(OnePathWithOrigin path) {
super(path);
origin = path.origin;
}
public String toString() {
return "Origin (" + origin + "), path: " + super.toString();
}
OnePathWithOrigin reversed() {
return new OnePathWithOrigin(reversedList(pointList()), false);
}
}
static interface IFieldsToList {
Object[] _fieldsToList();
}
static class IntBuffer implements Iterable {
int[] data;
int size;
IntBuffer() {}
IntBuffer(int size) { if (size != 0) data = new int[size]; }
IntBuffer(Iterable l) {
if (l instanceof Collection) allocate(((Collection) l).size());
addAll(l);
}
void add(int i) {
if (size >= lIntArray(data)) {
data = resizeIntArray(data, Math.max(1, toInt(Math.min(maximumSafeArraySize(), lIntArray(data)*2L))));
if (size >= data.length) throw fail("IntBuffer too large: " + size);
}
data[size++] = i;
}
void allocate(int n) {
data = resizeIntArray(data, max(n, size()));
}
void setSize(int n) {
data = resizeIntArray(data, n);
size = min(l(data), size);
}
void addAll(Iterable l) {
if (l != null) for (int i : l) add(i);
}
void addAll(int... l) {
if (l != null) for (int i : l) add(i);
}
// Note: may return the internal array
final int[] toIntArray(){ return toArray(); }
int[] toArray() {
return size == 0 ? null : resizeIntArray(data, size);
}
int[] subArray(int from, int to) {
return subIntArray(data, from, min(to, size));
}
int[] toArrayNonNull() {
return unnull(toArray());
}
// abandoned version
/*L toList() {
ret intArrayToList(data, 0, size);
}*/
// now all these return a virtual list
final List asList(){ return toList(); }
final List asVirtualList(){ return toList(); }
List toList() {
return new RandomAccessAbstractList() {
public int size() { return size; }
public Integer get(int i) { return IntBuffer.this.get(i); }
public Integer set(int i, Integer val) {
Integer a = get(i);
data[i] = val;
return a;
}
};
}
void reset() { size = 0; }
void clear() { reset(); }
int size() { return size; }
boolean isEmpty() { return size == 0; }
int get(int idx) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
return data[idx];
}
void set(int idx, int value) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
data[idx] = value;
}
int popLast() {
if (size == 0) throw fail("empty buffer");
return data[--size];
}
int last() { return data[size-1]; }
int nextToLast() { return data[size-2]; }
public String toString() { return squareBracket(joinWithSpace(toList())); }
public Iterator iterator() {
return new IterableIterator() {
int i = 0;
public boolean hasNext() { return i < size; }
public Integer next() {
//if (!hasNext()) fail("Index out of bounds: " + i);
return data[i++];
}
};
}
public IntegerIterator integerIterator() {
return new IntegerIterator() {
int i = 0;
public boolean hasNext() { return i < size; }
public int next() {
//if (!hasNext()) fail("Index out of bounds: " + i);
return data[i++];
}
public String toString() { return "Iterator@" + i + " over " + IntBuffer.this; }
};
}
void trimToSize() {
data = resizeIntArray(data, size);
}
}
// This is an LFSR (aka the XORSHIFT algorithm) which is a deterministic,
// very fast random number generator cycling through all integers
// between 1 and 2^n-1 (incl.).
static class TripletLSFR {
// params
int n;
int value = 1; // start value, but is also changed during execution
int a, b, c; // the triplet
// calculated
final public int getCycleLength(){ return cycleLength(); }
public int cycleLength() { return cycleLength; }
int cycleLength;
// changing variables
int step;
// triplets table for n's from 1 to 31
// (just the first triple that gives a full cycle, including degenerate ones,
// so not optimized for "randomness")
static final int[][] tripletsTable = {
{ 0, 0, 0 },
{ 0, 1, 1 },
{ 0, 1, 1 },
{ 0, 1, 3 },
{ 0, 1, 1 },
{ 0, 1, 1 },
{ 0, 1, 3 },
{ 1, 1, 2 },
{ 0, 1, 1 },
{ 0, 3, 7 },
{ 0, 1, 1 },
{ 1, 1, 4 },
{ 0, 1, 6 },
{ 0, 1, 1 },
{ 0, 1, 4 },
{ 1, 1, 14 },
{ 0, 3, 7 },
{ 1, 3, 14 },
{ 0, 1, 3 },
{ 1, 5, 6 },
{ 0, 1, 6 },
{ 0, 1, 21 },
{ 0, 1, 1 },
{ 1, 5, 18 },
{ 0, 1, 12 },
{ 0, 1, 1 },
{ 0, 2, 7 },
{ 0, 3, 25 },
{ 0, 1, 1 },
{ 0, 1, 1 },
{ 0, 3, 28 }
};
TripletLSFR(int n) {
this.n = n;
if (n < 1 || n > tripletsTable.length)
throw fail("Don't have triples for " + nBits(n));
init(tripletsTable[n-1]);
}
TripletLSFR(int n, int a, int b, int c) {
this.n = n;
init(a, b, c);
}
void init(int[] triplet) {
init(triplet[0], triplet[1], triplet[2]);
}
void init(int a, int b, int c) {
// handle triplets with 0s (meaning to skip one of the 3 xorshifts)
this.a = a == 0 ? n : a;
this.b = b == 0 ? n : b;
this.c = c == 0 ? n : c;
cycleLength = (1 << n)-1;
}
int next() {
step++;
int x = value;
x ^= (x << a) & cycleLength;
x ^= x >>> b;
x ^= (x << c) & cycleLength;
return value = x;
}
boolean cycleComplete() { return step >= cycleLength(); }
// takes any integer
final TripletLSFR value(int start){ return start(start); }
TripletLSFR start(int start) {
value = mod(start, cycleLength)+1;
return this;
}
}
abstract static class AbstractMatrix implements Matrix {
AbstractMatrix() {}
int w, h;
AbstractMatrix(int w, int h) {
this.h = h;
this.w = w;}
public int getWidth() { return w; }
public int getHeight() { return h; }
public String toString() {
return flexLines(
roundBracket(w+"x"+h),
countIteratorToList(y -> str(getLine(y)), getHeight()));
}
public void set(int x, int y, A a) { throw unimplemented(); }
}
static interface IRGBImage extends MakesBufferedImage {
int getIntPixel(int x, int y);
}
interface Matrix extends WidthAndHeight {
public A get(int x, int y);
public void set(int x, int y, A a);
default Pt size() { return pt(getWidth(), getHeight()); }
default int nCells() { return getWidth()*getHeight(); }
default A get(Pt p) { return get(p.x, p.y); }
default void put(Pt p, A a){ set(p, a); }
default void set(Pt p, A a) { set(p.x, p.y, a); }
// one row of the matrix as a virtual list (mutable)
default List getLine(int y) {
return new RandomAccessAbstractList() {
public int size() { return getWidth(); }
public A get(int x) {
return Matrix.this.get(x, y);
}
public A set(int x, A val) {
A old = Matrix.this.get(x, y);
Matrix.this.set(x, y, val);
return old;
}
};
}
}
interface IDoublePt {
public double x_double();
public double y_double();
}
abstract static class RandomAccessAbstractList extends AbstractList implements RandomAccess {
}
abstract static class IntegerIterator {
abstract boolean hasNext();
abstract int next();
}
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;
}
// idx = index in pixel array
public int getIntPixel_noRangeCheck(int idx) {
return pixels[idx];
}
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);
}
default int getWidth() { return getBufferedImage().getWidth(); }
default int getHeight() { return getBufferedImage().getHeight(); }
}
static class OnePath {
ByteBuffer steps = new ByteBuffer();
OnePath() {}
OnePath(String path) {
int n = l(path);
steps.allocate(n);
for (int i = 0; i < n; i++)
steps.add(parseDigit(path, i));
}
OnePath(OnePath path) {
steps = new ByteBuffer(path.steps);
}
// uses first point as origin, so there will be l(points)-1 steps.
// Unless you set close to true, then it adds the first point at the end again.
OnePath(Iterable points, boolean close) {
fromPoints(points, close);
}
void fromPoints(Iterable points, boolean close) {
var it = iterator(points);
if (empty(it)) return;
Pt firstPoint = it.next(), p = firstPoint;
while (it.hasNext()) {
Pt p2 = it.next();
steps.add(ptToDigit(ptMinus(p2, p)));
p = p2;
}
if (close)
steps.add(ptToDigit(ptMinus(firstPoint, p)));
}
final int length(){ return size(); }
final int nSteps(){ return size(); }
int size() { return steps.size(); }
public String toString() { return pathString(); }
String pathString() {
return singleDigitBytesToString(steps);
}
// includes first point, so returns length()+1 points in total
IterableIterator pointIterator() { return pointIterator(origin()); }
IterableIterator pointIterator(Pt startPt) {
return new IterableIterator() {
int i = 0, n = length();
Pt p = startPt;
public boolean hasNext() { return i <= n; }
public Pt next() {
var p = this.p;
if (i < n) this.p = translatePt(this.p, getStepAsPt(i));
++i;
return p;
}
};
}
final List pointsList(){ return pointList(); }
List pointList() { return ptBuffer(pointIterator()); }
int getStep(int i) { return steps.get(i); }
Pt getStepAsPt(int i) {
return onePathDirections()[steps.get(i)];
}
static int ptToDigit(Pt p) {
return p.y < 0 ? p.x < 0 ? 1 : p.x == 0 ? 2 : 3
: p.y == 0 ? p.x < 0 ? 8 : p.x == 0 ? 0 : 4
: p.x < 0 ? 7 : p.x == 0 ? 6 : 5;
}
Pt origin() { return main.origin(); }
Pt endPoint() { return last(pointIterator()); }
Pt drift() {
return ptMinus(endPoint(), origin());
}
void addStep(int p_x, int p_y) { addStep(pt(p_x, p_y)); }
void addStep(Pt p) {
int step = onePathLookupDirection(p);
if (step < 0) throw fail("Invalid one path step: " + p);
addStep(step);
}
void addStep(int step) {
assertBetween(0, 8, step);
steps.add(step);
}
void insertStep(int idx, int p_x, int p_y) { insertStep(idx, pt(p_x, p_y)); }
void insertStep(int idx, Pt p) {
int step = onePathLookupDirection(p);
if (step < 0) throw fail("Invalid one path step: " + p);
insertStep(idx, step);
}
void insertStep(int idx, int step) {
assertBetween(0, 8, step);
steps.add(idx, step);
}
Rect bounds() {
return boundsOfPts(pointIterator());
}
}
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 IBinaryImage extends MakesBufferedImage {
boolean getBoolPixel(int x, int y);
default void setPixel(int x, int y, boolean b) { throw unimplemented(); }
default boolean getBoolPixel(Pt p) {
return getBoolPixel(p.x, p.y);
}
default boolean getBoolPixel_noRangeCheck(int x, int y) { return getBoolPixel(x, y); }
public default BufferedImage getBufferedImage() {
return toImage2B().getBufferedImage();
}
default Image2B toImage2B() {
return this instanceof Image2B ? (Image2B) this : iBinaryImageToImage2B(this);
}
}
interface HasBounds extends WidthAndHeight {
Rect bounds();
default int getWidth() { return bounds().h; }
default int getHeight() { return bounds().w; }
}
// 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(); }
final boolean scaffolded(){ return scaffoldingEnabled(); }
boolean scaffoldingEnabled() { return main.scaffoldingEnabled(this); }
boolean scaffoldingEnabled(Object o) { return main.scaffoldingEnabled(o); }
// Implementing setMetaToString
String toString_base() { return super.toString(); }
public String toString() {
Object o = metaGet("toString", this);
if (o instanceof String) return ((String) o);
if (o instanceof IF1) return str(((IF1) o).get(this));
return toString_base();
}
}
static class ByteBuffer implements Iterable {
byte[] data;
int size;
ByteBuffer() {}
ByteBuffer(int size) { if (size != 0) data = new byte[size]; }
ByteBuffer(Iterable l) {
if (l instanceof Collection) allocate(((Collection) l).size());
addAll(l);
}
ByteBuffer(byte[] data) { this.data = data; size = l(data); }
// TODO *(ByteBuffer buf) { ... }
void add(int idx, int i) {
add(0);
arraycopy(data, idx, data, idx+1, size-(idx+1));
data[idx] = (byte) i;
}
void add(int i) { add((byte) i); }
void add(byte i) {
if (size >= lByteArray(data)) {
allocate(Math.max(1, toInt(Math.min(maximumSafeArraySize(), lByteArray(data)*2L))));
if (size >= data.length) throw fail("ByteBuffer too large: " + size);
}
data[size++] = i;
}
void allocate(int n) {
data = resizeByteArray(data, max(n, size()));
}
void addAll(Iterable l) {
if (l != null) for (byte i : l) add(i);
}
final byte[] toByteArray(){ return toArray(); }
byte[] toArray() {
return size == 0 ? null : resizeByteArray(data, size);
}
List toList() {
return byteArrayToList(data, 0, size);
}
List asVirtualList() {
return new RandomAccessAbstractList() {
public int size() { return size; }
public Byte get(int i) { return ByteBuffer.this.get(i); }
public Byte set(int i, Byte val) {
Byte a = get(i);
data[i] = val;
return a;
}
};
}
void reset() { size = 0; }
void clear() { reset(); }
int size() { return size; }
boolean isEmpty() { return size == 0; }
byte get(int idx) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
return data[idx];
}
void set(int idx, byte value) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
data[idx] = value;
}
byte popLast() {
if (size == 0) throw fail("empty buffer");
return data[--size];
}
// unefficient
byte popFirst() {
if (size == 0) throw fail("empty buffer");
byte b = data[0];
arraycopy(data, 1, 0, --size);
return b;
}
byte last() { return data[size-1]; }
byte nextToLast() { return data[size-2]; }
public String toString() { return squareBracket(joinWithSpace(toList())); }
public Iterator iterator() {
return new IterableIterator() {
int i = 0;
public boolean hasNext() { return i < size; }
public Byte next() {
//if (!hasNext()) fail("Index out of bounds: " + i);
return data[i++];
}
};
}
/*public ByteIterator byteIterator() {
ret new ByteIterator {
int i = 0;
public bool hasNext() { ret i < size; }
public int next() {
//if (!hasNext()) fail("Index out of bounds: " + i);
ret data[i++];
}
toString { ret "Iterator@" + i + " over " + ByteBuffer.this; }
};
}*/
void trimToSize() {
data = resizeByteArray(data, size);
}
int indexOf(byte b) {
for (int i = 0; i < size; i++)
if (data[i] == b)
return i;
return -1;
}
byte[] subArray(int start, int end) {
return subByteArray(data, start, min(end, size));
}
}
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);
}
}
static interface IAutoCloseableF0 extends IF0, AutoCloseable {}
public static interface IF0 {
A get();
}
static List iotaZeroList(int n) {
return intRangeList(0, 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(IntBuffer b) { return b == null ? 0 : b.size(); }
static int l(LongBuffer b) { return b == null ? 0 : b.size(); }
static int l(IntSize o) { return o == null ? 0 : o.size(); }
static int numberOfBitsNeededToRepresentNOptions(int i) {
return 32-Integer.numberOfLeadingZeros(max(0, i-1));
}
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 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 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(IntBuffer b) { return b != null && !b.isEmpty(); }
static boolean nempty(LongBuffer b) { return b != null && !b.isEmpty(); }
static boolean nempty(Rect r) { return r != null && r.w != 0 && r.h != 0; }
static boolean nempty(IntSize l) { return l != null && l.size() != 0; }
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 > A min(A a, A b) {
return cmp(a, b) <= 0 ? a : b;
}
static int dualLog(int i) {
return 32-Integer.numberOfLeadingZeros(max(0, i-1));
}
static A listGetOrCreate(List l, int i, Class extends A> c) {
return listGetOrCreate(l, i, () -> nuInstance(c));
}
static A listGetOrCreate(List l, int i, IF0 f) {
if (l == null) return null;
A a = get(l, i);
if (a == null)
listSet(l, i, a = f.get());
return a;
}
static A listGetOrCreate(Class extends A> c, List l, int i) {
return listGetOrCreate(l, i, c);
}
static IBWImage iBWImageFromFunction(IF2_IntInt_Double f, int w, int h) {
return new IBWImage() {
public int getWidth() { return w; }
public int getHeight() { return h; }
public float getFloatPixel(int x, int y) {
return (float) f.get(x, y);
}
};
}
static RuntimeException todo() {
throw new RuntimeException("TODO");
}
static RuntimeException todo(Object msg) {
throw new RuntimeException("TODO: " + msg);
}
// Use like this: printVars(+x, +y);
// Or: printVars("bla", +x);
// Or: printVars bla(, +x);
static void printVars(Object... params) {
printVars_str(params);
}
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(IntBuffer b) { return b == null || b.isEmpty(); }
static boolean empty(LongBuffer b) { return b == null || b.isEmpty(); }
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 boolean empty(IntSize l) { return l == null || l.size() == 0; }
static Pt pt(int x, int y) {
return new Pt(x, y);
}
static Pt pt(int x) {
return new Pt(x, x);
}
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);
}
// 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 List virtualCountList(int n) {
return new RandomAccessAbstractList() {
public int size() { return n; }
public Integer get(int i) { return i; }
};
}
// to is exclusive
static List virtualCountList(int from, int to) {
int n = max(to-from, 0);
return new RandomAccessAbstractList() {
public int size() { return n; }
public Integer get(int i) { return from+i; }
};
}
static String nRegions(long n) { return n2(n, "region"); }
static String nRegions(Collection l) { return nRegions(l(l)); }
static String nRegions(Map map) { return nRegions(l(map)); }
// TODO: clean up these type signatures?
static > IterableIterator nestedIterator(Iterable c, final F1 makeInnerIterator) {
return nestedIterator(iterator(c), makeInnerIterator);
}
static > IterableIterator nestedIterator(Iterable c, IF1 makeInnerIterator) {
return nestedIterator(iterator(c), makeInnerIterator);
}
static > IterableIterator nestedIterator(IterableIterator c, IF1 makeInnerIterator) {
return nestedIterator((Iterator) c, makeInnerIterator);
}
static > IterableIterator nestedIterator(Iterator it1, IF1 makeInnerIterator) {
if (it1 == null || !it1.hasNext()) return emptyItIt();
return iff(new F0() {
A a;
Iterator innerIterator;
{ nextOuter(); }
void nextOuter() {
a = it1.next();
innerIterator = makeInnerIterator.get(a);
}
public Object get() {
while (true) { ping();
if (innerIterator != null && innerIterator.hasNext())
return innerIterator.next();
if (!it1.hasNext()) return endMarker();
nextOuter();
}
}
});
}
static > IterableIterator nestedIterator(final Iterator it1, F1 makeInnerIterator) {
if (it1 == null || !it1.hasNext()) return emptyItIt();
return iff(new F0() {
A a;
Iterator innerIterator;
{ nextOuter(); }
void nextOuter() {
a = it1.next();
innerIterator = makeInnerIterator.get(a);
}
public Object get() {
while (true) { ping();
if (innerIterator != null && innerIterator.hasNext())
return innerIterator.next();
if (!it1.hasNext()) return endMarker();
nextOuter();
}
}
});
}
static > IterableIterator nestedIterator(IF1 makeInnerIterator, Iterator it1) {
return nestedIterator(it1, makeInnerIterator);
}
static > IterableIterator nestedIterator(IF1 makeInnerIterator, Collection l) {
return nestedIterator(l, makeInnerIterator);
}
static IterableIterator countIterator_inclusive_backwards(int a, int b) {
return new IterableIterator() {
int i = a;
public boolean hasNext() { return i >= b; }
public Integer next() { return i--; }
};
}
static IterableIterator countIterator_inclusive_backwards(int a, int b, IF1 f) {
return mapI_if1(f, countIterator_inclusive_backwards(a, b));
}
static Iterator iterator(Iterable c) {
return c == null ? emptyIterator() : c.iterator();
}
static Class run(String progID, String... args) {
Class main = hotwire(progID);
callMain(main, args);
return main;
}
static class ListFromFunction extends RandomAccessAbstractList implements IFieldsToList{
int n;
IF1 f;
ListFromFunction() {}
ListFromFunction(int n, IF1 f) {
this.f = f;
this.n = n;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + n + ", " + f + ")"; }public Object[] _fieldsToList() { return new Object[] {n, f}; }
public int size() { return n; }
public A get(int i) { return f.get(i); }
}
static List listFromFunction(int n, IF1 f) {
return new ListFromFunction(n, f);
}
static List listFromFunction(IF1 f, int n) {
return new ListFromFunction(n, f);
}
static WeakReference