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.image.DataBufferByte; import java.awt.geom.*; import java.text.*; import java.text.NumberFormat; import java.util.TimeZone; import static x30_pkg.x30_util.DynamicObject; import java.text.SimpleDateFormat; import java.nio.charset.Charset; class main { static void gazelle22_highlightRegionOutline(int[] pixels, Gazelle22_ImageToRegions itr, RegionBorder_innerPoints x) { class X { boolean hole = false; int n; Gazelle22_GradientImage gradientImage = new Gazelle22_GradientImage(itr.posterized); X() { gradientImage.init(); int color = 0xFF008000, holeColor = 0xFFFFB266; x.onNewTrace(isHole -> hole = isHole); x.onFoundPoint(p -> { ++n; boolean gradient = gradientImage.isGradientPoint(p.x, p.y); int col = hole ? holeColor : color; if (gradient) //col = darkerIntColor(col); if (odd(n)) return; pixels[p.y*itr.regions.w+p.x] = col; }); x.run(); } } new X(); } static boolean odd(int i) { return (i & 1) != 0; } static boolean odd(long i) { return (i & 1) != 0; } static boolean odd(BigInteger i) { return odd(toInt(i)); } 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 int parseInt(String s) { return emptyString(s) ? 0 : Integer.parseInt(s); } static int parseInt(char c) { return Integer.parseInt(str(c)); } static int boolToInt(boolean b) { return b ? 1 : 0; } static RuntimeException fail() { throw new RuntimeException("fail"); } static RuntimeException fail(Throwable e) { throw asRuntimeException(e); } static RuntimeException fail(Object msg) { throw new RuntimeException(String.valueOf(msg)); } static RuntimeException fail(Object... objects) { throw new Fail(objects); } static RuntimeException fail(String msg) { throw new RuntimeException(msg == null ? "" : msg); } static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); } static String getClassName(Object o) { return o == null ? "null" : o instanceof Class ? ((Class) o).getName() : o.getClass().getName(); } static boolean emptyString(String s) { return s == null || s.length() == 0; } 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 RuntimeException asRuntimeException(Throwable t) { if (t instanceof Error) _handleError((Error) t); return t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t); } static void _handleError(Error e) { //call(javax(), '_handleError, e); } static class Gazelle22_GradientImage { // input BWImage posterized; double gradientThreshold = .25; // output BWImage out; // internal PosterizeBWImage op; BWImage unposterized; int w, h, brightnessLevels, singleStep; Gazelle22_GradientImage(BWImage posterized) { this.posterized = posterized;} boolean init() { op = (PosterizeBWImage) getMetaSrc(posterized); if (op == null) return false; unposterized = op.img; brightnessLevels = op.brightnessLevels; assertSameSize(unposterized, posterized); w = posterized.getWidth(); h = posterized.getHeight(); singleStep = iceil(doubleRatio(255, brightnessLevels-1)); return true; } BWImage get() { init(); out = new BWImage(w*2+1, h*2+1); for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { if (x < w-1) checkPair(x, y, x+1, y); if (y < h-1) checkPair(x, y, x, y+1); } return out; } // also returns true for inner pixels // may not work 100% yet (see gazelle22_highlightRegionOutline) boolean isGradientPoint(int x, int y) { if (op == null) return false; IntMinMax posterizedMinMax = new IntMinMax(); IntMinMax unposterizedMinMax = new IntMinMax(); // need to grab point and all 8 neighbors to cover all cases for (int y2 = max(0, y-1); y2 <= min(h-1, y+1); y2++) for (int x2 = max(0, x-1); x2 <= min(w-1, x+1); x2++) { posterizedMinMax.add(posterized.getInt(x2, y2)); unposterizedMinMax.add(unposterized.getInt(x2, y2)); } int posterizedContrast = posterizedMinMax.rangeLength(); if (posterizedContrast > singleStep) return false; if (posterizedContrast == 0) return true; int unposterizedContrast = unposterizedMinMax.rangeLength(); return unposterizedContrast <= posterizedContrast*gradientThreshold; } void checkPair(int x, int y, int x2, int y2) { int posterizedContrast = absDiff(posterized.getInt(x, y), posterized.getInt(x2, y2)); if (posterizedContrast == 0) return; if (posterizedContrast > singleStep) return; int realContrast = absDiff(unposterized.getInt(x, y), unposterized.getInt(x2, y2)); double ratio = doubleRatio(realContrast, singleStep); float result = (float) ((.5-ratio)*2); /*if (ratio < .5)*/ { out.setPixel(x*2+1-(y2-y), y*2+1, result); out.setPixel(x*2+1, y*2+1-(x2-x), result); } } } // S&P, then regions static class Gazelle22_ImageToRegions { BufferedImage inputImage; BWIntegralImage ii; SnPSettings snpSettings; BWImage posterized; FastRegions_BWImage regions; Map, Double> scoredRegions; boolean wasRun = false; final public Gazelle22_ImageToRegions setWithDiagonals(boolean withDiagonals){ return withDiagonals(withDiagonals); } public Gazelle22_ImageToRegions 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? FunctionTimings timings; Gazelle22_ImageToRegions(FunctionTimings timings, BufferedImage inputImage, SnPSettings snpSettings) { this.snpSettings = snpSettings; this.inputImage = inputImage; this.timings = timings;} Decolorizer decolorizer() { return snpSettings.decolorizer; } public void run() { try { wasRun = true; var decolorizer = decolorizer(); timings.dO(decolorizer != null ? "bwIntegralImage w/decolorizer" : "bwIntegralImage", () -> ii = bwIntegralImage_withMeta(inputImage, decolorizer)); timings.dO("scaleAndPosterize", () -> posterized = scaleAndPosterize(ii, snpSettings)); regions = new FastRegions_BWImage(posterized); regions.collectBounds(); regions.withDiagonals(withDiagonals); timings.dO("Regions", regions); } catch (Exception __e) { throw rethrow(__e); } } // information about original screenshot if available ScreenShotMeta screenShotMeta() { return optCast(ScreenShotMeta.class, getMetaSrc(inputImage)); } Pt coordinatesFromScreen(Pt p) { var meta = screenShotMeta(); if (meta == null) return null; var r = meta.bounds; if (!r.contains(p)) return null; p = translatePt(p, -r.x, -r.y); return scalePt(p, doubleRatio(snpSettings.pixelRows, r.h)); } Pt coordinatesToScreen(Pt p) { var meta = screenShotMeta(); if (meta == null) return null; var r = meta.bounds; return translatePt(r.x, r.y, scalePt(p, doubleRatio(r.h, snpSettings.pixelRows))); } final FastRegions_BWImage get(){ return regions(); } FastRegions_BWImage regions() { if (!wasRun) run(); return regions; } int nRegions() { return regions().nRegions(); } } // both the outer outline and the outline of a hole are called a "trace" // should return outer outline first and then outline of holes // seems to return first point again at the end sometimes static class RegionBorder_innerPoints extends AbstractSteppable implements IFieldsToList{ FastRegions_BWImage regions; int iRegion; RegionBorder_innerPoints() {} RegionBorder_innerPoints(FastRegions_BWImage regions, int iRegion) { this.iRegion = iRegion; this.regions = regions;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + regions + ", " + iRegion + ")"; }public Object[] _fieldsToList() { return new Object[] {regions, iRegion}; } transient Set> onNewTrace; public RegionBorder_innerPoints onNewTrace(IVF1 f) { onNewTrace = createOrAddToSyncLinkedHashSet(onNewTrace, f); return this; } public RegionBorder_innerPoints removeNewTraceListener(IVF1 f) { main.remove(onNewTrace, f); return this; } public void newTrace(boolean isHole) { if (onNewTrace != null) for (var listener : onNewTrace) pcallF_typed(listener, isHole); } transient Set> onFoundPoint; public RegionBorder_innerPoints onFoundPoint(IVF1 f) { onFoundPoint = createOrAddToSyncLinkedHashSet(onFoundPoint, f); return this; } public RegionBorder_innerPoints removeFoundPointListener(IVF1 f) { main.remove(onFoundPoint, f); return this; } public void foundPoint(Pt p) { if (onFoundPoint != null) for (var listener : onFoundPoint) pcallF_typed(listener, p); } transient Set onTraceDone; public RegionBorder_innerPoints onTraceDone(Runnable r) { onTraceDone = createOrAddToSyncLinkedHashSet(onTraceDone, r); return this; } public RegionBorder_innerPoints removeTraceDoneListener(Runnable r) { main.remove(onTraceDone, r); return this; } public void traceDone() { if (onTraceDone != null) for (var listener : onTraceDone) pcallF_typed(listener); } int w, x, y, dir; FastRegions_BWImage.RegionIterator it; byte[] reachedInDirections; // bits 0 to 3 boolean tracing = false; final public boolean getTracingHole(){ return tracingHole(); } public boolean tracingHole() { return tracingHole; } boolean tracingHole = false; // directions (0 = right, 1 = down, 2 = left, 3 = up) static Pt[] directions = { pt(1, 0), pt(0, 1), pt(-1, 0), pt(0, -1) // r, d, l, u }; void init() { w = regions.w; reachedInDirections = new byte[w*regions.h]; it = regions.regionIterator(iRegion); } public boolean step() { if (reachedInDirections == null) init(); if (tracing) { int pos = y*w+x; if ((reachedInDirections[pos] & (1 << dir)) != 0) { traceDone(); tracing = false; return true; } reachedInDirections[pos] |= (byte) 1 << dir; foundPoint(x, y); for (int turn = 3; turn <= 6; turn++) { // try left, straight, right, back int newDir = (dir+turn) & 3; Pt d = directions[newDir]; int x2 = x+d.x, y2 = y+d.y; boolean b = regions.inRegion(iRegion, x2, y2); if (b) { x = x2; y = y2; dir = newDir; return true; } } return true; // i think this ends the trace in next iteration } else { // search for first border pixel if (!it.next()) return false; // done x = it.x(); y = it.y(); if (reachedInDirections[y*w+x] != 0) return true; // seen pixel before // if pixel above is empty, walk to the right if (!regions.inRegion(iRegion, x, y-1)) { startTrace(0); return true; } // if pixel on the left is empty, walk upwards if (!regions.inRegion(iRegion, x-1, y)) { startTrace(3); return true; } // if pixel on the right is empty, walk downwards if (!regions.inRegion(iRegion, x+1, y)) { startTrace(1); return true; } // if pixel below is empty, walk left if (!regions.inRegion(iRegion, x, y+1)) { startTrace(2); return true; } // not a border pixel, continue search return true; } } void startTrace(int dir) { this.dir = dir; // mark point reached from all directions in next step reachedInDirections[y*w+x] = (byte) (15 & ~(1 << dir)); tracing = true; newTrace(tracingHole); tracingHole = true; } void foundPoint(int x, int y) { foundPoint(pt(x, y)); } // get all points as list List allPoints_cache; List allPoints() { if (allPoints_cache == null) allPoints_cache = allPoints_load(); return allPoints_cache;} List allPoints_load() { PtBuffer l = new PtBuffer(); onFoundPoint(p -> l.add(p)); run(); return l; } // get outline as OnePath OnePath onePath_cache; OnePath onePath() { if (onePath_cache == null) onePath_cache = onePath_load(); return onePath_cache;} OnePath onePath_load() { return new OnePath(allPoints(), true); } // for debugging void runAndPrint() { onNewTrace(hole -> print(!hole ? "new outline" : "new hole")); onTraceDone(() -> print("traceDone")); onFoundPoint(p -> print("foundPoint " + p)); stepMaxWithStats(this, 10000); } } static class Fail extends RuntimeException implements IFieldsToList{ Object[] objects; Fail() {} Fail(Object... objects) { this.objects = objects;}public Object[] _fieldsToList() { return new Object[] {objects}; } Fail(Throwable cause, Object... objects) { super(cause); this.objects = objects; } public String toString() { return joinNemptiesWithColon("Fail", getMessage()); } public String getMessage() { return commaCombine(getCause(), objects); } } // function type is A (e.g. String) static class FunctionTimings { Map data = syncMap(); final void timeCtex(A function, RunnableWithExceptions r){ doCtex(function, r); } void doCtex(A function, RunnableWithExceptions r) { try { if (r == null) return; long time = nanoTime(); try { r.run(); } finally { time = nanoTime()-time; saveTiming(function, time); } } catch (Exception __e) { throw rethrow(__e); } } B get(A function, IF0 f) { if (f == null) return null; Var var = new Var(); time(function, () -> var.set(f.get())); return var.get(); } final void time(A function, Runnable r){ dO(function, r); } void dO(A function, Runnable r) { if (r == null) return; long time = nanoTime(); try { r.run(); } finally { time = nanoTime()-time; saveTiming(function, time); } } void saveTiming(A function, long time) { Average avg = syncMapGetOrCreate(data, function, () -> new Average()); avg.add(time); } Map get() { return cloneMap(data); } final void clear(){ reset(); } void reset() { data.clear(); } // as multi-line string, sorted alphabetically final public String toString(){ return render(); } public String render() { return lines(renderedEntries()); } List renderedEntries() { return ciSorted(map(get(), (f, avg) -> functionToString(f) + ": " + n2(iround(nsToMicroseconds(avg.get()))) + " " + microSymbol() + "s (" + n2(iround(avg.n())) + ")")); } String toStringSingleLine() { return joinWithComma(renderedEntries()); } String functionToString(A f) { return firstToUpper(str(f)); } } 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 IImageRegion extends INumberOfPixels { // 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); // convenience methods public default boolean contains(Pt p) { return contains(p.x, p.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 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); } } // SnP = Scale and Posterize // pixelRows = -1 means use original image's height static class SnPSettings extends MetaWithChangeListeners implements IFieldsToList{ static final String _fieldOrder = "pixelRows colors decolorizer"; int pixelRows; int colors; SnPSettings(int pixelRows, int colors) { this.colors = colors; this.pixelRows = pixelRows;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + pixelRows + ", " + colors + ")"; } public boolean equals(Object o) { if (!(o instanceof SnPSettings)) return false; SnPSettings __1 = (SnPSettings) o; return pixelRows == __1.pixelRows && colors == __1.colors; } public int hashCode() { int h = -327917448; h = boostHashCombine(h, _hashCode(pixelRows)); h = boostHashCombine(h, _hashCode(colors)); return h; } public Object[] _fieldsToList() { return new Object[] {pixelRows, colors}; } SnPSettings() { pixelRows = 128; colors = 8; } // use full image resolution (no downsampling) SnPSettings(BufferedImage image, int colors) { pixelRows = image.getHeight(); this.colors = colors; } SnPSettings cloneMe() { return shallowClone(this); } // optional - null for simple decolorization public transient FieldVar varDecolorizer_cache; public FieldVar varDecolorizer() { if (varDecolorizer_cache == null) varDecolorizer_cache = varDecolorizer_load(); return varDecolorizer_cache;} public FieldVar varDecolorizer_load() { return new FieldVar(this, "decolorizer", () -> decolorizer(), decolorizer -> decolorizer(decolorizer)); } final public SnPSettings setDecolorizer(Decolorizer decolorizer){ return decolorizer(decolorizer); } public SnPSettings decolorizer(Decolorizer decolorizer) { if (!eq(this.decolorizer, decolorizer)) { this.decolorizer = decolorizer; change(); } return this; } final public Decolorizer getDecolorizer(){ return decolorizer(); } public Decolorizer decolorizer() { return decolorizer; } Decolorizer decolorizer; int pixelRowsForImage(WidthAndHeight img) { return pixelRows < 0 ? img.getHeight() : pixelRows; } static SnPSettings allColors(int pixelRows) { return new SnPSettings(pixelRows, 255); } } static class ScreenShotMeta implements IFieldsToList{ TimestampRange takenWhen; Rect bounds; BufferedImage image; ScreenShotMeta() {} ScreenShotMeta(TimestampRange takenWhen, Rect bounds, BufferedImage image) { this.image = image; this.bounds = bounds; this.takenWhen = takenWhen;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + takenWhen + ", " + bounds + ", " + image + ")"; }public Object[] _fieldsToList() { return new Object[] {takenWhen, bounds, image}; } } 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; } } // grayscale, actually static class BWIntegralImage extends Meta implements IBWIntegralImage, IIntegralImage { int w, h; int[] data; // 1 entry per pixel // constructors BWIntegralImage() {} BWIntegralImage(File f) { this(loadImage2(f)); } BWIntegralImage(MakesBufferedImage img) { this(toBufferedImage(img)); } BWIntegralImage(BufferedImage image) { grab(image); } BWIntegralImage(BufferedImage image, Decolorizer decolorizer) { grab(image, decolorizer); } BWIntegralImage(GrabbableIntPixels gp) { grab(gp); } BWIntegralImage(GrabbableRGBBytePixels gp) { grab(gp, new Decolorizer.Simple()); } BWIntegralImage(GrabbableGrayPixels gp) { grab(gp); } BWIntegralImage(BWImage img) { grab(new GrabbableGrayPixels(img.pixels, img.width, img.height, 0, img.width)); } // grab functions void grab(BufferedImage image) { grab(image, null); } void grab(BufferedImage image, Decolorizer decolorizer) { if (image.getType() == BufferedImage.TYPE_BYTE_GRAY) grab(grabbableGrayPixels(image)); else { // rgb image var gp = grabbableRGBBytePixels(image); if (gp != null) grab(gp, decolorizer); else grab(grabbableIntPixels_fastOrSlow(image), decolorizer); } } void grab(GrabbableRGBBytePixels gp, Decolorizer decolorizer) { if (decolorizer == null) decolorizer = new Decolorizer.Simple(); alloc(gp.w, gp.h); int w = this.w, h = this.h; // first row int iImage = gp.offset, sum = 0, pixelStride = gp.pixelStride; byte[] image = gp.data; for (int x = 0; x < w; x++) { int packed = rgbInt(image[iImage], image[iImage+1], image[iImage+2]); iImage += pixelStride; int brightness = decolorizer.toGrayScale(packed); data[x] = (sum += brightness); } // subsequent rows var ping = pingSource(); int scanlineExtra = gp.scanlineStride-w*pixelStride; iImage = gp.offset+gp.scanlineStride; int i = w; for (int y = 1; y < h; y++) { sum = 0; for (int x = 0; x < w; x++) { int packed = rgbInt(image[iImage], image[iImage+1], image[iImage+2]); iImage += pixelStride; int brightness = decolorizer.toGrayScale(packed); sum += brightness; data[i] = sum + data[i-w]; i++; } iImage += scanlineExtra; { if (ping != null) ping.get(); } } // for y } void grab(GrabbableIntPixels gp, Decolorizer decolorizer) { if (isDefaultDecolorizer(decolorizer)) { grab(gp); return; } alloc(gp.w, gp.h); int w = this.w, h = this.h; int offset = gp.offset, sum = 0; int[] image = gp.data; for (int x = 0; x < w; x++) { int packed = image[offset+x]; int brightness = decolorizer.toGrayScale(packed); data[x] = (sum += brightness); } var ping = pingSource(); int scanlineExtra = gp.scanlineStride-w; int iImage = offset+gp.scanlineStride, i = w; for (int y = 1; y < h; y++) { sum = 0; for (int x = 0; x < w; x++) { int packed = image[iImage]; int brightness = decolorizer.toGrayScale(packed); sum += brightness; data[i] = sum + data[i-w]; iImage++; i++; } iImage += scanlineExtra; { if (ping != null) ping.get(); } } // for y } void grab(GrabbableIntPixels gp) { alloc(gp.w, gp.h); int w = this.w, h = this.h; int offset = gp.offset, sum = 0; int[] image = gp.data; for (int x = 0; x < w; x++) { int packed = image[offset+x]; int brightness = packedToBrightness(packed); data[x] = (sum += brightness); } var ping = pingSource(); int scanlineExtra = gp.scanlineStride-w; int iImage = offset+gp.scanlineStride, i = w; for (int y = 1; y < h; y++) { sum = 0; for (int x = 0; x < w; x++) { int packed = image[iImage]; int brightness = packedToBrightness(packed); sum += brightness; data[i] = sum + data[i-w]; iImage++; i++; } iImage += scanlineExtra; { if (ping != null) ping.get(); } } // for y } void grab(GrabbableGrayPixels gp) { alloc(gp.w, gp.h); int offset = gp.offset, sum = 0; byte[] image = gp.data; for (int x = 0; x < w; x++) { int brightness = image[offset+x]; data[x] = (sum += brightness); } var ping = pingSource(); int scanlineExtra = gp.scanlineStride-w; int iImage = offset+gp.scanlineStride, i = w; for (int y = 1; y < h; y++) { sum = 0; for (int x = 0; x < w; x++) { int brightness = image[iImage]; sum += brightness; data[i] = sum + data[i-w]; iImage++; i++; } iImage += scanlineExtra; { if (ping != null) ping.get(); } } // for y } private void alloc(int w, int h) { this.w = w; this.h = h; if (w*h > 8*1024*1024) throw fail("Image too large (more than 8 MP): " + w + "*" + h); data = new int[w*h]; } // pixels outside of image are considered black int get(int x, int y) { return x < 0 || y < 0 ? 0 : data[min(y, h-1)*w+min(x, w-1)]; } // precise version [TO TEST!] public double getIIValue(double x, double y) { int xFloor = ifloor(x), yFloor = ifloor(y); double val = getIIValue(xFloor, yFloor); // at integer coordinate? if (xFloor == x && yFloor == y) return val; // at non-integer coordinate, perform subpixel calculation double val2 = getIIValue(xFloor+1, yFloor); double val3 = getIIValue(xFloor , yFloor+1); double val4 = getIIValue(xFloor+1, yFloor+1); return blend2D(val, val2, val3, val4, x-xFloor, y-yFloor); } public int getIIValue(int x, int y) { return get(x, y); } public double getIntegralValue(int x, int y, int channel) { return get(x, y); } public double getPixelAverage(int x1, int y1, int x2, int y2) { int area = (x2-x1)*(y2-y1); return doubleRatio(bwIntegralImage_sumRect(this, x1, y1, x2, y2), area); } public int getWidth() { return w; } public int getHeight() { return h; } int packedToBrightness(int packed) { int b = (packed & 0xFF); int r = ((packed >> 16) & 0xFF); int g = ((packed >> 8) & 0xFF); return (r+g+b+1)/3; } // get brightness of pixel public int getInt(int x, int y) { return iround(rectSum(x, y, x+1, y+1, 0)); } // returns RGB pixel without alpha public int getPixel(int x, int y) { return rgbIntFromGrayscale(getInt(x, y)); } public BufferedImage getBufferedImage() { //ret scaleDownUsingIntegralImageBW(this, w).getBufferedImage(); Object src = metaGet("src", this); if (src instanceof BufferedImage) return ((BufferedImage) src); return grayImageFromIBWIntegralImage(this); } } abstract static class AbstractSteppable implements Steppable { public void run() { try { stepAll(this); } catch (Exception __e) { throw rethrow(__e); } } } static interface IFieldsToList { Object[] _fieldsToList(); } static interface Decolorizer { int toGrayScale(int rgb); static class Red implements Decolorizer , IFieldsToList{ Red() {} public String toString() { return shortClassName_dropNumberPrefix(this); } public boolean equals(Object o) { return o instanceof Red; } public int hashCode() { int h = 82033; return h; } public Object[] _fieldsToList() { return null; } public int toGrayScale(int rgb) { return (rgb >> 16) & 0xFF; } } static class Green implements Decolorizer , IFieldsToList{ Green() {} public String toString() { return shortClassName_dropNumberPrefix(this); } public boolean equals(Object o) { return o instanceof Green; } public int hashCode() { int h = 69066467; return h; } public Object[] _fieldsToList() { return null; } public int toGrayScale(int rgb) { return (rgb >> 8) & 0xFF; } } static class Blue implements Decolorizer , IFieldsToList{ Blue() {} public String toString() { return shortClassName_dropNumberPrefix(this); } public boolean equals(Object o) { return o instanceof Blue; } public int hashCode() { int h = 2073722; return h; } public Object[] _fieldsToList() { return null; } public int toGrayScale(int rgb) { return rgb & 0xFF; } } static class Alpha implements Decolorizer , IFieldsToList{ Alpha() {} public String toString() { return shortClassName_dropNumberPrefix(this); } public boolean equals(Object o) { return o instanceof Alpha; } public int hashCode() { int h = 63357246; return h; } public Object[] _fieldsToList() { return null; } public int toGrayScale(int rgb) { return (rgb >> 24) & 0xFF; } } // just the average of the 3 channels static class Simple implements Decolorizer , IFieldsToList{ Simple() {} public String toString() { return shortClassName_dropNumberPrefix(this); } public boolean equals(Object o) { return o instanceof Simple; } public int hashCode() { int h = -1818419758; return h; } public Object[] _fieldsToList() { return null; } public int toGrayScale(int rgb) { int r = (rgb >> 16) & 0xFF; int g = (rgb >> 8) & 0xFF; int b = rgb & 0xFF; return (r+g+b+1)/3; } } } static class PtBuffer extends RandomAccessAbstractList < Pt > { LongBuffer buf = new LongBuffer(); PtBuffer() {} PtBuffer(int size) { buf.allocate(size); } PtBuffer(Iterable l) { if (l != null) for (Pt p : l) add(p); } public int size() { return buf.size(); } public Pt get(int i) { return longToPt(buf.get(i)); } public long getLong(int i) { return buf.get(i); } public Pt set(int i, Pt val) { Pt old = get(i); buf.set(i, ptToLong(val)); return old; } public boolean add(Pt p) { buf.add(ptToLong(p)); return true; } public void add(int x, int y) { buf.add(ptToLong(x, y)); } public Pt remove(int i) { Pt p = get(i); buf.remove(i); return p; } public void clear() { buf.clear(); } } static class IntMinMax { int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE; void add(int i) { if (i < min) min = i; if (i > max) max = i; } int rangeLength() { return max-min; } } static interface IVF1 { void get(A a); } static class PosterizeBWImage implements PixelPreservingImageOpResult , IFieldsToList{ int brightnessLevels; BWImage img; PosterizeBWImage() {} PosterizeBWImage(int brightnessLevels, BWImage img) { this.img = img; this.brightnessLevels = brightnessLevels;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + brightnessLevels + ", " + img + ")"; }public Object[] _fieldsToList() { return new Object[] {brightnessLevels, img}; } BWImage result; public void run() { try { if (brightnessLevels >= 256 || img == null) { result = img; return; } byte[] pixels = img.getBytes(); int w = img.getWidth(), h = img.getHeight(), n = pixels.length; byte[] pixels2 = new byte[n]; var posterizer = cachePosterizer(new SinglePixelPosterizer(brightnessLevels)); for (int i = 0; i < n; i++) pixels2[i] = (byte) posterizer.get(ubyteToInt(pixels[i])); result = new BWImage(w, h, pixels2); } catch (Exception __e) { throw rethrow(__e); } } public BWImage pixelPreservingSrcImage() { return img; } BWImage get() { if (result == null) run(); return result; } } 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()); } } // 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 FastRegions_BWImage extends AbstractFastRegions { int getColor(int pos) { return image.getInt(x(pos), y(pos)); } RGB rgbForRegion(ImageRegion r) { return rgbFromGrayscale(r.brightness()); } FastRegions_BWImage(BWImage img) { super(img); } FastRegions_BWImage(BufferedImage img) { this(toBWImage(img)); } } static class Var implements IVar, ISetter { Var() {} Var(A v) { this.v = v;} A v; // you can access this directly if you use one thread public synchronized void set(A a) { if (v != a) { v = a; notifyAll(); } } public synchronized A get() { return v; } public synchronized boolean has() { return v != null; } public void clear() { set(null); } public synchronized A getAndSet(A a) { var value = v; set(a); return value; } public IF0 getter() { return () -> get(); } public IVF1 setter() { return __26 -> set(__26); } public String toString() { return str(this.get()); } } static class SinglePixelPosterizer implements IPosterizer { int brightnessLevels; double factor1, factor2; SinglePixelPosterizer(int brightnessLevels) { this.brightnessLevels = brightnessLevels; factor1 = doubleRatio(brightnessLevels, 256); factor2 = doubleRatio(255, brightnessLevels-1); } // 0..255 to 0..255 public int get(int brightness) { return iround(ifloor(brightness*factor1)*factor2); } } 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 class FieldVar extends VarWithNotify { IHasChangeListeners containingObject; String fieldName; IF0 getter; IVF1 setter; FieldVar(IHasChangeListeners containingObject, String fieldName, IF0 getter, IVF1 setter) { this.setter = setter; this.getter = getter; this.fieldName = fieldName; this.containingObject = containingObject; containingObject.onChangeAndNow(() -> _updateFromObject()); } void _updateFromObject() { set(getter.get()); } public void fireChange() { setter.get(get()); super.fireChange(); } public FieldVar onChange(IVF1 r) { if (r != null) onChange(() -> r.get(get())); return this; } } static interface Steppable { public boolean step(); // return false if done } static interface IIntegralImage extends MakesBufferedImage { public int getWidth(); public int getHeight(); default public Pt getSize() { return pt(getWidth(), getHeight()); } default public int defaultChannel() { return 0; } default public int nChannels() { return 3; } // get value for 1 channel // normal range [0; pixelCount*256) public double getIntegralValue(int x, int y, int channel); default double getIntegralValue(double x, double y, int channel) { return getIntegralValue(ifloor(x), ifloor(y), channel); } // gets summed value of the 3 channels // normal range [0; pixelCount*256*3) default double getIntegralValue(int x, int y) { return getIntegralValue(x, y, 0) + getIntegralValue(x, y, 1) + getIntegralValue(x, y, 2); } default double rectSum(int x1, int y1, int x2, int y2, int channel) { double bottomLeft = getIntegralValue(x1-1, y2-1, channel); double bottomRight = getIntegralValue(x2-1, y2-1, channel); double topLeft = getIntegralValue(x1-1, y1-1, channel); double topRight = getIntegralValue(x2-1, y1-1, channel); return bottomRight+topLeft-topRight-bottomLeft; } default double rectSum(double x1, double y1, double x2, double y2, int channel) { double bottomLeft = getIntegralValue(x1-1, y2-1, channel); double bottomRight = getIntegralValue(x2-1, y2-1, channel); double topLeft = getIntegralValue(x1-1, y1-1, channel); double topRight = getIntegralValue(x2-1, y1-1, channel); return bottomRight+topLeft-topRight-bottomLeft; } default double rectSum(int x1, int y1, int x2, int y2) { double bottomLeft = getIntegralValue(x1-1, y2-1); double bottomRight = getIntegralValue(x2-1, y2-1); double topLeft = getIntegralValue(x1-1, y1-1); double topRight = getIntegralValue(x2-1, y1-1); return bottomRight+topLeft-topRight-bottomLeft; } default double rectAverage(int x1, int y1, int x2, int y2, int channel) { return doubleRatio(rectSum(x1, y1, x2, y2, channel), areaFromPoints(x1, y1, x2, y2)); } default double rectAverage(Rect r, int channel) { return doubleRatio(rectSum(r, channel), rectArea(r)); } default double rectSum(Rect r) { return rectSum(r.x, r.y, r.x2(), r.y2()); } default double rectSum(Rect r, int channel) { return rectSum(r.x, r.y, r.x2(), r.y2(), channel); } default double pixelSum(DoubleRect r) { return rectSum(toRect_floor(r), defaultChannel()); } default IIntegralImage clip(int x, int y, int w, int h) { return new IIVirtualClip(this, x, y, w, h); } default double averageBrightness() { int w = getWidth(), h = getHeight(); return doubleRatio(getIntegralValue(w-1, h-1), w*h*3*255.0); } default RGB averageRGB() { int w = getWidth(), h = getHeight(); double factor = 1/(255.0*(w*h)); return new RGB( rectSum(0, 0, w, h, 0)*factor, rectSum(0, 0, w, h, 1)*factor, rectSum(0, 0, w, h, 2)*factor); } // normal range: (0, 255) default double getPixel(int x, int y, int channel) { return rectSum(x, y, x+1, y+1, channel); } // returns RGB pixel without alpha default int getPixel(int x, int y) { int r = iround(rectSum(x, y, x+1, y+1, 0)); int g = iround(rectSum(x, y, x+1, y+1, 1)); int b = iround(rectSum(x, y, x+1, y+1, 2)); return rgbInt(r, g, b); } default int nPixels() { return getWidth()*getHeight(); } default BufferedImage getBufferedImage() { int w = getWidth(), h = getHeight(); int[] pixels = new int[w*h]; int i = 0; for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) pixels[i++] = getPixel(x, y) | fullAlphaMask(); return intArrayToBufferedImage(pixels, w, h); } // minimum and maximum brightness possible in image // Better not to use because without this information // you have a more general recognition algorithm. //default DoubleRange colorRange(int channel) { ret doubleRange(0, 256); } } interface RunnableWithExceptions { public void run() throws Exception; } static class LongBuffer implements Iterable, ILongQueue { long[] data; int size; LongBuffer() {} LongBuffer(int size) { if (size != 0) data = new long[size]; } LongBuffer(Iterable l) { if (l instanceof Collection) allocate(((Collection) l).size()); addAll(l); } public void add(long i) { if (size >= lLongArray(data)) { data = resizeLongArray(data, Math.max(1, toInt(Math.min(maximumSafeArraySize(), lLongArray(data)*2L)))); if (size >= data.length) throw fail("LongBuffer too large: " + size); } data[size++] = i; } void allocate(int n) { data = resizeLongArray(data, max(n, size())); } void addAll(Iterable l) { if (l != null) for (long i : l) add(i); } long[] toArray() { return size == 0 ? null : resizeLongArray(data, size); } List toList() { return longArrayToList(data, 0, size); } List asVirtualList() { return listFromFunction(__32 -> get(__32), size); } void reset() { size = 0; } void clear() { reset(); } int size() { return size; } public boolean isEmpty() { return size == 0; } long get(int idx) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); return data[idx]; } void set(int idx, long value) { if (idx >= size) throw fail("Index out of range: " + idx + "/" + size); data[idx] = value; } long popLast() { if (size == 0) throw fail("empty buffer"); return data[--size]; } long last() { return data[size-1]; } long 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 Long next() { if (!hasNext()) throw fail("Index out of bounds: " + i); return data[i++]; } }; } void trimToSize() { data = resizeLongArray(data, size); } void remove(int idx) { arraycopy(data, idx+1, data, idx, size-1-idx); --size; } // don't rely on return value if buffer is empty public long poll() { return size == 0 ? -1 : data[--size]; } } 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); } } static class GrabbableGrayPixels implements IFieldsToList{ static final String _fieldOrder = "data w h offset scanlineStride"; byte[] data; int w; int h; int offset; int scanlineStride; GrabbableGrayPixels() {} GrabbableGrayPixels(byte[] data, int w, int h, int offset, int scanlineStride) { this.scanlineStride = scanlineStride; this.offset = offset; this.h = h; this.w = w; this.data = data;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + data + ", " + w + ", " + h + ", " + offset + ", " + scanlineStride + ")"; } public boolean equals(Object o) { if (!(o instanceof GrabbableGrayPixels)) return false; GrabbableGrayPixels __1 = (GrabbableGrayPixels) o; return eq(data, __1.data) && w == __1.w && h == __1.h && offset == __1.offset && scanlineStride == __1.scanlineStride; } public int hashCode() { int h = 1984426208; h = boostHashCombine(h, _hashCode(data)); h = boostHashCombine(h, _hashCode(w)); h = boostHashCombine(h, _hashCode(h)); h = boostHashCombine(h, _hashCode(offset)); h = boostHashCombine(h, _hashCode(scanlineStride)); return h; } public Object[] _fieldsToList() { return new Object[] {data, w, h, offset, scanlineStride}; } } static class Average { final public double getSum(){ return sum(); } public double sum() { return sum; } double sum; final public double getN(){ return n(); } public double n() { return n; } double n; void add(double d) { ++n; sum += d; } void add(double d, double weight) { n += weight; sum += d*weight; } final double avg(){ return get(); } final double average(){ return get(); } final double getAverage(){ return get(); } double get() { return doubleRatio(sum, n); } boolean isEmpty() { return n == 0; } public String toString() { return get() + " (n=" + n + ")"; } void clear() { n = 0; sum = 0; } } static class GrabbableRGBBytePixels implements IFieldsToList{ static final String _fieldOrder = "data w h offset scanlineStride pixelStride"; byte[] data; int w; int h; int offset; int scanlineStride; int pixelStride; GrabbableRGBBytePixels() {} GrabbableRGBBytePixels(byte[] data, int w, int h, int offset, int scanlineStride, int pixelStride) { this.pixelStride = pixelStride; this.scanlineStride = scanlineStride; this.offset = offset; this.h = h; this.w = w; this.data = data;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + data + ", " + w + ", " + h + ", " + offset + ", " + scanlineStride + ", " + pixelStride + ")"; } public boolean equals(Object o) { if (!(o instanceof GrabbableRGBBytePixels)) return false; GrabbableRGBBytePixels __1 = (GrabbableRGBBytePixels) o; return eq(data, __1.data) && w == __1.w && h == __1.h && offset == __1.offset && scanlineStride == __1.scanlineStride && pixelStride == __1.pixelStride; } public int hashCode() { int h = 330924690; h = boostHashCombine(h, _hashCode(data)); h = boostHashCombine(h, _hashCode(w)); h = boostHashCombine(h, _hashCode(h)); h = boostHashCombine(h, _hashCode(offset)); h = boostHashCombine(h, _hashCode(scanlineStride)); h = boostHashCombine(h, _hashCode(pixelStride)); return h; } public Object[] _fieldsToList() { return new Object[] {data, w, h, offset, scanlineStride, pixelStride}; } } // you still need to implement hasNext() and next() static abstract class IterableIterator implements Iterator, Iterable { public Iterator iterator() { return this; } public void remove() { unsupportedOperation(); } } abstract static class RandomAccessAbstractList extends AbstractList implements RandomAccess { } 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 class TimestampRange implements IFieldsToList{ static final String _fieldOrder = "start end"; Timestamp start; Timestamp end; TimestampRange() {} TimestampRange(Timestamp start, Timestamp end) { this.end = end; this.start = start;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + start + ", " + end + ")"; } public boolean equals(Object o) { if (!(o instanceof TimestampRange)) return false; TimestampRange __1 = (TimestampRange) o; return eq(start, __1.start) && eq(end, __1.end); } public int hashCode() { int h = -2020922905; h = boostHashCombine(h, _hashCode(start)); h = boostHashCombine(h, _hashCode(end)); return h; } public Object[] _fieldsToList() { return new Object[] {start, end}; } TimestampRange(long start, long end) { this(new Timestamp(start), new Timestamp(end)); } Timestamp start() { return start; } Timestamp end() { return end; } final TimestampRange startTime(Timestamp start){ return start(start); } TimestampRange start(Timestamp start) { this.start = start; return this; } final TimestampRange endTime(Timestamp end){ return end(end); } TimestampRange end(Timestamp end) { this.end = end; return this; } boolean complete() { return start != null && end != null; } Duration duration() { return !complete() ? null : end.minusAsDuration(start); } } 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 MakesBufferedImage extends WidthAndHeight { BufferedImage getBufferedImage(); public default void drawAt(Graphics2D g, int x, int y) { g.drawImage(getBufferedImage(), x, y, null); } } static class GrabbableIntPixels implements IFieldsToList{ static final String _fieldOrder = "data w h offset scanlineStride"; int[] data; int w; int h; int offset; int scanlineStride; GrabbableIntPixels() {} GrabbableIntPixels(int[] data, int w, int h, int offset, int scanlineStride) { this.scanlineStride = scanlineStride; this.offset = offset; this.h = h; this.w = w; this.data = data;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + data + ", " + w + ", " + h + ", " + offset + ", " + scanlineStride + ")"; } public boolean equals(Object o) { if (!(o instanceof GrabbableIntPixels)) return false; GrabbableIntPixels __1 = (GrabbableIntPixels) o; return eq(data, __1.data) && w == __1.w && h == __1.h && offset == __1.offset && scanlineStride == __1.scanlineStride; } public int hashCode() { int h = -1183022196; h = boostHashCombine(h, _hashCode(data)); h = boostHashCombine(h, _hashCode(w)); h = boostHashCombine(h, _hashCode(h)); h = boostHashCombine(h, _hashCode(offset)); h = boostHashCombine(h, _hashCode(scanlineStride)); return h; } public Object[] _fieldsToList() { return new Object[] {data, w, h, offset, scanlineStride}; } int[] asPackedArray() { if (offset == 0 && data.length == w*h) return data; int[] pixels = new int[w*h]; for (int y = 0; y < h; y++) arrayCopy(data, offset+y*scanlineStride, pixels, y*h, w); return pixels; } } static interface IBWIntegralImage extends MakesBufferedImage, IBWImage, IMeta { public int getWidth(); public int getHeight(); // implement one of the following two methods! // integer version (both coordinates & brightnesses are ints) public default int getIIValue(int x, int y) { return iround(getIIValue((double) x, (double) y)); } // floating point version (both coordinates & brightnesses are doubles) public default double getIIValue(double x, double y) { return getIIValue(ifloor(x), ifloor(y)); } // 0.0 to 255.0 default public double getPixelAverage(int x1, int y1, int x2, int y2) { int area = (x2-x1)*(y2-y1); return doubleRatio(getPixelSum(x1, y1, x2, y2), area); } default public double getPixelAverage(double x1, double y1, double x2, double y2) { double area = (x2-x1)*(y2-y1); return doubleRatio(getPixelSum(x1, y1, x2, y2), area); } default public double getPixelAverage(Rect r) { return getPixelAverage(r.x, r.y, r.x2(), r.y2()); } default public int getPixelSum(int x1, int y1, int x2, int y2) { return bwIntegralImage_sumRect(this, x1, y1, x2, y2); } default public double getPixelSum(double x1, double y1, double x2, double y2) { return bwIntegralImage_sumRect(this, x1, y1, x2, y2); } default public int getInt(int x, int y){ return getPixelBrightness(x, y); } default public int getPixelBrightness(int x, int y) { return getPixelSum(x, y, x+1, y+1); } default public int getPixelBrightness(Pt p) { return getPixelBrightness(p.x, p.y); } public default BufferedImage getBufferedImage() { //ret scaleDownUsingIntegralImageBW(this, w).getBufferedImage(); return grayImageFromIBWIntegralImage(this); } // implementing IBWImage public default float getFloatPixel(int x, int y) { return getPixelBrightness(x, y)/255f; } } public static interface IF0 { A get(); } static class MetaWithChangeListeners extends Meta implements IHasChangeListeners, ChangeTriggerable { transient Set onChange; public MetaWithChangeListeners onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; } public MetaWithChangeListeners removeChangeListener(Runnable r) { main.remove(onChange, r); return this; } public void change() { if (onChange != null) for (var listener : onChange) pcallF_typed(listener); } } interface PixelPreservingImageOpResult { Img pixelPreservingSrcImage(); } interface IDoublePt { public double x_double(); public double y_double(); } 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 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 interface IRGBImage extends MakesBufferedImage { int getIntPixel(int x, int y); } // 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, runner; final public int getSize(){ return size(); } public int size() { return size; } int size; // =w*h IntBuffer stack = new IntBuffer(); // locations as y*w+x 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; 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); public void run() { try { if (regionMatrix != null) 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); } while (runner < size) { if (regionMatrix[runner] == 0) { // make a new region, get color int region = ++regionCounter; { if (regionFirstPixel != null) regionFirstPixel.add(regionPixels != null ? l(regionPixels) : runner); } stack.add(runner); int color = getColor(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; // touching myself (or someone else) if (getColor(pos) != color) continue; // wrong color // new pixel found, mark as ours regionMatrix[pos] = region; ++rsize; { if (regionPixels != null) regionPixels.add(pos); } int x = x(pos), y = y(pos); if (x < x1) x1 = x; if (x > x2) x2 = x; if (y < y1) y1 = y; if (y > y2) y2 = y; // explore neighborhood if (x > 0) stack.add(pos-1); if (x < w-1) stack.add(pos+1); if (y > 0) stack.add(pos-w); if (y < h-1) stack.add(pos+w); if (withDiagonals) { if (y > 0) { if (x > 0) stack.add(pos-w-1); if (x < w-1) stack.add(pos-w+1); } if (y < h-1) { if (x > 0) stack.add(pos+w-1); if (x < w-1) stack.add(pos+w+1); } } } { 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); } } ++runner; } } catch (Exception __e) { throw rethrow(__e); } } IBWImage regionsImage() { return iBWImageFromFunction((x, y) -> { var region = regionMatrix[pos(x, y)]; return ((region-1)*regionStep) % (1.0+regionStep-0.0001); }, w, h); } final int nRegions(){ return regionCount(); } 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); } } // 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 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 IImageRegion get(int iRegion){ return getRegion(iRegion); } 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 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); } static interface ISetter { void set(A a); } 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 class Timestamp implements Comparable , IFieldsToList{ long date; Timestamp(long date) { this.date = date;} public boolean equals(Object o) { if (!(o instanceof Timestamp)) return false; Timestamp __1 = (Timestamp) o; return date == __1.date; } public int hashCode() { int h = 2059094262; h = boostHashCombine(h, _hashCode(date)); return h; } public Object[] _fieldsToList() { return new Object[] {date}; } Timestamp() { date = now(); } Timestamp(Date date) { if (date != null) this.date = date.getTime(); } long unixDate() { return date; } long unixSeconds() { return unixDate()/1000; } public String toString() { return formatLocalDateWithSeconds(date); } // Hmm. Should Timestamp(0) be equal to null? Question, questions... public int compareTo(Timestamp t) { return t == null ? 1 : cmp(date, t.date); } Timestamp plus(Seconds seconds) { return plus(seconds == null ? null : seconds.getDouble()); } final Timestamp plusSeconds(double seconds){ return plus(seconds); } Timestamp plus(double seconds) { return new Timestamp(date+toMS(seconds)); } // returns milliseconds long minus(Timestamp ts) { return unixDate()-ts.unixDate(); } long sysTime() { return clockTimeToSystemTime(date); } Duration minusAsDuration(Timestamp ts) { return Duration.ofMillis(minus(ts)); } } static interface ILongQueue { public boolean isEmpty(); public void add(long element); // return value is undefined if queue is empty public long poll(); } static class DoubleRect { double x, y, w, h; DoubleRect() {} DoubleRect(Rectangle r) { x = r.x; y = r.y; w = r.width; h = r.height; } DoubleRect(double x, double y, double w, double h) { this.h = h; this.w = w; this.y = y; this.x = x;} // Huh. not implementing equals()/hashCode? Stefan is mysterious boolean eq(Object o) { if (!(o instanceof DoubleRect)) return false; if (o == this) return true; DoubleRect r = (DoubleRect) o; return x == r.x && y == r.y && w == r.w && h == r.h; } public String toString() { return x + "," + y + " / " + w + "," + h; } double x1() { return x; } double y1() { return y; } double x2() { return x + w; } double y2() { return y + h; } boolean contains(Pt p) { return contains(p.x, p.y); } boolean contains(double _x, double _y) { return _x >= x && _y >= y && _x < x+w && _y < y+h; } boolean empty() { return w <= 0 || h <= 0; } } interface ChangeTriggerable { public void change(); } static class IIVirtualClip extends Meta implements IIntegralImage { RegisteredReference < IIntegralImage > fullImage = new RegisteredReference<>(this); int x1, y1, w, h; IIVirtualClip() {} IIVirtualClip(IIntegralImage fullImage, int x1, int y1, int w, int h) { this.fullImage.set(fullImage); this.h = h; this.w = w; this.y1 = y1; this.x1 = x1; } public int getWidth() { return w; } public int getHeight() { return h; } public double getIntegralValue(int x, int y, int channel) { return fullImage.get().getIntegralValue(x+x1, y+y1, channel); } public double getIntegralValue(int x, int y) { return fullImage.get().getIntegralValue(x+x1, y+y1); } public BufferedImage getBufferedImage() { return clipBufferedImage(fullImage.get().getBufferedImage(), x1, y1, w, h); } } interface IHasChangeListeners { IHasChangeListeners onChange(Runnable r); IHasChangeListeners removeChangeListener(Runnable r); default IHasChangeListeners onChangeAndNow(Runnable l) { onChange(l); callF(l); return this; } } interface IPosterizer extends IF1_IntToInt {} static class VarWithNotify extends Var implements IVarWithNotify { transient Set onChange; public VarWithNotify onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; } public VarWithNotify removeChangeListener(Runnable r) { main.remove(onChange, r); return this; } public void fireChange() { if (onChange != null) for (var listener : onChange) pcallF_typed(listener); } VarWithNotify() {} VarWithNotify(A a) { super(a); } // clever way (I hope) to do eq() outside of synchronization block public void set(A a) { A v = get(); if (eq(v, a)) { return; } synchronized(this) { this.v = a; notifyAll(); } fireChange(); } } static interface IVar extends IF0 { void set(A a); A get(); // reified type of value (if available) default Class getType() { return null; } default IF0 getter() { return () -> get(); } default IVF1 setter() { return __1 -> set(__1); } default boolean has() { return get() != null; } default void clear() { set(null); } } interface IVarWithNotify extends IVar, IF0WithChangeListeners { default IVarWithNotify onChange(IVF1 r) { if (r == null) return this; onChange(() -> r.get(get())); return this; } default IVarWithNotify onChangeAndNow(IVF1 r) { if (r == null) return this; onChangeAndNow(() -> r.get(get())); return this; } } interface IImageRegions { public List> regions(); } 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[] 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); } } static interface IAutoCloseableF0 extends IF0, AutoCloseable {} static class RegisteredReference implements IRef { Meta owner; // all we require is a meta field A value; RegisteredReference() { //if (!dynamicObjectIsLoading()) registerRef(); } /*void registerRef { vmBus_send registeringReference(this); }*/ // Note that the single-argument constructor takes an owner, not a value RegisteredReference(Meta owner) { this(owner, null); } RegisteredReference(Meta owner, A value) { this.value = value; this.owner = owner; index(); //registerRef(); } // get owning object (source) Meta owner() { return owner; } // get target public A get() { return value; } public boolean has() { return value != null; } public boolean set_trueIfChanged(A a) { if (eq(a, value)) return false; unindex(); value = a; index(); return true; } public void set(A a) { set_trueIfChanged(a); } void setIfEmpty(A a) { if (!has()) set(a); } public void set(IRef ref) { set(ref == null ? null : ref.get()); } public void clear() { set((A) null); } // can override boolean validRef() { return true; } // TODO: sync all the indexing and unindexing!? void index() { if (!validRef()) return; var br = lookupInterface(IHasBackRefs.class, get()); { if (br != null) br._registerBackRef(this); } } void unindex() { if (!validRef()) return; var br = lookupInterface(IHasBackRefs.class, get()); { if (br != null) br._unregisterBackRef(this); } } // not used yet void change() {} public String toString() { return str(value); } } static class Seconds implements Comparable , IFieldsToList{ double seconds; Seconds() {} Seconds(double seconds) { this.seconds = seconds;} public boolean equals(Object o) { if (!(o instanceof Seconds)) return false; Seconds __1 = (Seconds) o; return seconds == __1.seconds; } public int hashCode() { int h = -660217249; h = boostHashCombine(h, _hashCode(seconds)); return h; } public Object[] _fieldsToList() { return new Object[] {seconds}; } final double get(){ return seconds(); } final double getDouble(){ return seconds(); } double seconds() { return seconds; } public String toString() { return formatDouble(seconds, 3) + " s"; } public int compareTo(Seconds s) { return cmp(seconds, s.seconds); } Seconds div(double x) { return new Seconds(get()/x); } Seconds minus(Seconds x) { return new Seconds(get()-x.get()); } } static interface IF1_IntToInt { int get(int i); } 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) { unimplemented(); } } 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 IF0WithChangeListeners extends IF0, IHasChangeListeners {} static interface IHasBackRefs { public void _registerBackRef(IRef ref); public void _unregisterBackRef(IRef ref); } // could almost add IVar here - but void set() and bool set() are incompatible in some places static interface IRef extends IF0 { // called by the referencee to atomically replace itself // Passing oldValue to avoid race conditions public default void replaceValue(A oldValue, A newValue) {} } abstract static class IntegerIterator { abstract boolean hasNext(); abstract int next(); } static Object getMetaSrc(Object o) { return metaGet("src", o); } static void assertSameSize(BufferedImage a, BufferedImage b) { assertEquals( pair(a.getWidth(), a.getHeight()), pair(b.getWidth(), b.getHeight())); } static void assertSameSize(WidthAndHeight a, WidthAndHeight b) { assertEquals( pair(a.getWidth(), a.getHeight()), pair(b.getWidth(), b.getHeight())); } static int iceil(double d) { return (int) Math.ceil(d); } static double doubleRatio(double x, double y) { return y == 0 ? 0 : x/y; } static double doubleRatio(Seconds x, Seconds y) { return doubleRatio(x.get(), y.get()); } 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 int absDiff(int a, int b) { return abs(a-b); } static double absDiff(double a, double b) { return abs(a-b); } static BWIntegralImage bwIntegralImage_withMeta(BufferedImage img) { return bwIntegralImage_withMeta(img, null); } static BWIntegralImage bwIntegralImage_withMeta(BufferedImage img, Decolorizer decolorizer) { return setMetaSrc(bwIntegralImage(img, decolorizer), img); } static BWIntegralImage bwIntegralImage_withMeta(BWImage img) { return setMetaSrc(bwIntegralImage(img), img); } static BWImage scaleAndPosterize(BufferedImage img, SnPSettings settings) { return scaleAndPosterize(new BWIntegralImage(img), settings); } static BWImage scaleAndPosterize(IBWIntegralImage ii, SnPSettings settings) { return posterizeBWImage_withMeta(settings.colors, scaledBWImageFromBWIntegralImage_withMeta_height(settings.pixelRowsForImage(ii), ii)); } 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 A optCast(Class c, Object o) { return isInstance(c, o) ? (A) o : null; } static Pt translatePt(Pt a, Pt b) { return addPts(a, b); } static Pt translatePt(int x, int y, Pt a) { return addPts(a, pt(x, y)); } static Pt translatePt(Pt a, int x, int y) { return translatePt(x, y, a); } static Pt scalePt(Pt p, double f) { return new Pt(iround(p.x*f), iround(p.y*f)); } static Pt scalePt(double f, Pt p) { return scalePt(p, f); } static Pt scalePt(int w, int h, double f) { return scalePt(new Pt(w, h), f); } static Class run(String progID, String... args) { Class main = hotwire(progID); callMain(main, args); return main; } static String shortClassName_dropNumberPrefix(Object o) { return dropNumberPrefix(shortClassName(o)); } static Set createOrAddToSyncLinkedHashSet(Set set, A a) { if (set == null) set = syncLinkedHashSet(); set.add(a); return set; } static void remove(List l, int i) { if (l != null && i >= 0 && i < l(l)) l.remove(i); } static void remove(Collection l, A a) { if (l != null) l.remove(a); } static B remove(Map map, Object a) { return map == null ? null : map.remove(a); } static void remove(BitSet bs, int i) { bs.clear(i); } static A pcallF_typed(F0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null; } static B pcallF_typed(F1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null; } static void pcallF_typed(VF1 f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { printStackTrace(__e); } } static void pcallF_typed(IVF1 f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { printStackTrace(__e); } } static void pcallF_typed(IVF2 f, A a, B b) { try { { if (f != null) f.get(a, b); } } catch (Throwable __e) { printStackTrace(__e); } } static Object pcallF_typed(Runnable r) { try { { if (r != null) r.run(); } } catch (Throwable __e) { printStackTrace(__e); } return null; } static A pcallF_typed(IF0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null; } static B pcallF_typed(IF1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null; } static Pt pt(int x, int y) { return new Pt(x, y); } static Pt pt(int x) { return new Pt(x, x); } static volatile StringBuffer local_log = new StringBuffer(); // not redirected static boolean printAlsoToSystemOut = true; static volatile Appendable print_log = local_log; // might be redirected, e.g. to main bot // in bytes - will cut to half that static volatile int print_log_max = 1024*1024; static volatile int local_log_max = 100*1024; static boolean print_silent = false; // total mute if set static Object print_byThread_lock = new Object(); static volatile ThreadLocal print_byThread; // special handling by thread - prefers F1 static volatile Object print_allThreads; static volatile Object print_preprocess; static void print() { print(""); } static A print(String s, A o) { print(combinePrintParameters(s, o)); return o; } // slightly overblown signature to return original object... static A print(A o) { ping_okInCleanUp(); if (print_silent) return o; String s = o + "\n"; print_noNewLine(s); return o; } static void print_noNewLine(String s) { try { Object f = getThreadLocal(print_byThread_dontCreate()); if (f == null) f = print_allThreads; if (f != null) // We do need the general callF machinery here as print_byThread is sometimes shared between modules if (isFalse( f instanceof F1 ? ((F1) f).get(s) : callF(f, s))) return; } catch (Throwable e) { System.out.println(getStackTrace(e)); } print_raw(s); } static void print_raw(String s) { if (print_preprocess != null) s = (String) callF(print_preprocess, s); s = fixNewLines(s); Appendable loc = local_log; Appendable buf = print_log; int loc_max = print_log_max; if (buf != loc && buf != null) { print_append(buf, s, print_log_max); loc_max = local_log_max; } if (loc != null) print_append(loc, s, loc_max); if (printAlsoToSystemOut) System.out.print(s); vmBus_send("printed", mc(), s); } static void print_autoRotate() { } static void stepMaxWithStats(Steppable s, long maxSteps) { stepAllWithStats(s, maxSteps); } static void stepMaxWithStats(long maxSteps, Steppable s) { stepMaxWithStats(s, maxSteps); } static String joinNemptiesWithColon(String... strings) { return joinNempties(": ", strings); } static String joinNemptiesWithColon(Collection strings) { return joinNempties(": ", strings); } static String commaCombine(Object... l) { return joinNemptiesWithComma(flattenCollectionsAndArrays(ll(l))); } static List syncMap(Object f, Map map) { return syncMap(map, f); } // map: func(key, value) -> list element static List syncMap(Map map, Object f) { return map(cloneLinkedHashMap(map), f); // TODO: use a temporary list instead } static Map syncMap() { return synchroHashMap(); } static Map syncMap(Map map) { return synchronizedMap(map); } static long nanoTime() { return System.nanoTime(); } static ThreadLocal saveTiming_last = new ThreadLocal(); static void saveTiming(long ms) { print(ms + " ms"); saveTiming_noPrint(ms); } static void saveTiming_noPrint(long ms) { saveTiming_last.set(ms); } static ThreadLocal saveTiming_tl() { return saveTiming_last; } // 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 Object time(Object f) { long time = sysNow(); Object o = callF(f); done2_always(str(f), time); return o; } static A time(F0 f) { return (A) time((Object) f); } static A time(IF0 f) { return (A) time((Object) f); } static A time(String msg, IF0 f) { long time = sysNow(); A o = f.get(); done2_always(msg, time); return o; } static void time(Runnable f) { time(str(f), f); } static void time(String msg, Runnable f) { time(msg, runnableToIF0(f)); } static DynamicObject dO(String className, Object... x) { return dynamicObject(className, x); } // Irgendwie gab's mal nen Unterschied zwischen den mapGet und get // Methoden. null keys oder so. static B syncMapGetOrCreate(Map map, A key, Class c) { return syncGetOrCreate(map, key, c); } // f : func -> B static B syncMapGetOrCreate(Map map, A key, Object f) { return syncGetOrCreate(map, key, f); } static B syncMapGetOrCreate(Class c, Map map, A key) { return syncGetOrCreate(c, map, key); } static B syncMapGetOrCreate(Map map, A key, IF0 f) { return syncGetOrCreate(map, key, f); } 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 BufferedImage render(WidthAndHeight size, G2Drawable drawable) { return drawableToImage(size, drawable); } static String lines(Iterable lines) { return fromLines(lines); } static String lines(Object[] lines) { return fromLines(asList(lines)); } static List lines(String s) { return toLines(s); } // convenience map call static String lines(Iterable l, IF1 f) { return mapToLines(l, f); } static List ciSorted(Collection c) { return sortedIgnoreCase(c); } static List map(Iterable l, Object f) { return map(f, l); } static List map(Object f, Iterable l) { List x = emptyList(l); if (l != null) for (Object o : l) { ping(); x.add(callF(f, o)); } return x; } // map: func(key, value) -> list element static List map(Map map, Object f) { List x = new ArrayList(); if (map != null) for (Object _e : map.entrySet()) { ping(); Map.Entry e = (Map.Entry) _e; x.add(callF(f, e.getKey(), e.getValue())); } return x; } static List map(Object f, Object[] l) { return map(f, asList(l)); } static List map(Object[] l, Object f) { return map(f, l); } static List map(Object f, Map map) { return map(map, f); } static List map(Iterable l, F1 f) { return map(f, l); } static List map(F1 f, Iterable l) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(callF(f, o)); } return x; } static List map(IF1 f, Iterable l) { return map(l, f); } static List map(Iterable l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(f.get(o)); } return x; } static List map(IF1 f, A[] l) { return map(l, f); } static List map(A[] l, IF1 f) { List x = emptyList(l); if (l != null) for (A o : l) { ping(); x.add(f.get(o)); } return x; } static List map(Map map, IF2 f) { List x = new ArrayList(); if (map != null) for (Map.Entry e : map.entrySet()) { ping(); x.add(f.get(e.getKey(), e.getValue())); } return x; } // new magic alias for mapLL - does it conflict? static List map(IF1 f, A data1, A... moreData) { List x = emptyList(l(moreData)+1); x.add(f.get(data1)); if (moreData != null) for (A o : moreData) { ping(); x.add(f.get(o)); } return x; } static String n2(long l) { return formatWithThousands(l); } static String n2(AtomicLong l) { return n2(l.get()); } static String n2(Collection l) { return n2(l(l)); } static String n2(Map map) { return n2(l(map)); } static String n2(double l, String singular) { return empty(singular) ? str(l) : n2(l, singular, singular + "s"); } static String n2(double l, String singular, String plural) { if (fraction(l) == 0) return n2((long) l, singular, plural); else return l + " " + plural; } static String n2(long l, String singular, String plural) { return n_fancy2(l, singular, plural); } static String n2(long l, String singular) { return empty(singular) ? n2(l) : n_fancy2(l, singular, singular + "s"); } static String n2(Collection l, String singular) { return n2(l(l), singular); } static String n2(Collection l, String singular, String plural) { return n_fancy2(l, singular, plural); } static String n2(Map m, String singular, String plural) { return n_fancy2(m, singular, plural); } static String n2(Map m, String singular) { return n2(l(m), singular); } static String n2(long[] a, String singular) { return n2(l(a), singular); } static String n2(Object[] a, String singular) { return n2(l(a), singular); } static String n2(Object[] a, String singular, String plural) { return n_fancy2(a, singular, plural); } static String n2(IMultiMap mm, String singular) { return n2(mm, singular, singular + "s"); } static String n2(IMultiMap mm, String singular, String plural) { return n_fancy2(l(mm), singular, plural); } static int iround(double d) { return (int) Math.round(d); } static int iround(Number n) { return iround(toDouble(n)); } static double nsToMicroseconds(double ns) { return nanosToMicroseconds(ns); } static String microSymbol() { return "\u00B5"; } static String joinWithComma(Collection c) { return join(", ", c); } static String joinWithComma(Object... c) { return join(", ", c); } static String joinWithComma(String... c) { return join(", ", c); } static String joinWithComma(Pair p) { return p == null ? "" : joinWithComma(str(p.a), str(p.b)); } static String firstToUpper(String s) { if (empty(s)) return s; return Character.toUpperCase(s.charAt(0)) + s.substring(1); } 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 cmp(Number a, Number b) { return a == null ? b == null ? 0 : -1 : cmp(a.doubleValue(), b.doubleValue()); } static int cmp(double a, double b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(int a, int b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(long a, long b) { return a < b ? -1 : a == b ? 0 : 1; } static int cmp(Object a, Object b) { if (a == null) return b == null ? 0 : -1; if (b == null) return 1; return ((Comparable) a).compareTo(b); } static double sqrt(double x) { return Math.sqrt(x); } static Pt ptMinus(Pt a, Pt b) { if (b == null) return a; return new Pt(a.x-b.x, a.y-b.y); } static boolean 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(Rect r, Pt p) { return rectContains(r, p); } static int l(Object[] a) { return a == null ? 0 : a.length; } static int l(boolean[] a) { return a == null ? 0 : a.length; } static int l(byte[] a) { return a == null ? 0 : a.length; } static int l(short[] a) { return a == null ? 0 : a.length; } static int l(long[] a) { return a == null ? 0 : a.length; } static int l(int[] a) { return a == null ? 0 : a.length; } static int l(float[] a) { return a == null ? 0 : a.length; } static int l(double[] a) { return a == null ? 0 : a.length; } static int l(char[] a) { return a == null ? 0 : a.length; } static int l(Collection c) { return c == null ? 0 : c.size(); } static int l(Iterator i) { return iteratorCount_int_close(i); } // consumes the iterator && closes it if possible static int l(Map m) { return m == null ? 0 : m.size(); } static int l(CharSequence s) { return s == null ? 0 : s.length(); } static long l(File f) { return f == null ? 0 : f.length(); } static int l(IMultiMap mm) { return mm == null ? 0 : mm.size(); } static int l(IntRange r) { return r == null ? 0 : r.length(); } static double l(DoubleRange r) { return r == null ? 0 : r.length(); } 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 Object first(Object list) { return first((Iterable) list); } static A first(List list) { return empty(list) ? null : list.get(0); } static A first(A[] bla) { return bla == null || bla.length == 0 ? null : bla[0]; } static Pair first(Map map) { return mapEntryToPair(first(entrySet(map))); } static Pair first(MultiMap mm) { if (mm == null) return null; var e = first(mm.data.entrySet()); if (e == null) return null; return pair(e.getKey(), first(e.getValue())); } static A first(IterableIterator i) { return first((Iterator) i); } static A first(Iterator i) { return i == null || !i.hasNext() ? null : i.next(); } static A first(Iterable i) { if (i == null) return null; Iterator it = i.iterator(); return it.hasNext() ? it.next() : null; } static Character first(String s) { return empty(s) ? null : s.charAt(0); } static Character first(CharSequence s) { return empty(s) ? null : s.charAt(0); } static A first(Pair p) { return p == null ? null : p.a; } static A first(T3 t) { return t == null ? null : t.a; } static Byte first(byte[] l) { return empty(l) ? null : l[0]; } static int first(IntBuffer buf) { return buf.get(0); } static byte first(ByteBuffer buf) { return buf.get(0); } static A first(A[] l, IF1 pred) { return firstThat(l, pred); } static A first(Iterable l, IF1 pred) { return firstThat(l, pred); } static A first(IF1 pred, Iterable l) { return firstThat(pred, l); } static A first(AppendableChain a) { return a == null ? null : a.element; } static java.awt.Color color(String hex) { return awtColor(hex); } static OnePathWithOrigin g22_regionOutline(IImageRegion region) { // TODO: optimize List outlines = region.createdWithDiagonals() ? g22_allBorderTraces_withDiagonals(region) : g22_allBorderTraces(region); return new OnePathWithOrigin(first(outlines), false); } static int _hashCode(Object a) { return a == null ? 0 : a.hashCode(); } static A shallowClone(A o) { return (A) shallowClone_impl(o); } static A shallowClone(A o, A emptyClone) { return copyFields(o, emptyClone); } static Object shallowClone_impl(Object o) { if (o == null) return o; if (o instanceof List) return cloneList((List) o); if (o instanceof Map) return cloneMap((Map) o); if (o instanceof String || o instanceof Number || o instanceof Boolean) return o; if (o instanceof Object[]) { Object[] l = (Object[]) o; return l.clone(); } // clone an arbitrary custom object Object clone; if (o instanceof IMakeEmptyClone) clone = ((IMakeEmptyClone) o).makeEmptyClone(); else clone = nuEmptyObject(o.getClass()); //print("Cloning custom: " + o); copyFields(o, clone); return clone; } 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 void change() { //mainConcepts.allChanged(); // safe version for now cause function is sometimes included unnecessarily (e.g. by EGDiff) callOpt(getOptMC("mainConcepts"), "allChanged"); } 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 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 int ubyteToInt(byte b) { return b & 0x0FF; } static int ubyteToInt(char c) { return c & 0x0FF; } 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 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 int limitToUByte(int i) { return max(0, min(255, i)); } 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 BufferedImage loadImage2(String snippetIDOrURL) { return loadBufferedImage(snippetIDOrURL); } static BufferedImage loadImage2(File file) { return loadBufferedImage(file); } static BufferedImage toBufferedImage(Object o) { return toBufferedImageOpt(o); } static Boolean grabbableGrayPixels_succeeded; static Throwable grabbableGrayPixels_error; static boolean grabbableGrayPixels_enable = true; static GrabbableGrayPixels grabbableGrayPixels(BufferedImage img) { if (img == null || !grabbableGrayPixels_enable) return null; try { var result = grabbableGrayPixels_impl(img); grabbableGrayPixels_succeeded = result != null; return result; } catch (Throwable e) { grabbableGrayPixels_error = e; grabbableGrayPixels_succeeded = false; throw rethrow(e); } } static GrabbableGrayPixels grabbableGrayPixels_impl(BufferedImage img) { Raster raster = img.getRaster(); SampleModel sampleModel = raster.getSampleModel(); if (!(sampleModel instanceof PixelInterleavedSampleModel)) return null; DataBufferByte dataBuffer = (DataBufferByte) (raster.getDataBuffer()); assertEquals(1, dataBuffer.getNumBanks()); assertEquals(DataBuffer.TYPE_BYTE, dataBuffer.getDataType()); // Let's at this point assume the raster data is gray and not a palette int w = img.getWidth(), h = img.getHeight(); int scanlineStride = ((PixelInterleavedSampleModel) sampleModel).getScanlineStride(); byte[] pixels = dataBuffer.getData(); int offset = dataBuffer.getOffset(); int translateX = raster.getSampleModelTranslateX(); int translateY = raster.getSampleModelTranslateY(); offset += -translateX-translateY*scanlineStride; return new GrabbableGrayPixels(pixels, w, h, offset, scanlineStride); } static GrabbableRGBBytePixels grabbableRGBBytePixels(BufferedImage img) { Raster raster = img.getRaster(); SampleModel _sampleModel = raster.getSampleModel(); if (!(_sampleModel instanceof PixelInterleavedSampleModel)) return null; PixelInterleavedSampleModel sampleModel = (PixelInterleavedSampleModel) _sampleModel; DataBufferByte dataBuffer = (DataBufferByte) (raster.getDataBuffer()); assertEquals(1, dataBuffer.getNumBanks()); assertEquals(DataBuffer.TYPE_BYTE, dataBuffer.getDataType()); // Let's at this point assume the raster data is what we // think it is... (TODO: test on unusual platforms) int w = img.getWidth(), h = img.getHeight(); int pixelStride = sampleModel.getPixelStride(); int scanlineStride = sampleModel.getScanlineStride(); byte[] pixels = dataBuffer.getData(); int offset = dataBuffer.getOffset(); int translateX = raster.getSampleModelTranslateX(); int translateY = raster.getSampleModelTranslateY(); offset += -translateX-translateY*scanlineStride; return new GrabbableRGBBytePixels(pixels, w, h, offset, scanlineStride, pixelStride); } 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 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); } // assumptions: // -pingSource() stays constant within a stack frame (so you can cache it) // -all changes happen with tempSetPingSource // -multiple threads can share a PingSource static PingSource pingSource() { return pingSource_tl().get(); } static PingSource pingSource(Thread thread) { return pingSource_tl().get(thread); } static boolean isDefaultDecolorizer(Decolorizer decolorizer) { return decolorizer == null || decolorizer instanceof Decolorizer.Simple; } static int ifloor(double d) { return (int) Math.floor(d); } static IntRange ifloor(DoubleRange r) { return r == null ? null : intRange(ifloor(r.start), ifloor(r.end)); } static double blend2D(double val, double val2, double val3, double val4, double xFrac, double yFrac) { double upper = blend(val, val2, xFrac); double lower = blend(val3, val4, xFrac); return blend(upper, lower, yFrac); } static int bwIntegralImage_sumRect(BWIntegralImage img, int x1, int y1, int x2, int y2) { int bottomLeft = img.getIIValue(x1-1, y2-1); int bottomRight = img.getIIValue(x2-1, y2-1); int topLeft = img.getIIValue(x1-1, y1-1); int topRight = img.getIIValue(x2-1, y1-1); return bottomRight+topLeft-topRight-bottomLeft; } static int bwIntegralImage_sumRect(IBWIntegralImage img, int x1, int y1, int x2, int y2) { int bottomLeft = img.getIIValue(x1-1, y2-1); int bottomRight = img.getIIValue(x2-1, y2-1); int topLeft = img.getIIValue(x1-1, y1-1); int topRight = img.getIIValue(x2-1, y1-1); return bottomRight+topLeft-topRight-bottomLeft; } static double bwIntegralImage_sumRect(IBWIntegralImage img, double x1, double y1, double x2, double y2) { double bottomLeft = img.getIIValue(x1-1, y2-1); double bottomRight = img.getIIValue(x2-1, y2-1); double topLeft = img.getIIValue(x1-1, y1-1); double topRight = img.getIIValue(x2-1, y1-1); return bottomRight+topLeft-topRight-bottomLeft; } // 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 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 BufferedImage grayImageFromIBWIntegralImage(IBWIntegralImage 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 = img.getPixelBrightness(x, y); pixels[i++] = clampToUByte(pixel); } return newGrayBufferedImage(w, h, pixels); } // returns number of steps static long stepAll(Steppable s) { long steps = 0; if (s != null) { var pingSource = pingSource(); while (true) { ping(pingSource); if (s.step()) ++steps; else break; } } return steps; } static 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); } static Pt longToPt(long l) { return new Pt(firstIntFromLong(l), secondIntFromLong(l)); } static long ptToLong(Pt p) { return p == null ? -1: intPairToLong(p.x, p.y); } static long ptToLong(int x, int y) { return intPairToLong(x, y); } static CachedPosterizer cachePosterizer(IPosterizer posterizer) { if (posterizer == null) return null; if (posterizer instanceof CachedPosterizer) return ((CachedPosterizer) posterizer); return new CachedPosterizer(posterizer); } static int parseDigit(char c) { return c >= '0' && c <= '9' ? c-'0' : 0; } static int parseDigit(String s, int i) { return parseDigit(s.charAt(i)); } static Iterator iterator(Iterable c) { return c == null ? emptyIterator() : c.iterator(); } static boolean empty(Collection c) { return c == null || c.isEmpty(); } static boolean empty(Iterable c) { return c == null || !c.iterator().hasNext(); } static boolean empty(CharSequence s) { return s == null || s.length() == 0; } static boolean empty(Map map) { return map == null || map.isEmpty(); } static boolean empty(Object[] o) { return o == null || o.length == 0; } static boolean empty(BitSet bs) { return bs == null || bs.isEmpty(); } static boolean empty(Object o) { if (o instanceof Collection) return empty((Collection) o); if (o instanceof String) return empty((String) o); if (o instanceof Map) return empty((Map) o); if (o instanceof Object[]) return empty((Object[]) o); if (o instanceof byte[]) return empty((byte[]) o); if (o == null) return true; throw fail("unknown type for 'empty': " + getType(o)); } static boolean empty(Iterator i) { return i == null || !i.hasNext(); } static boolean empty(double[] a) { return a == null || a.length == 0; } static boolean empty(float[] a) { return a == null || a.length == 0; } static boolean empty(int[] a) { return a == null || a.length == 0; } static boolean empty(long[] a) { return a == null || a.length == 0; } static boolean empty(byte[] a) { return a == null || a.length == 0; } static boolean empty(short[] a) { return a == null || a.length == 0; } static boolean empty(IMultiMap mm) { return mm == null || mm.size() == 0; } static boolean empty(File f) { return getFileSize(f) == 0; } static boolean empty(IntRange r) { return r == null || r.empty(); } static boolean empty(DoubleRange r) { return r == null || r.isEmpty(); } 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 String singleDigitBytesToString(ByteBuffer l) { if (l == null) return null; int n = l.size(); char[] data = new char[n]; for (int i = 0; i < n; i++) data[i] = (char) ('0' + ubyteToInt(l.get(i)) % 10); return str(data); } static Pt origin() { return pt(0, 0); } static int length(Object[] array) { return array == null ? 0 : array.length; } static int length(List list) { return list == null ? 0 : list.size(); } static int length(String s) { return s == null ? 0 : s.length(); } static PtBuffer ptBuffer(Iterable l) { return new PtBuffer(l); } static final Pt[] onePathDirections_directions = { pt(0, 0), pt(-1, -1), pt(0, -1), pt(1, -1), pt(1, 0), pt(1, 1), pt(0, 1), pt(-1, 1), pt(-1, 0) }; static Pt[] onePathDirections() { return onePathDirections_directions; } static A last(List l) { return empty(l) ? null : l.get(l.size()-1); } static char last(String s) { return empty(s) ? '#' : s.charAt(l(s)-1); } static byte last(byte[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } static int last(int[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } static double last(double[] a) { return l(a) != 0 ? a[l(a)-1] : 0; } static A last(A[] a) { return l(a) != 0 ? a[l(a)-1] : null; } static A last(Iterator it) { A a = null; while (it.hasNext()) { ping(); a = it.next(); } return a; } static A last(Collection l) { if (l == null) return null; if (l instanceof List) return (A) last((List) l); if (l instanceof SortedSet) return (A) last((SortedSet) l); Iterator it = iterator(l); A a = null; while (it.hasNext()) { ping(); a = it.next(); } return a; } static A last(SortedSet l) { return l == null ? null : l.last(); } static A last(ReverseChain l) { return l == null ? null : l.element; } static int last(IntBuffer buf) { return buf.get(buf.size()-1); } static byte last(ByteBuffer buf) { return buf.get(buf.size()-1); } static A last(CompactLinkedHashSet set) { return set == null ? null : set.last(); } static int onePathLookupDirection(Pt p) { return indexOf(onePathDirections(), p); } static void assertBetween(long a, long b, long x) { assertBetween("", a, b, x); } static void assertBetween(String msg, long a, long b, long x) { { if (x >= a && x <= b) return; } throw fail(colonCombine(msg, x + " is not between " + a + " and " + b)); } static void assertBetween(int a, int b, int x) { assertBetween("", a, b, x); } static void assertBetween(String msg, int a, int b, int x) { { if (x >= a && x <= b) return; } throw fail(colonCombine(msg, x + " is not between " + a + " and " + b)); } static Rect boundsOfPts(Iterable l) { BoundsFinder bf = new BoundsFinder(); for (var p : unnullForIteration(l)) bf.add(p); return bf.get(); } // brightness: 0 to 255 static RGB rgbFromGrayscale(int brightness) { float f = RGB.brightnessToFloat(brightness); return new RGB(f, f, f); } static BWImage toBWImage(RGBImage img) { return toBW(img); } static BWImage toBWImage(BufferedImage img) { return img == null ? null : new BWImage(img); } static BWImage toBWImage(MakesBufferedImage img) { if (img == null) return null; if (img instanceof BWImage) return ((BWImage) img); return new BWImage(toBufferedImage(img)); } // unclear semantics as to whether return null on null static ArrayList asList(A[] a) { return a == null ? new ArrayList() : new ArrayList(Arrays.asList(a)); } static ArrayList asList(int[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (int i : a) l.add(i); return l; } static ArrayList asList(long[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (long i : a) l.add(i); return l; } static ArrayList asList(float[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (float i : a) l.add(i); return l; } static ArrayList asList(double[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (double i : a) l.add(i); return l; } static ArrayList asList(short[] a) { if (a == null) return null; ArrayList l = emptyList(a.length); for (short i : a) l.add(i); return l; } static ArrayList asList(Iterator it) { ArrayList l = new ArrayList(); if (it != null) while (it.hasNext()) l.add(it.next()); return l; } // disambiguation static ArrayList asList(IterableIterator s) { return asList((Iterator) s); } static ArrayList asList(Iterable s) { if (s instanceof ArrayList) return (ArrayList) s; ArrayList l = new ArrayList(); if (s != null) for (A a : s) l.add(a); return l; } static ArrayList asList(Enumeration e) { ArrayList l = new ArrayList(); if (e != null) while (e.hasMoreElements()) l.add(e.nextElement()); return l; } static ArrayList asList(ReverseChain c) { return c == null ? emptyList() : c.toList(); } static List asList(Pair p) { return p == null ? null : ll(p.a, p.b); } static List reversedList(Iterable l) { List x = cloneList(l); Collections.reverse(x); return x; } static A set(A o, String field, Object value) { if (o == null) return null; if (o instanceof Class) set((Class) o, field, value); else try { Field f = set_findField(o.getClass(), field); makeAccessible(f); smartSet(f, o, value); } catch (Exception e) { throw new RuntimeException(e); } return o; } static void set(Class c, String field, Object value) { if (c == null) return; try { Field f = set_findStaticField(c, field); makeAccessible(f); smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field set_findStaticField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field) && (f.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Static field '" + field + "' not found in " + c.getName()); } static Field set_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); throw new RuntimeException("Field '" + field + "' not found in " + c.getName()); } static void set(BitSet bs, int idx) { { if (bs != null) bs.set(idx); } } static A onChange(A spinner, Object r) { return onChange(spinner, toRunnable(r)); } static A onChange(A spinner, Runnable r) { if (r != null) { swing(() -> { spinner.addChangeListener(changeListener(r)); }); } return spinner; } static A onChange(A b, Object r) { { swing(() -> { b.addItemListener(itemListener(r)); }); } return b; } static void onChange(JTextComponent tc, Object r) { onUpdate(tc, r); } static void onChange(JTextComponent tc, Runnable r) { onUpdate(tc, r); } static A onChange(A slider, final Object r) { { swing(() -> { slider.addChangeListener(changeListener(r)); }); } return slider; } static JComboBox onChange(JComboBox cb, Runnable r) { addActionListener(cb, r); return cb; } static JComboBox onChange(JComboBox cb, IVF1 f) { if (f != null) addActionListener(cb, () -> f.get(getSelectedItem_typed(cb))); return cb; } static JComboBox onChange(Object r, JComboBox cb) { return onChange(cb, r); } static JComboBox onChange(JComboBox cb, final Object r) { if (isEditableComboBox(cb)) onChange(textFieldFromComboBox(cb), r); else onSelectedItem(cb, new VF1() { public void get(String s) { try { callF(r) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(r)"; }}); return cb; } static A onChange(A tabs, Runnable r) { { swing(() -> { tabs.addChangeListener(changeListener(r)); }); } return tabs; } static A onChange(Runnable r, A cc) { if (cc != null && r != null) { swing(() -> { cc.getSelectionModel().addChangeListener(changeListener(r)); }); } return cc; } static void onChange(IHasChangeListeners a, ChangeTriggerable b) { if (a != null && b != null) a.onChange(new ChangeTrigger(b)); } 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 int areaFromPoints(int x1, int y1, int x2, int y2) { return (x1-x1)*(y2-y1); } static int rectArea(Rect r) { return r == null ? 0 : r.w*r.h; } static Rect toRect_floor(DoubleRect r) { if (r == null) return null; int x = ifloor(r.x), y = ifloor(r.y); return new Rect(x, y, ifloor(r.x2())-x, ifloor(r.y2())-y); } static int fullAlphaMask() { return 0xFF000000; } // from: https://stackoverflow.com/questions/14416107/int-array-to-bufferedimage // pixels are RGB pixels static BufferedImageWithMeta intArrayToBufferedImage(int[] pixels, int w) { return intArrayToBufferedImage(pixels, w, pixels.length/w); } static BufferedImageWithMeta intArrayToBufferedImage(int[] pixels, int w, int h) { int[] bitMasks = new int[]{0xFF0000, 0xFF00, 0xFF, 0xFF000000}; SinglePixelPackedSampleModel sm = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, w, h, bitMasks); DataBufferInt db = new DataBufferInt(pixels, pixels.length); WritableRaster wr = Raster.createWritableRaster(sm, db, new Point()); return new BufferedImageWithMeta(ColorModel.getRGBdefault(), wr, false, null); } static BufferedImageWithMeta intArrayToBufferedImage(int w, int... pixels) { return intArrayToBufferedImage(pixels, w); } 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 b) { if (a != null && b != null) a.putAll(b); return a; } static A addAll(A c, Collection components) { return addComponents(c, components); } static A addAll(A c, Component... components) { return addComponents(c, components); } static int lLongArray(long[] a) { return a == null ? 0 : a.length; } static long[] resizeLongArray(long[] a, int n) { if (n == lLongArray(a)) return a; long[] b = new long[n]; arraycopy(a, 0, b, 0, Math.min(lLongArray(a), n)); return b; } static int maximumSafeArraySize() { return Integer.MAX_VALUE-8; } static ArrayList toList(A[] a) { return asList(a); } static ArrayList toList(int[] a) { return asList(a); } static ArrayList toList(short[] a) { return asList(a); } static ArrayList toList(Set s) { return asList(s); } static ArrayList toList(Iterable s) { return asList(s); } static ArrayList longArrayToList(long[] a) { if (a == null) return null; return longArrayToList(a, 0, a.length); } // no range checking on from/to in the interest of S P E E E E E EEED static ArrayList longArrayToList(long[] a, int from, int to) { if (a == null) return null; ArrayList < Long > l = new ArrayList<>(to-from); for (int i = from; i < to; i++) l.add(a[i]); return l; } static List asVirtualList(A[] a) { return wrapArrayAsList(a); } 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 String squareBracket(String s) { return "[" + s + "]"; } static String joinWithSpace(Iterable c) { return join(" ", c); } static String joinWithSpace(Object... c) { return join(" ", c); } static void arraycopy(Object[] a, Object[] b) { if (a != null && b != null) arraycopy(a, 0, b, 0, Math.min(a.length, b.length)); } static void arraycopy(Object src, int srcPos, int destPos, int n) { arraycopy(src, srcPos, src, destPos, n); } static void arraycopy(Object src, int srcPos, Object dest, int destPos, int n) { if (n != 0) System.arraycopy(src, srcPos, dest, destPos, n); } static 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 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 String n(long l, String name) { return l + " " + trim(l == 1 ? singular(name) : getPlural(name)); } static String n(Collection l, String name) { return n(l(l), name); } static String n(Map m, String name) { return n(l(m), name); } static String n(Object[] a, String name) { return n(l(a), name); } static UnsupportedOperationException unsupportedOperation() { throw new UnsupportedOperationException(); } static int rgbRed(int rgb) { return (rgb >> 16) & 0xFF; } static int rgbGreen(int rgb) { return (rgb >> 8) & 0xFF; } static int rgbBlue(int rgb) { return rgb & 0xFF; } static Color getColor(BufferedImage img, int x, int y) { return colorFromRGBA(img.getRGB(x, y)); } static Color getColor(BufferedImage img, Pt p) { return colorFromRGBA(img.getRGB(p.x, p.y)); } static int asInt(Object o) { return toInt(o); } static int lByteArray(byte[] a) { return a == null ? 0 : a.length; } static byte[] resizeByteArray(byte[] a, int n) { if (n == l(a)) return a; byte[] b = new byte[n]; arraycopy(a, 0, b, 0, min(l(a), n)); return b; } static Object[] toArray(Collection c) { return toObjectArray(c); } static A[] toArray(Class type, Iterable c) { return toArray(c, type); } static A[] toArray(Class type, Collection c) { return toArray(c, type); } static A[] toArray(Collection c, Class type) { A[] a = arrayOfType(l(c), type); if (a.length == 0) return a; asList(c).toArray(a); return a; } static A[] toArray(Iterable c, Class type) { var c2 = asList(c); A[] a = arrayOfType(l(c2), type); if (a.length == 0) return a; c2.toArray(a); return a; } // array must have correct length and will be filled static A[] toArray(A[] array, Collection c) { if (array == null || c == null) return null; asList(c).toArray(array); return array; } static ArrayList byteArrayToList(byte[] a) { if (a == null) return null; return byteArrayToList(a, 0, a.length); } // no range checking on from/to in the interest of speed static ArrayList byteArrayToList(byte[] a, int from, int to) { if (a == null) return null; ArrayList < Byte > l = new ArrayList<>(to-from); for (int i = from; i < to; i++) l.add(a[i]); return l; } static A popLast(List l) { return liftLast(l); } static List popLast(int n, List l) { return liftLast(n, l); } static A popFirst(List l) { if (empty(l)) return null; A a = first(l); l.remove(0); return a; } static A popFirst(Collection l) { if (empty(l)) return null; A a = first(l); l.remove(a); return a; } static Pair popFirst(Map map) { if (map == null) return null; var it = map.entrySet().iterator(); if (!it.hasNext()) return null; var p = mapEntryToPair(it.next()); it.remove(); return p; } static List popFirst(int n, List l) { List part = cloneSubList(l, 0, n); removeSubList(l, 0, n); return part; } static AppendableChain popFirst(AppendableChain a) { return a == null ? null : a.popFirst(); } static A nextToLast(List l) { return get(l, l(l)-2); } static byte[] subByteArray(byte[] b, int start) { return subByteArray(b, start, l(b)); } static byte[] subByteArray(byte[] b, int start, int end) { start = max(start, 0); end = min(end, l(b)); if (start == 0 && end == l(b)) return b; if (start >= end) return new byte[0]; byte[] x = new byte[end-start]; System.arraycopy(b, start, x, 0, end-start); return x; } static byte[] subByteArray(byte[] b, IntRange r) { return r == null ? null : subByteArray(b, r.start, r.end); } 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 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 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 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 long longMul(long a, long b) { return a*b; } static boolean nempty(Collection c) { return !empty(c); } static boolean nempty(CharSequence s) { return !empty(s); } static boolean nempty(Object[] o) { return !empty(o); } static boolean nempty(byte[] o) { return !empty(o); } static boolean nempty(int[] o) { return !empty(o); } static boolean nempty(BitSet bs) { return !empty(bs); } static boolean nempty(Map m) { return !empty(m); } static boolean nempty(Iterator i) { return i != null && i.hasNext(); } static boolean nempty(IMultiMap mm) { return mm != null && mm.size() != 0; } static boolean nempty(Object o) { return !empty(o); } static boolean nempty(IntRange r) { return !empty(r); } static boolean nempty(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 int dualLog(int i) { return 32-Integer.numberOfLeadingZeros(max(0, i-1)); } static A listGetOrCreate(List l, int i, Class 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 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); } }; } // Use like this: printVars(+x, +y); // Or: printVars("bla", +x); // Or: printVars bla(, +x); static void printVars(Object... params) { printVars_str(params); } static Rect rectFromPoints(int x1, int y1, int x2, int y2) { return pointsRect(x1, y1, x2, y2); } static Rect rectFromPoints(Pt a, Pt b) { return pointsRect(a.x, a.y, b.x, b.y); } static 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 WeakReference creator_class; static Object creator() { return creator_class == null ? null : creator_class.get(); } // f: func -> A (stream ends when f returns null) static IterableIterator iteratorFromFunction(final Object f) { class IFF extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = (A) callF(f); done = a == null; } }; return new IFF(); } // optimized version for F0 argument static IterableIterator iteratorFromFunction(F0 f) { return iteratorFromFunction_f0(f); } static IterableIterator iteratorFromFunction(IF0 f) { return iteratorFromFunction_if0(f); } static Color toColor(RGB rgb) { return rgb == null ? null : rgb.getColor(); } static Color toColor(int rgb) { return new Color(rgb); } static Color toColor(String hex) { return awtColor(hex); } static Color toColor(int[] rgb) { return new Color(rgb[0], rgb[1], rgb[2]); } // Use like this: renderRecordVars("MyRecord", +x, +y) static String renderRecordVars(String recordName, Object... params) { List l = new ArrayList(); int i = 0; for (; i+1 < l(params); i += 2) l.add(params[i] + "=" + params[i+1]); return formatFunctionCall(recordName, l); } 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 createEmptyMap) { if (o == null) return null; // The following shortcut depends on the assumption that a meta field never reverts // to null when it was a map Object meta = o._getMeta(); if (meta instanceof Map) return ((Map) meta); // non-shortcut path (create meta) var mutex = tempMetaMutex(o); try { var actualMutex = mutex.get(); synchronized(actualMutex) { meta = o._getMeta(); if (meta instanceof Map) return ((Map) meta); Map map = createEmptyMap.get(); if (meta != null) map.put("previousMeta" , meta); o._setMeta(map); return map; } } finally { _close(mutex); }} static void syncMapPutOrRemove(Map map, A key, B value) { syncMapPut2(map, key, value); } static long now_virtualTime; static long now() { return now_virtualTime != 0 ? now_virtualTime : System.currentTimeMillis(); } static String formatLocalDateWithSeconds(long time) { return localDateWithSeconds(time); } static String formatLocalDateWithSeconds() { return localDateWithSeconds(); } static BigInteger plus(BigInteger a, BigInteger b) { return a.add(b); } static BigInteger plus(BigInteger a, long b) { return a.add(bigint(b)); } static long plus(long a, long b) { return a+b; } static int plus(int a, int b) { return a+b; } static float plus(float a, float b) { return a+b; } static double plus(double a, double b) { return a+b; } static long toMS(double seconds) { return (long) (seconds*1000); } static long clockTimeToSystemTime(long now) { return now == 0 ? 0 : now + clockToSysTimeDiff(); } static List minus(Collection a, Object... b) { Set set = asSet(b); List l = new ArrayList(); for (Object s : unnull(a)) if (!set.contains(s)) l.add(s); return l; } static BigInteger minus(BigInteger a, BigInteger b) { return a.subtract(b); } static Complex minus(Complex c) { return c == null ? null : complex(-c.re(), -c.im()); } static int minus(int a, int b) { return a-b; } static double minus(double a, double b) { return a-b; } static BufferedImage clipBufferedImage(BufferedImage src, Rectangle clip) { return clipBufferedImage(src, new Rect(clip)); } static BufferedImage clipBufferedImage(BufferedImage src, Rect r) { if (src == null || r == null) return null; // fixClipRect r = intersectRects(r, new Rect(0, 0, src.getWidth(), src.getHeight())); if (rectEmpty(r)) return null; // can't make zero-sized BufferedImage return src.getSubimage(r.x, r.y, r.w, r.h); } static BufferedImage clipBufferedImage(BufferedImage src, int x, int y, int w, int h) { return clipBufferedImage(src, new Rect(x, y, w, h)); } static Map> callF_cache = newDangerousWeakHashMap(); static A callF(F0 f) { return f == null ? null : f.get(); } static B callF(F1 f, A a) { return f == null ? null : f.get(a); } static A callF(IF0 f) { return f == null ? null : f.get(); } static B callF(IF1 f, A a) { return f == null ? null : f.get(a); } static B callF(A a, IF1 f) { return f == null ? null : f.get(a); } static C callF(IF2 f, A a, B b) { return f == null ? null : f.get(a, b); } static void callF(VF1 f, A a) { if (f != null) f.get(a); } static void callF(A a, IVF1 f) { if (f != null) f.get(a); } static void callF(IVF1 f, A a) { if (f != null) f.get(a); } static Object callF(Runnable r) { { if (r != null) r.run(); } return null; } static Object callF(Object f, Object... args) { return safeCallF(f, args); } static Object safeCallF(Object f, Object... args) { if (f instanceof Runnable) { ((Runnable) f).run(); return null; } if (f == null) return null; Class c = f.getClass(); ArrayList methods; synchronized(callF_cache) { methods = callF_cache.get(c); if (methods == null) methods = callF_makeCache(c); } int n = l(methods); if (n == 0) { if (f instanceof String) throw fail("Legacy call: " + f); throw fail("No get method in " + getClassName(c)); } if (n == 1) return invokeMethod(methods.get(0), f, args); for (int i = 0; i < n; i++) { Method m = methods.get(i); if (call_checkArgs(m, args, false)) return invokeMethod(m, f, args); } throw fail("No matching get method in " + getClassName(c)); } // used internally static ArrayList callF_makeCache(Class c) { ArrayList l = new ArrayList(); Class _c = c; do { for (Method m : _c.getDeclaredMethods()) if (m.getName().equals("get")) { makeAccessible(m); l.add(m); } if (!l.isEmpty()) break; _c = _c.getSuperclass(); } while (_c != null); callF_cache.put(c, l); return l; } static String getType(Object o) { return getClassName(o); } // action = runnable or method name static void onChangeAndNow(JComponent c, Object r) { onUpdateAndNow(c, r); } static void onChangeAndNow(List l, Object r) { onUpdateAndNow(l, r); } static void onChangeAndNow(JTextComponent c, IVF1 r) { onUpdateAndNow(c, r); } static JComboBox onChangeAndNow(JComboBox cb, IVF1 f) { onChange(cb, f); { if (f != null) f.get(getSelectedItem_typed(cb)); } return cb; } static A onChangeAndNow(A tabs, Runnable r) { if (r != null) { onChange(tabs, r); r.run(); } return tabs; } static JSlider onChangeAndNow(JSlider s, Runnable f) { if (s != null && f != null) { onChange(s, f); f.run(); } return s; } static int lIntArray(int[] a) { return a == null ? 0 : a.length; } static int[] resizeIntArray(int[] a, int n) { if (n == lIntArray(a)) return a; int[] b = new int[n]; arraycopy(a, 0, b, 0, Math.min(lIntArray(a), n)); return b; } static String unnull(String s) { return s == null ? "" : s; } static Collection unnull(Collection l) { return l == null ? emptyList() : l; } static List unnull(List l) { return l == null ? emptyList() : l; } static int[] unnull(int[] l) { return l == null ? emptyIntArray() : l; } static char[] unnull(char[] l) { return l == null ? emptyCharArray() : l; } static double[] unnull(double[] l) { return l == null ? emptyDoubleArray() : l; } static Map unnull(Map l) { return l == null ? emptyMap() : l; } static Iterable unnull(Iterable i) { return i == null ? emptyList() : i; } static A[] unnull(A[] a) { return a == null ? (A[]) emptyObjectArray() : a; } static BitSet unnull(BitSet b) { return b == null ? new BitSet() : b; } static Pt unnull(Pt p) { return p == null ? new Pt() : p; } //ifclass Symbol static Symbol unnull(Symbol s) { return s == null ? emptySymbol() : s; } //endif static Pair unnull(Pair p) { return p != null ? p : new Pair(null, null); } static int unnull(Integer i) { return i == null ? 0 : i; } static long unnull(Long l) { return l == null ? 0L : l; } static double unnull(Double l) { return l == null ? 0.0 : l; } static boolean set_trueIfChanged(Object o, String field, Object value) { if (eq(get(o, field), value)) return false; set(o, field, value); return true; } static boolean set_trueIfChanged(IVar v, A value) { return setVar_trueIfChanged(v, value); } static boolean has(String a, String b, String c) { return false; } static boolean has(T3 t) { return false; } // We even allow overriding existing interface implementations static A lookupInterface(Class intrface, Object o) { return lookupDynamicInterface(intrface, o); } static A lookupInterface(Object o, Class intrface) { return lookupDynamicInterface(o, intrface); } static int seconds() { return seconds(java.util.Calendar.getInstance()); } static int seconds(java.util.Calendar c) { return c.get(java.util.Calendar.SECOND); } static String formatDouble(double d, int digits) { String format = digits <= 0 ? "0" : "0." + rep(digits, '#'); return decimalFormatEnglish(format, d); } static String formatDouble(double d) { return str(d); } static String formatDouble(DoubleRange r, int digits) { return r == null ? "null" : "[" + formatDouble(r.start, digits) + ";" + formatDouble(r.end, digits) + "]"; } static String flexLines(Object... l) { return lines(flattenIterablesAndArrays(ll(l))); } static String roundBracket(String s) { return "(" + s + ")"; } static String roundBracket(Object s) { return roundBracket(str(s)); } static List countIteratorToList(int b) { return countIteratorToList(0, b); } static List countIteratorToList(int a, int b) { return asList(countIterator(a, b)); } static List countIteratorToList(int b, IF1 f) { return countIteratorToList(0, b, f); } static List countIteratorToList(int a, int b, IF1 f) { return asList(countIterator(a, b, f)); } static List countIteratorToList(int a, int b, int step) { return asList(countIterator(a, b, step)); } static List countIteratorToList(double a, double b, double step, IF1 f) { return asList(countIterator(a, b, step, f)); } static List countIteratorToList(double a, double b, double step) { return asList(countIterator(a, b, step)); } static List countIteratorToList(IF1 f, double a, double b, double step) { return asList(countIterator(f, a, b, step)); } static List countIteratorToList(IF1 f, int b) { return countIteratorToList(f, 0, b); } static List countIteratorToList(IF1 f, int a, int b) { return asList(countIterator(f, a, b)); } // lineNr starts at 1 static String getLine(String s, int lineNr) { return safeGet(toLines(s), lineNr-1); } 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 AutoCloseable tempInterceptPrintIfNotIntercepted(F1 f) { return print_byThread().get() == null ? tempInterceptPrint(f) : null; } static A assertEquals(Object x, A y) { return assertEquals("", x, y); } static A assertEquals(String msg, Object x, A y) { if (assertVerbose()) return assertEqualsVerbose(msg, x, y); if (!(x == null ? y == null : x.equals(y))) throw fail((msg != null ? msg + ": " : "") + y + " != " + x); return y; } static Pair pair(A a, B b) { return new Pair(a, b); } static Pair pair(A a) { return new Pair(a, a); } static float abs(float f) { return Math.abs(f); } static int abs(int i) { return Math.abs(i); } static double abs(double d) { return Math.abs(d); } static double abs(Complex c) { return c.abs(); } static A setMetaSrc(A a, Object src) { setMetaAndVerify(a, "src", src); return a; } static A setMetaSrc(A a, Object src) { setMetaAndVerify(a, "src", src); return a; } static BWIntegralImage bwIntegralImage(BufferedImage img) { return bwIntegralImage(img, null); } static BWIntegralImage bwIntegralImage(BufferedImage img, Decolorizer decolorizer) { return img == null ? null : new BWIntegralImage(img, decolorizer); } static BWIntegralImage bwIntegralImage(BWImage img) { return img == null ? null : new BWIntegralImage(img); } static BWIntegralImage bwIntegralImage(IBWImage img) { return img == null ? null : new BWIntegralImage(img); } static BWImage posterizeBWImage_withMeta(int brightnessLevels, BufferedImage img) { return posterizeBWImage_withMeta(brightnessLevels, toBWImage(img)); } static BWImage posterizeBWImage_withMeta(int brightnessLevels, BWImage img) { PosterizeBWImage x = new PosterizeBWImage(brightnessLevels, img); x.run(); return setMetaSrcUnlessSame(img, x.result, x); } static BWImage scaledBWImageFromBWIntegralImage_withMeta(int w, IBWIntegralImage img) { return scaledBWImageFromBWIntegralImage_withMeta(img, w); } static BWImage scaledBWImageFromBWIntegralImage_withMeta_height(int h, IBWIntegralImage img) { return scaledBWImageFromBWIntegralImage_withMeta(img, widthForHeight(img, h), h); } static BWImage scaledBWImageFromBWIntegralImage_withMeta(IBWIntegralImage img, int w) { return scaledBWImageFromBWIntegralImage_withMeta(img, w, heightForWidth(img, w)); } static BWImage scaledBWImageFromBWIntegralImage_withMeta(IBWIntegralImage img, int w, int h) { ScaledBWImageFromBWIntegralImage x = new ScaledBWImageFromBWIntegralImage(img, w, h); x.run(); return setMetaSrc(x.result, x); } static boolean isInstance(Class type, Object arg) { return type.isInstance(arg); } static Pt addPts(Pt a, Pt b) { return a == null ? b : b == null ? a : new Pt(a.x+b.x, a.y+b.y); } // custom mainClass only works with hotwire_here static Class hotwire(String src) { return hotwire(src, __1 -> mainClassNameForClassLoader(__1)); } static Class hotwire(String src, IF1 calculateMainClass) { assertFalse(_inCore()); Class j = getJavaX(); if (isAndroid()) { synchronized(j) { // hopefully this goes well... List libraries = new ArrayList(); File srcDir = (File) call(j, "transpileMain", src, libraries); if (srcDir == null) throw fail("transpileMain returned null (src=" + quote(src) + ")"); Object androidContext = get(j, "androidContext"); return (Class) call(j, "loadx2android", srcDir, src); } } else { Class c = (Class) (call(j, "hotwire", src)); hotwire_copyOver(c); return c; } } static A callMain(A c, String... args) { callOpt(c, "main", new Object[] {args}); return c; } static void callMain() { callMain(mc()); } static String dropNumberPrefix(String s) { return dropFirst(s, indexOfNonDigit(s)); } static String shortClassName(Object o) { if (o == null) return null; Class c = o instanceof Class ? (Class) o : o.getClass(); String name = c.getName(); return shortenClassName(name); } static Set syncLinkedHashSet() { return synchroLinkedHashSet(); } static A printStackTrace(A e) { // we go to system.out now - system.err is nonsense if (e != null) print(getStackTrace(e)); return e; } static void printStackTrace() { printStackTrace(new Throwable()); } static void printStackTrace(String msg) { printStackTrace(new Throwable(msg)); } static void printStackTrace(String msg, Throwable e) { printStackTrace(new Throwable(msg, e)); } static String combinePrintParameters(String s, Object o) { return (endsWithLetterOrDigit(s) ? s + ": " : s) + o; } static void ping_okInCleanUp() { if (ping_pauseAll || ping_anyActions) ping_impl(true); } // this syntax should be removed... static Object getThreadLocal(Object o, String name) { ThreadLocal t = (ThreadLocal) (getOpt(o, name)); return t != null ? t.get() : null; } static A getThreadLocal(ThreadLocal tl) { return tl == null ? null : tl.get(); } static A getThreadLocal(ThreadLocal tl, A defaultValue) { return or(getThreadLocal(tl), defaultValue); } static ThreadLocal print_byThread_dontCreate() { return print_byThread; } static boolean isFalse(Object o) { return eq(false, o); } static String getStackTrace(Throwable throwable) { lastException(throwable); return getStackTrace_noRecord(throwable); } static String getStackTrace_noRecord(Throwable throwable) { StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); return hideCredentials(writer.toString()); } static String getStackTrace() { return getStackTrace_noRecord(new Throwable()); } static String getStackTrace(String msg) { return getStackTrace_noRecord(new Throwable(msg)); } static String fixNewLines(String s) { int i = indexOf(s, '\r'); if (i < 0) return s; int l = s.length(); StringBuilder out = new StringBuilder(l); out.append(s, 0, i); for (; i < l; i++) { char c = s.charAt(i); if (c != '\r') out.append(c); else { out.append('\n'); if (i+1 < l && s.charAt(i+1) == '\n') ++i; } } return out.toString(); } static void print_append(Appendable buf, String s, int max) { try { synchronized(buf) { buf.append(s); if (buf instanceof StringBuffer) rotateStringBuffer(((StringBuffer) buf), max); else if (buf instanceof StringBuilder) rotateStringBuilder(((StringBuilder) buf), max); } } catch (Exception __e) { throw rethrow(__e); } } static void vmBus_send(String msg, Object... args) { Object arg = vmBus_wrapArgs(args); pcallFAll_minimalExceptionHandling(vm_busListeners_live(), msg, arg); pcallFAll_minimalExceptionHandling(vm_busListenersByMessage_live().get(msg), msg, arg); } static void vmBus_send(String msg) { vmBus_send(msg, (Object) null); } static Class mc() { return main.class; } static void stepAllWithStats(Steppable s) { stepAllWithStats(s, null); } static void stepAllWithStats(Steppable s, Long maxSteps) { if (s == null) return; long n = 0; long time = sysNow(); if (maxSteps == null) while (s.step()) { ping(); ++n; } else while (n < maxSteps && s.step()) { ping(); ++n; } time = sysNow()-time; print(n2(n, "step") + " in " + n2(time) + " ms"); } static String joinNempties(String sep, Object... strings) { return joinStrings(sep, strings); } static String joinNempties(String sep, Iterable strings) { return joinStrings(sep, strings); } static String joinNemptiesWithComma(Object... strings) { return joinNempties(", ", strings); } static String joinNemptiesWithComma(Iterable strings) { return joinNempties(", ", strings); } static List flattenCollectionsAndArrays(Iterable a) { List l = new ArrayList(); for (Object x : a) if (x instanceof Collection) l.addAll(flattenCollectionsAndArrays((Collection) x)); else if (x instanceof Object[]) l.addAll(flattenCollectionsAndArrays(asList((Object[]) x))); else l.add(x); return l; } static List ll(A... a) { ArrayList l = new ArrayList(a.length); if (a != null) for (A x : a) l.add(x); return l; } static LinkedHashMap cloneLinkedHashMap(Map map) { return map == null ? new LinkedHashMap() : new LinkedHashMap(map); } static Map synchroHashMap() { return synchronizedMap(new HashMap()); } static Map synchronizedMap() { return synchroMap(); } static Map synchronizedMap(Map map) { return synchroMap(map); } static Field getOpt_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static Field makeAccessible(Field f) { try { f.setAccessible(true); } catch (Throwable e) { // Note: The error reporting only works with Java VM option --illegal-access=deny vmBus_send("makeAccessible_error", e, f); } return f; } static Method makeAccessible(Method m) { try { m.setAccessible(true); } catch (Throwable e) { vmBus_send("makeAccessible_error", e, m); } return m; } static Constructor makeAccessible(Constructor c) { try { c.setAccessible(true); } catch (Throwable e) { vmBus_send("makeAccessible_error", e, c); } return c; } static Object getOptDynOnly(DynamicObject o, String field) { if (o == null || o.fieldValues == null) return null; return o.fieldValues.get(field); } static long sysNow() { ping(); return System.nanoTime()/1000000; } static long done2_always(long startTime, String desc) { long time = sysNow()-startTime; // BREAKING CHANGE: Now stores result for lastTiming() saveTiming_noPrint(time); print(desc + " [" + time + " ms]"); return time; } static long done2_always(String desc, long startTime) { return done2_always(startTime, desc); } static long done2_always(long startTime) { return done2_always(startTime, ""); } static IF0 runnableToIF0(Runnable r) { return r == null ? null : () -> { r.run(); return null; }; } static DynamicObject dynamicObject(String className, Object... x) { DynamicObject d = new DynamicObject(className); for (int i = 0; i < x.length-1; i += 2) if (x[i+1] != null) setDyn(d, (String) x[i], x[i+1]); return d; } static B syncGetOrCreate(Map map, A key, Class c) { try { synchronized(map) { return getOrCreate(map, key, c); } } catch (Exception __e) { throw rethrow(__e); } } // f : func -> B static B syncGetOrCreate(Map map, A key, Object f) { synchronized(map) { return getOrCreate(map, key, f); } } static B syncGetOrCreate(Class c, Map map, A key) { return syncGetOrCreate(map, key, c); } static B syncGetOrCreate(Map map, A key, IF0 f) { synchronized(map) { return getOrCreate(map, key, f); } } 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 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 BufferedImage drawableToImage(WidthAndHeight size, G2Drawable drawable) { var img = whiteImage(size); { if (drawable != null) drawable.drawOn(img); } return img; } // usually L static String fromLines(Iterable lines) { StringBuilder buf = new StringBuilder(); if (lines != null) for (Object line : lines) buf.append(str(line)).append('\n'); return buf.toString(); } static String fromLines(String... lines) { return fromLines(asList(lines)); } static IterableIterator toLines(File f) { return linesFromFile(f); } static List toLines(String s) { List lines = new ArrayList(); if (s == null) return lines; int start = 0; while (true) { int i = toLines_nextLineBreak(s, start); if (i < 0) { if (s.length() > start) lines.add(s.substring(start)); break; } lines.add(s.substring(start, i)); if (s.charAt(i) == '\r' && i+1 < s.length() && s.charAt(i+1) == '\n') i += 2; else ++i; start = i; } return lines; } static int toLines_nextLineBreak(String s, int start) { int n = s.length(); for (int i = start; i < n; i++) { char c = s.charAt(i); if (c == '\r' || c == '\n') return i; } return -1; } static List mapToLines(Map map) { List l = new ArrayList(); for (Object key : keys(map)) l.add(str(key) + " = " + str(map.get(key))); return l; } static String mapToLines(Map map, Object f) { return lines(map(map, f)); } static String mapToLines(Object f, Map map) { return lines(map(map, f)); } static String mapToLines(Object f, Iterable l) { return lines(map(f, l)); } static String mapToLines(Iterable l, IF1 f) { return mapToLines((Object) f, l); } static String mapToLines(IF1 f, Iterable l) { return mapToLines((Object) f, l); } static String mapToLines(Map map, IF2 f) { return lines(map(map, f)); } static String mapToLines(IF1 f, A data1, A... moreData) { return lines(map(f, data1, moreData)); } static List sortedIgnoreCase(Collection c) { List l = cloneList(c); Collections.sort(l, caseInsensitiveComparator()); return l; } // legacy mode //sbool ping_actions_shareable = true; static volatile boolean ping_pauseAll = false; static int ping_sleep = 100; // poll pauseAll flag every 100 static volatile boolean ping_anyActions = false; static Map ping_actions = newWeakHashMap(); static ThreadLocal ping_isCleanUpThread = new ThreadLocal(); // ignore pingSource if not PingV3 static boolean ping(PingSource pingSource) { return ping(); } // always returns true static boolean ping() { //ifdef useNewPing newPing(); //endifdef if (ping_pauseAll || ping_anyActions) ping_impl(true /* XXX */); //ifndef LeanMode ping_impl(); endifndef return true; } // returns true when it slept static boolean ping_impl(boolean okInCleanUp) { try { if (ping_pauseAll && !isAWTThread()) { do Thread.sleep(ping_sleep); while (ping_pauseAll); return true; } if (ping_anyActions) { // don't allow sharing ping_actions if (!okInCleanUp && !isTrue(ping_isCleanUpThread.get())) failIfUnlicensed(); Object action = null; synchronized(ping_actions) { if (!ping_actions.isEmpty()) { action = ping_actions.get(currentThread()); if (action instanceof Runnable) ping_actions.remove(currentThread()); if (ping_actions.isEmpty()) ping_anyActions = false; } } if (action instanceof Runnable) ((Runnable) action).run(); else if (eq(action, "cancelled")) throw fail("Thread cancelled."); } return false; } catch (Exception __e) { throw rethrow(__e); } } static String formatWithThousands(long l) { return formatWithThousandsSeparator(l); } static double fraction(double d) { return d % 1; } static String n_fancy2(long l, String singular, String plural) { return formatWithThousandsSeparator(l) + " " + trim(l == 1 ? singular : plural); } static String n_fancy2(Collection l, String singular, String plural) { return n_fancy2(l(l), singular, plural); } static String n_fancy2(Map m, String singular, String plural) { return n_fancy2(l(m), singular, plural); } static String n_fancy2(Object[] a, String singular, String plural) { return n_fancy2(l(a), singular, plural); } static double toDouble(Object o) { if (o instanceof Number) return ((Number) o).doubleValue(); if (o instanceof BigInteger) return ((BigInteger) o).doubleValue(); if (o instanceof String) return parseDouble((String) o); if (o == null) return 0.0; throw fail(o); } static double nanosToMicroseconds(double ns) { return ns/1000; } public static String join(String glue, Iterable strings) { if (strings == null) return ""; if (strings instanceof Collection) { if (((Collection) strings).size() == 1) return str(first((Collection) strings)); } StringBuilder buf = new StringBuilder(); Iterator i = strings.iterator(); if (i.hasNext()) { buf.append(i.next()); while (i.hasNext()) buf.append(glue).append(i.next()); } return buf.toString(); } public static String join(String glue, String... strings) { return join(glue, Arrays.asList(strings)); } public static String join(String glue, Object... strings) { return join(glue, Arrays.asList(strings)); } static String join(Iterable strings) { return join("", strings); } static String join(Iterable strings, String glue) { return join(glue, strings); } public static String join(String[] strings) { return join("", strings); } static String join(String glue, Pair p) { return p == null ? "" : str(p.a) + glue + str(p.b); } static int iteratorCount_int_close(Iterator i) { try { int n = 0; if (i != null) while (i.hasNext()) { i.next(); ++n; } if (i instanceof AutoCloseable) ((AutoCloseable) i).close(); return n; } catch (Exception __e) { throw rethrow(__e); } } static Pair mapEntryToPair(Map.Entry e) { return e == null ? null : pair(e.getKey(), e.getValue()); } static Set> entrySet(Map map) { return _entrySet(map); } static A firstThat(Iterable l, IF1 pred) { for (A a : unnullForIteration(l)) if (pred.get(a)) return a; return null; } static A firstThat(A[] l, IF1 pred) { for (A a : unnullForIteration(l)) if (pred.get(a)) return a; return null; } static A firstThat(IF1 pred, Iterable l) { return firstThat(l, pred); } static A firstThat(IF1 pred, A[] l) { return firstThat(l, pred); } static java.awt.Color awtColor(String hex) { byte[] b = bytesFromHex(dropPrefix("#", hex)); return new Color(ubyteToInt(b[0]), ubyteToInt(b[1]), ubyteToInt(b[2])); } static List g22_allBorderTraces_withDiagonals(IImageRegion region) { RegionBorder_innerPoints_withDiagonals walker = new RegionBorder_innerPoints_withDiagonals(region); List out = new ArrayList(); walker.onNewTrace(hole -> out.add(new PtBuffer())); walker.onFoundPoint(p -> last(out).add(p)); walker.run(); return out; } static List g22_allBorderTraces(IImageRegion region) { RegionBorder_innerPoints_v2 walker = new RegionBorder_innerPoints_v2(region); List out = new ArrayList(); walker.onNewTrace(hole -> out.add(new PtBuffer())); walker.onFoundPoint(p -> last(out).add(p)); walker.run(); return out; } static A copyFields(Object x, A y, String... fields) { if (empty(fields)) { // assume we should copy all fields Map map = objectToMap(x); for (String field : map.keySet()) setOpt(y, field, map.get(field)); } else for (String field : fields) { Object o = getOpt(x, field); if (o != null) setOpt(y, field, o); } return y; } static A copyFields(Object x, A y, Collection fields) { return copyFields(x, y, asStringArray(fields)); } static Map nuEmptyObject_cache = newDangerousWeakHashMap(); static A nuEmptyObject(Class c) { try { Constructor ctr; synchronized(nuEmptyObject_cache) { ctr = nuEmptyObject_cache.get(c); if (ctr == null) { nuEmptyObject_cache.put(c, ctr = nuEmptyObject_findConstructor(c)); makeAccessible(ctr); } } try { return (A) ctr.newInstance(); } catch (InstantiationException e) { if (empty(e.getMessage())) if ((c.getModifiers() & Modifier.ABSTRACT) != 0) throw fail("Can't instantiate abstract class " + className(c), e); else throw fail("Can't instantiate " + className(c), e); else throw rethrow(e); } } catch (Exception __e) { throw rethrow(__e); } } static Constructor nuEmptyObject_findConstructor(Class c) { for (Constructor m : getDeclaredConstructors_cached(c)) if (m.getParameterTypes().length == 0) return m; throw fail("No default constructor declared in " + c.getName()); } static Object callOpt(Object o) { return callF(o); } static Object callOpt(Object o, String method, Object... args) { return callOpt_withVarargs(o, method, args); } static Object getOptMC(String field) { return getOpt(mc(), field); } static B mapGet(Map map, A a) { return map == null || a == null ? null : map.get(a); } static B mapGet(A a, Map map) { return map == null || a == null ? null : map.get(a); } 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; } // undefined color, seems to be all black in practice // This is without alpha? static BufferedImage newBufferedImage(int w, int h) { return new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); } static BufferedImage newBufferedImage(int w, int h, RGB rgb) { return newBufferedImage(w, h, rgb.getColor()); } static BufferedImage newBufferedImage(int w, int h, Color color) { BufferedImage img = newBufferedImage(w, h); Graphics2D g = img.createGraphics(); g.setColor(or(color, Color.white)); g.fillRect(0, 0, w, h); return img; } static BufferedImage newBufferedImage(Pt p, Color color) { return newBufferedImage(p.x, p.y, color); } // This one is with alpha... static BufferedImage newBufferedImage(int w, int h, int[] pixels) { return intArrayToBufferedImage(pixels, w, h); } static boolean loadBufferedImage_useImageCache = true; static BufferedImage loadBufferedImage(String snippetIDOrURLOrFile) { try { ping(); if (snippetIDOrURLOrFile == null) return null; if (isURL(snippetIDOrURLOrFile)) return imageIO_readURL(snippetIDOrURLOrFile); if (isSnippetID(snippetIDOrURLOrFile)) { String snippetID = "" + parseSnippetID(snippetIDOrURLOrFile); IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return loadBufferedImage(rl.loadLibrary(snippetID)); File dir = imageSnippetsCacheDir(); if (loadBufferedImage_useImageCache) { dir.mkdirs(); File file = new File(dir, snippetID + ".png"); if (file.exists() && file.length() != 0) try { return ImageIO.read(file); } catch (Throwable e) { e.printStackTrace(); // fall back to loading from sourceforge } } String imageURL = snippetImageURL_http(snippetID); print("Loading image: " + imageURL); BufferedImage image = imageIO_readURL(imageURL); if (loadBufferedImage_useImageCache) { File tempFile = new File(dir, snippetID + ".tmp." + System.currentTimeMillis()); ImageIO.write(image, "png", tempFile); tempFile.renameTo(new File(dir, snippetID + ".png")); //Log.info("Cached image."); } //Log.info("Loaded image."); return image; } else return loadBufferedImage(new File(snippetIDOrURLOrFile)); } catch (Exception __e) { throw rethrow(__e); } } static BufferedImage loadBufferedImage(File file) { return loadBufferedImageFile(file); } static BufferedImage toBufferedImageOpt(Object o) { if (o instanceof BufferedImage) return ((BufferedImage) o); if (o instanceof Image) return copyImage((Image) o); if (o instanceof MakesBufferedImage) return ((MakesBufferedImage) o).getBufferedImage(); String c = getClassName(o); // Keep this because it also works on imported objects if (eqOneOf(c, "main$BWImage", "main$RGBImage")) return (BufferedImage) call(o, "getBufferedImage"); return null; } static Boolean grabbableIntPixels_succeeded; static boolean grabbableIntPixels_enable = true; static GrabbableIntPixels grabbableIntPixels(BufferedImage img) { if (img == null || !grabbableIntPixels_enable) return null; try { var result = grabbableIntPixels_impl(img); grabbableIntPixels_succeeded = result != null; return result; } catch (Throwable _e) { grabbableIntPixels_succeeded = false; throw rethrow(_e); } } static GrabbableIntPixels grabbableIntPixels_impl(BufferedImage img) { Raster raster = img.getRaster(); SampleModel _sampleModel = raster.getSampleModel(); if (!(_sampleModel instanceof SinglePixelPackedSampleModel)) return null; SinglePixelPackedSampleModel sampleModel = (SinglePixelPackedSampleModel) _sampleModel; DataBufferInt dataBuffer = (DataBufferInt) (raster.getDataBuffer()); assertEquals(1, dataBuffer.getNumBanks()); assertEquals(DataBuffer.TYPE_INT, dataBuffer.getDataType()); // Let's at this point assume the raster data is what we // think it is... (TODO: test on unusual platforms) int w = img.getWidth(), h = img.getHeight(); int scanlineStride = sampleModel.getScanlineStride(); int[] pixels = dataBuffer.getData(); int offset = dataBuffer.getOffset(); int translateX = raster.getSampleModelTranslateX(); int translateY = raster.getSampleModelTranslateY(); offset += -translateX-translateY*scanlineStride; return new GrabbableIntPixels(pixels, w, h, offset, scanlineStride); } static float clamp(float x, float a, float b) { return x < a ? a : x > b ? b : x; } static double clamp(double x, double a, double b) { return x < a ? a : x > b ? b : x; } static int clamp(int x, int a, int b) { return x < a ? a : x > b ? b : x; } static long clamp(long x, long a, long b) { return x < a ? a : x > b ? b : x; } static BetterThreadLocal pingSource_tl_var = new BetterThreadLocal() { @Override public PingSource initialValue() { return ping_v3_pingSourceMaker().get(); } }; static BetterThreadLocal pingSource_tl() { return pingSource_tl_var; } static IntRange intRange(int start, int end) { return new IntRange(start, end); } static int blend(int x, int y, double yish) { double xish = 1-yish; return (int) (x*xish+y*yish); } static double blend(double x, double y, double yish) { double xish = 1-yish; return x*xish+y*yish; } static byte clampToUByte(long l) { return (byte) clamp(l, 0, 255); } static int zeroToOneToZeroTo255(double brightness) { return iround(brightness*255.0); } // undefined color, seems to be all black in practice static BufferedImage newGrayBufferedImage(int w, int h) { return new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY); } static BufferedImage newGrayBufferedImage(int w, int h, byte[] pixels) { return byteArrayToGrayBufferedImage(pixels, w, h); } static void addToContainer(Container a, Component... b) { if (a == null) return; { swing(() -> { for (Component c : unnullForIteration(b)) if (c != null) a.add(c); }); } } static int firstIntFromLong(long l) { return (int) (l >> 32); } static int secondIntFromLong(long l) { return (int) l; } static long intPairToLong(IntPair p) { return p == null ? 0 : (((long) p.a) << 32) | (((long) p.b) & 0xFFFFFFFF); } static long intPairToLong(int a, int b) { return (((long) a) << 32) | (((long) b) & 0xFFFFFFFF); } static Iterator emptyIterator() { return Collections.emptyIterator(); } static long getFileSize(String path) { return path == null ? 0 : new File(path).length(); } static long getFileSize(File f) { return f == null ? 0 : f.length(); } static int indexOf(List l, A a, int startIndex) { if (l == null) return -1; int n = l(l); for (int i = startIndex; i < n; i++) if (eq(l.get(i), a)) return i; return -1; } static int indexOf(List l, int startIndex, A a) { return indexOf(l, a, startIndex); } static int indexOf(List l, A a) { if (l == null) return -1; return l.indexOf(a); } static int indexOf(String a, String b) { return a == null || b == null ? -1 : a.indexOf(b); } static int indexOf(String a, String b, int i) { return a == null || b == null ? -1 : a.indexOf(b, i); } static int indexOf(String a, char b) { return a == null ? -1 : a.indexOf(b); } static int indexOf(String a, int i, char b) { return indexOf(a, b, i); } static int indexOf(String a, char b, int i) { return a == null ? -1 : a.indexOf(b, i); } static int indexOf(String a, int i, String b) { return a == null || b == null ? -1 : a.indexOf(b, i); } static int indexOf(A[] x, A a) { int n = l(x); for (int i = 0; i < n; i++) if (eq(x[i], a)) return i; return -1; } static int indexOf(Iterable l, A a) { if (l == null) return -1; int i = 0; for (A x : l) { if (eq(x, a)) return i; i++; } return -1; } static String colonCombine(Object... l) { return joinNemptiesWithColon(flattenCollectionsAndArrays(ll(l))); } 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 BWImage toBW(RGBImage img) { return img == null ? null : new BWImage(img); } static void smartSet(Field f, Object o, Object value) throws Exception { try { f.set(o, value); } catch (Exception e) { Class type = f.getType(); // take care of common case (long to int) if (type == int.class && value instanceof Long) { f.set(o, ((Long) value).intValue()); return; } if (type == boolean.class && value instanceof String) { f.set(o, isTrueOrYes(((String) value))); return; } if (type == LinkedHashMap.class && value instanceof Map) { f.set(o, asLinkedHashMap((Map) value)); return; } throw e; } } static Runnable toRunnable(final Object o) { if (o == null) return null; if (o instanceof Runnable) return (Runnable) o; if (o instanceof String) throw fail("callF_legacy"); return new Runnable() { public void run() { try { callF(o) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(o)"; }}; } static Object swing(Object f) { return swingAndWait(f); } static void swing(Runnable f) { swingAndWait(f); } static A swing(F0 f) { return (A) swingAndWait(f); } static A swing(IF0 f) { return (A) swingAndWait(f); } static ChangeListener changeListener(final Object r) { return new ChangeListener() { public void stateChanged(ChangeEvent e) { pcallF(r); } }; } static ItemListener itemListener(final Object r) { return new ItemListener() { public void itemStateChanged(ItemEvent e) { pcallF(r); } }; } static void onUpdate(JComponent c, Runnable r) { onUpdate(c, (Object) r); } static void onUpdate(JTextComponent c, IVF1 r) { if (c == null || r == null) return; c.getDocument().addDocumentListener(runnableToDocumentListener(() -> r.get(c.getText()))); } // legacy signature static void onUpdate(JComponent c, Object r) { if (c instanceof JTextComponent) ((JTextComponent) c).getDocument().addDocumentListener(runnableToDocumentListener(toRunnable(r))); else if (c instanceof ItemSelectable) // JCheckBox and others ((ItemSelectable) c).addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { pcallF(r); } }); else if (c instanceof JSpinner) onChange(((JSpinner) c), r); else print("Warning: onUpdate doesn't know " + getClassName(c)); } static void onUpdate(List l, Object r) { for (JComponent c : l) onUpdate(c, r); } static void addActionListener(JTextField tf, final Runnable action) { onEnter(tf, action); } static void addActionListener(final JComboBox cb, final Runnable action) { if (cb != null) { swing(() -> { cb.addActionListener(actionListener(action)); }); } } static void addActionListener(final AbstractButton b, final Runnable action) { if (b != null) { swing(() -> { b.addActionListener(actionListener(action)); }); } } static A getSelectedItem_typed(JList l) { return swing(() -> l.getSelectedValue()); } static A getSelectedItem_typed(JComboBox cb) { return swing(() -> (A) cb.getSelectedItem()); } static boolean isEditableComboBox(final JComboBox cb) { return cb != null && swing(new F0() { public Boolean get() { try { return cb.isEditable(); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return cb.isEditable();"; }}); } static JTextField textFieldFromComboBox(JComboBox cb) { return (JTextField) cb.getEditor().getEditorComponent(); } static JComboBox onSelectedItem(final JComboBox cb, final VF1 f) { addActionListener(cb, new Runnable() { public void run() { try { pcallF(f, selectedItem(cb)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pcallF(f, selectedItem(cb))"; }}); return cb; } static JComboBox onSelectedItem(final JComboBox cb, IVF1 f) { addActionListener(cb, new Runnable() { public void run() { try { pcallF(f, selectedItem(cb)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pcallF(f, selectedItem(cb))"; }}); return cb; } static Object swingCall(final Object o, final String method, final Object... args) { return swing(new F0() { public Object get() { try { return call(o, method, args); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return call(o, method, args);"; }}); } static A addComponents(A c, Collection components) { if (nempty(components)) { swing(() -> { for (Component comp : components) if (comp != null) c.add(comp); revalidate(c); }); } return c; } static A addComponents(A c, Component... components) { return addComponents(c, asList(components)); } static List wrapArrayAsList(A[] a) { return a == null ? null : Arrays.asList(a); } static Rect toRect(Rectangle r) { return r == null ? null : new Rect(r); } static Rect toRect(RectangularShape r) { return r == null ? null : toRect(r.getBounds()); } static Rect toRect(DoubleRect r) { if (r == null) return null; int x = iround(r.x), y = iround(r.y); return new Rect(x, y, iround(r.x2())-x, iround(r.y2())-y); } static Rect toRect(Rect r) { return r; } static String trim(String s) { return s == null ? null : s.trim(); } static String trim(StringBuilder buf) { return buf.toString().trim(); } static String trim(StringBuffer buf) { return buf.toString().trim(); } static Map singular_specials = litmap( "children", "child", "images", "image", "chess", "chess"); static Set singular_specials2 = litciset("time", "machine", "line", "rule"); static String singular(String s) { if (s == null) return null; { String __1 = singular_specials.get(s); if (!empty(__1)) return __1; } //try answer hippoSingulars().get(lower(s)); if (singular_specials2.contains(dropSuffix("s", afterLastSpace(s)))) return dropSuffix("s", s); if (s.endsWith("ness")) return s; if (s.endsWith("ges")) return dropSuffix("s", s); if (endsWith(s, "bases")) return dropLast(s); s = dropSuffix("es", s); s = dropSuffix("s", s); return s; } static Set getPlural_specials = litciset("sheep", "fish"); static String getPlural(String s) { if (contains(getPlural_specials, s)) return s; if (ewic(s, "y")) return dropSuffixIgnoreCase("y", s) + "ies"; if (ewicOneOf(s, "ss", "ch")) return s + "es"; if (ewic(s, "s")) return s; return s + "s"; } static Color colorFromRGBA(int rgba) { return new Color(rgba, true); } // binary legacy signature static Object[] toObjectArray(Collection c) { return toObjectArray((Iterable) c); } static Object[] toObjectArray(Iterable c) { List l = asList(c); return l.toArray(new Object[l.size()]); } static A[] arrayOfType(Class type, int n) { return makeArray(type, n); } static A[] arrayOfType(int n, Class type) { return arrayOfType(type, n); } static A liftLast(List l) { if (empty(l)) return null; int i = l(l)-1; A a = l.get(i); l.remove(i); return a; } static List liftLast(int n, List l) { int i = l(l)-n; List part = cloneSubList(l, i); removeSubList(l, i); return part; } static List cloneSubList(List l, int startIndex, int endIndex) { return newSubList(l, startIndex, endIndex); } static List cloneSubList(List l, int startIndex) { return newSubList(l, startIndex); } static void removeSubList(List l, int from, int to) { if (l != null) subList(l, from, to).clear(); } static void removeSubList(List l, int from) { if (l != null) subList(l, from).clear(); } static boolean hasJPEGExtension(File f) { return ewicOneOf(fileName(f), ".jpg", ".jpeg"); } static void saveJPG(BufferedImage img, File file) { try { if (!ImageIO.write(img, "jpeg", mkdirsFor(file))) { print("Reconstructing image for saving JPEG"); img = reconstructBufferedImage(img); if (!ImageIO.write(img, "jpeg", file)) throw fail("Couldn't write JPEG: " + file + " (" + img + ")"); } vmBus_send("wroteFile", file); } catch (Exception __e) { throw rethrow(__e); } } static void saveJPG(File file, BufferedImage img) { try { saveJPG(img, file); } catch (Exception __e) { throw rethrow(__e); } } static void savePNG(BufferedImage img, File file) { try { File tempFile = new File(file.getPath() + "_temp"); CriticalAction ca = beginCriticalAction("Save " + f2s(file)); try { ImageIO.write(img, "png", mkdirsFor(tempFile)); file.delete(); tempFile.renameTo(file); } finally { ca.done(); } } catch (Exception __e) { throw rethrow(__e); } } // gotta love convenience & program-smartness static void savePNG(String file, BufferedImage img) { savePNG(toFile(file), img); } static void savePNG(File file, BufferedImage img) { savePNG(img, file); } static void savePNG(File file, RGBImage img) { savePNG(file, img.getBufferedImage()); } static int colorToIntOpaque(Color c) { return c.getRGB() | 0xFF000000; } static Class _getClass(String name) { try { return Class.forName(name); } catch (ClassNotFoundException e) { return null; // could optimize this } } static Class _getClass(Object o) { return o == null ? null : o instanceof Class ? (Class) o : o.getClass(); } static Class _getClass(Object realm, String name) { try { return classLoaderForObject(realm).loadClass(classNameToVM(name)); } catch (ClassNotFoundException e) { return null; // could optimize this } } static A nuInstance(Class c) { return nuEmptyObject(c); } static void listSet(List l, int i, A a, A emptyElement) { if (i < 0) return; while (i >= l(l)) l.add(emptyElement); l.set(i, a); } static void listSet(List l, int i, A a) { listSet(l, i, a, null); } // Use like this: printVars_str(+x, +y); // Or: printVars("bla", +x); // Or: printVars bla(+x); static void printVars_str(Object... params) { print(renderVars_str(params)); } static Rect pointsRect(int x1, int y1, int x2, int y2) { return new Rect(x1, y1, x2-x1, y2-y1); } static IterableIterator emptyItIt() { return emptyIterableIterator(); } // f: func -> A | endMarker static IterableIterator iff(Object f) { return iteratorFromFunction_withEndMarker(f); } // can't use type parameter because end marker static IterableIterator iff(F0 f) { return iteratorFromFunction_withEndMarker(f); } static IterableIterator iff(IF0 f) { return iteratorFromFunction_withEndMarker(f); } static Object endMarker() { return iteratorFromFunction_endMarker; } static class mapI_if1_It extends IterableIterator { IF1 f; Iterator i; mapI_if1_It() {} mapI_if1_It(IF1 f, Iterator i) { this.i = i; this.f = f;} public boolean hasNext() { return i.hasNext(); } public B next() { return f.get(i.next()); } public String toString() { return formatFunctionCall("mapI_if1", f, i); } } static IterableIterator mapI_if1(IF1 f, Iterable i) { return new mapI_if1_It(f, i.iterator()); } static IterableIterator mapI_if1(Iterable i, IF1 f) { return mapI_if1(f, i); } static IterableIterator iteratorFromFunction_f0(final F0 f) { class IFF2 extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } }; return new IFF2(); } static IterableIterator iteratorFromFunction_if0(final IF0 f) { class IFF2 extends IterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } }; return new IFF2(); } // binary legacy syntax static String formatFunctionCall(String fname, Object... args) { return formatFunctionCall((Object) fname, args); } static String formatFunctionCall(Object fname, Object... args) { return fname + "(" + joinWithComma(allToString(args)) + ")"; } static String formatFunctionCall(String fname, Iterable args) { return formatFunctionCall((Object) fname, args); } static String formatFunctionCall(Object fname, Iterable args) { return formatFunctionCall(fname, toObjectArray(args)); } static IMeta toIMeta(Object o) { return initIMeta(o); } static IMeta initIMeta(Object o) { if (o == null) return null; if (o instanceof IMeta) return ((IMeta) o); if (o instanceof JComponent) return initMetaOfJComponent((JComponent) o); // This is not really used. Try to use BufferedImageWithMeta instead if (o instanceof BufferedImage) return optCast(IMeta.class, ((BufferedImage) o).getProperty("meta")); return null; } static Map makeObjectMetaMap() { //ret synchroLinkedHashMap(); return new CompactHashMap(); } static IAutoCloseableF0 tempMetaMutex(IMeta o) { return o == null ? null : o._tempMetaMutex(); } static void _close(AutoCloseable c) { if (c != null) try { c.close(); } catch (Throwable e) { // Some classes stupidly throw an exception on double-closing if (c instanceof javax.imageio.stream.ImageOutputStream) return; else throw rethrow(e); } } static void syncMapPut2(Map map, A key, B value) { if (map != null && key != null) synchronized(collectionMutex(map)) { if (value != null) map.put(key, value); else map.remove(key); } } static String localDateWithSeconds(long time) { SimpleDateFormat format = simpleDateFormat_local("yyyy/MM/dd HH:mm:ss"); return format.format(time); } static String localDateWithSeconds() { return localDateWithSeconds(now()); } static BigInteger bigint(String s) { return new BigInteger(s); } static BigInteger bigint(long l) { return BigInteger.valueOf(l); } static long clockToSysTimeDiff() { return sysNow()-now(); } static Set asSet(Object[] array) { HashSet set = new HashSet(); for (Object o : array) if (o != null) set.add(o); return set; } static Set asSet(String[] array) { TreeSet set = new TreeSet(); for (String o : array) if (o != null) set.add(o); return set; } static Set asSet(Iterable l) { if (l instanceof Set) return (Set) l; HashSet set = new HashSet(); for (A o : unnull(l)) if (o != null) set.add(o); return set; } static Complex complex(double re, double im) { return new Complex(re, im); } static Complex complex(double re) { return new Complex(re, 0.0); } static Complex complex(double[] reIm) { if (empty(reIm)) return null; if (l(reIm) != 2) throw fail("Need 2 doubles to make complex number"); return complex(reIm[0], reIm[1]); } static Rect intersectRects(Rect a, Rect b) { int x = max(a.x, b.x), y = max(a.y, b.y); int x2 = min(a.x+a.w, b.x+b.w), y2 = min(a.y+a.h, b.y+b.h); return new Rect(x, y, x2-x, y2-y); } static Rect intersectRects(Rect a, int x1, int y1, int w, int h) { if (a == null || a.x >= x1 && a.y >= y1 && a.x2() < x1+w && a.y2() < y1+h) return a; return rectFromPoints( max(a.x, x1), max(a.y, y1), min(a.x2(), x1+w), min(a.y2(), y1+h)); } static boolean rectEmpty(Rect r) { return r == null || r.w <= 0 || r.h <= 0; } static Map newDangerousWeakHashMap() { return _registerDangerousWeakMap(synchroMap(new WeakHashMap())); } // initFunction: voidfunc(Map) - is called initially, and after clearing the map static Map newDangerousWeakHashMap(Object initFunction) { return _registerDangerousWeakMap(synchroMap(new WeakHashMap()), initFunction); } static Object invokeMethod(Method m, Object o, Object... args) { try { try { return m.invoke(o, args); } catch (InvocationTargetException e) { throw rethrow(getExceptionCause(e)); } catch (IllegalArgumentException e) { throw new IllegalArgumentException(e.getMessage() + " - was calling: " + m + ", args: " + joinWithSpace(classNames(args))); } } catch (Exception __e) { throw rethrow(__e); } } static boolean call_checkArgs(Method m, Object[] args, boolean debug) { Class[] types = m.getParameterTypes(); if (types.length != l(args)) { if (debug) print("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) { Object arg = args[i]; if (!(arg == null ? !types[i].isPrimitive() : isInstanceX(types[i], arg))) { if (debug) print("Bad parameter " + i + ": " + arg + " vs " + types[i]); return false; } } return true; } // action = runnable or method name (old) static void onUpdateAndNow(JComponent c, final Object r) { onUpdate(c, r); callF(r); } static void onUpdateAndNow(JTextComponent c, IVF1 r) { swing(() -> { if (c == null) return; onUpdate(c, r); callF(r, c.getText()); }); } static void onUpdateAndNow(List l, Object r) { for (JComponent c : l) onUpdate(c, r); callF(r); } static int[] emptyIntArray_a = new int[0]; static int[] emptyIntArray() { return emptyIntArray_a; } static char[] emptyCharArray = new char[0]; static char[] emptyCharArray() { return emptyCharArray; } static double[] emptyDoubleArray = new double[0]; static double[] emptyDoubleArray() { return emptyDoubleArray; } static Map emptyMap() { return new HashMap(); } static Object[] emptyObjectArray_a = new Object[0]; static Object[] emptyObjectArray() { return emptyObjectArray_a; } static Symbol emptySymbol_value; static Symbol emptySymbol() { if (emptySymbol_value == null) emptySymbol_value = symbol(""); return emptySymbol_value; } static boolean setVar_trueIfChanged(IVar v, A value) { if (v == null) return false; synchronized(v) { if (eq(v.get(), value)) return false; v.set(value); return true; } } // We even allow overriding existing interface implementations static A lookupDynamicInterface(Class intrface, Object o) { if (o instanceof DynamicObject) { A a = (A) (mapGet((Map) ((DynamicObject) o).fieldValues, intrface)); if (a != null) return a; } if (isInstance(intrface, o)) return (A) o; return null; } static A lookupDynamicInterface(Object o, Class intrface) { return lookupDynamicInterface(intrface, o); } static String rep(int n, char c) { return repeat(c, n); } static String rep(char c, int n) { return repeat(c, n); } static List rep(A a, int n) { return repeat(a, n); } static List rep(int n, A a) { return repeat(n, a); } static String decimalFormatEnglish(String format, double d) { return decimalFormatEnglish(format).format(d); } static java.text.DecimalFormat decimalFormatEnglish(String format) { return new java.text.DecimalFormat(format, new java.text.DecimalFormatSymbols(Locale.ENGLISH)); } static List flattenIterablesAndArrays(Iterable a) { List l = new ArrayList(); for (Object x : a) if (x instanceof Iterable) l.addAll(flattenIterablesAndArrays((Iterable) x)); else if (x instanceof Iterator) l.addAll(flattenIterablesAndArrays(asList((Iterator) x))); else if (x instanceof Object[]) l.addAll(flattenIterablesAndArrays(asList((Object[]) x))); else l.add(x); return l; } static IterableIterator countIterator(int b) { return countIterator(0, b); } static IterableIterator countIterator(int a, int b) { return countIterator_exclusive(a, b); } static IterableIterator countIterator(int b, IF1 f) { return countIterator(0, b, f); } static IterableIterator countIterator(int a, int b, IF1 f) { return countIterator_exclusive(a, b, f); } static IterableIterator countIterator(int a, int b, int step) { return countIterator_exclusive_step(a, b, step); } static IterableIterator countIterator(double a, double b, double step, IF1 f) { return countIterator_exclusive_step(a, b, step, f); } static IterableIterator countIterator(double a, double b, double step) { return countIterator_exclusive_step(a, b, step); } static IterableIterator countIterator(IF1 f, double a, double b, double step) { return countIterator(a, b, step, f); } static IterableIterator countIterator(IF1 f, int b) { return countIterator(f, 0, b); } static IterableIterator countIterator(IF1 f, int a, int b) { return countIterator_exclusive(a, b, f); } static IterableIterator countIterator(List l) { return countIterator(l(l)); } static A safeGet(List l, int i) { return i >= 0 && i < l(l) ? l.get(i) : null; } static String className(Object o) { return getClassName(o); } // requires ugly casting when used (O -> A) static Object iteratorFromFunction_endMarker = new Object(); // f: func -> A | endMarker static IterableIterator iteratorFromFunction_withEndMarker(final Object f) { class IFF extends IterableIterator { A a; boolean have, done; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; have = false; return _a; } void getNext() { if (done || have) return; Object o = callF(f); if (o == iteratorFromFunction_endMarker) { done = true; return; } a = (A) o; have = true; } }; return new IFF(); } // optimized version for F0 argument; TODO: do same for IF0 static IterableIterator iteratorFromFunction_withEndMarker(final F0 f) { return iteratorFromFunction_withEndMarker_f0(f); } static ThreadLocal print_byThread() { synchronized(print_byThread_lock) { if (print_byThread == null) print_byThread = new ThreadLocal(); } return print_byThread; } // f can return false to suppress regular printing // call print_raw within f to actually print something static AutoCloseable tempInterceptPrint(F1 f) { return tempSetThreadLocal(print_byThread(), f); } static ThreadLocal assertVerbose_value = new ThreadLocal(); static void assertVerbose(boolean b) { assertVerbose_value.set(b); } static boolean assertVerbose() { return isTrue(assertVerbose_value.get()); } static A assertEqualsVerbose(Object x, A y) { assertEqualsVerbose((String) null, x, y); return y; } // x = expected, y = actual static A assertEqualsVerbose(String msg, Object x, A y) { if (!eq(x, y)) { throw fail((nempty(msg) ? msg + ": " : "") + "expected: "+ x + ", got: " + y); } else print("OK" + (empty(msg) ? "" : " " + msg) + ": " + /*sfu*/(x)); return y; } static String nullIfEmpty(String s) { return isEmpty(s) ? null : s; } static Map nullIfEmpty(Map map) { return isEmpty(map) ? null : map; } static List nullIfEmpty(List l) { return isEmpty(l) ? null : l; } static void setMetaAndVerify(Object o, Object key, Object value) { setMeta(o, key, value); assertSame(() -> "setMeta failed (class: " + className(o) + ", key: " + key + ")", value, metaGet(o, key)); } static void setMetaAndVerify(IMeta o, Object key, Object value) { setMeta(o, key, value); assertSame(() -> "setMeta failed (class: " + className(o) + ", key: " + key + ")", value, metaGet(o, key)); } static A setMetaSrcUnlessSame(Object b, A a, Object src) { if (a != b) setMeta(a, "src", src); return a; } // binary legacy signature static int widthForHeight(int w, int h, int newHeight) { return iround(newHeight*doubleRatio(w, h)); } static int widthForHeight(double w, double h, int newHeight) { return iround(newHeight*doubleRatio(w, h)); } static int widthForHeight(WidthAndHeight img, int newHeight) { return widthForHeight(img.getWidth(), img.getHeight(), newHeight); } static int heightForWidth(int w, int h, int newWidth) { return iround(newWidth*doubleRatio(h, w)); } static int heightForWidth(BufferedImage img, int newWidth) { return heightForWidth(img.getWidth(), img.getHeight(), newWidth); } static int heightForWidth(int newWidth, BufferedImage img) { return heightForWidth(img, newWidth); } static int heightForWidth(WidthAndHeight img, int newWidth) { return heightForWidth(img.getWidth(), img.getHeight(), newWidth); } static String mainClassNameForClassLoader(ClassLoader cl) { return or((String) callOpt(cl, "mainClassName"), "main"); } static void assertFalse(Object o) { if (!(eq(o, false) /*|| isFalse(pcallF(o))*/)) throw fail(str(o)); } static boolean assertFalse(boolean b) { if (b) throw fail("oops"); return b; } static boolean assertFalse(String msg, boolean b) { if (b) throw fail(msg); return b; } static boolean _inCore() { return false; } static Class __javax; static Class getJavaX() { try { return __javax; } catch (Exception __e) { throw rethrow(__e); } } static void __setJavaX(Class j) { __javax = j; _onJavaXSet(); } static int isAndroid_flag; static boolean isAndroid() { if (isAndroid_flag == 0) isAndroid_flag = System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0 ? 1 : -1; return isAndroid_flag > 0; } static Object call(Object o) { return callF(o); } // varargs assignment fixer for a single string array argument static Object call(Object o, String method, String[] arg) { return call(o, method, new Object[] {arg}); } static Object call(Object o, String method, Object... args) { //ret call_cached(o, method, args); return call_withVarargs(o, method, args); } static String quote(Object o) { if (o == null) return "null"; return quote(str(o)); } static String quote(String s) { if (s == null) return "null"; StringBuilder out = new StringBuilder((int) (l(s)*1.5+2)); quote_impl(s, out); return out.toString(); } static void quote_impl(String s, StringBuilder out) { out.append('"'); int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c == '\\' || c == '"') out.append('\\').append(c); else if (c == '\r') out.append("\\r"); else if (c == '\n') out.append("\\n"); else if (c == '\t') out.append("\\t"); else if (c == '\0') out.append("\\0"); else out.append(c); } out.append('"'); } static List hotwire_copyOver_after = synchroList(); static void hotwire_copyOver(Class c) { // TODO: make a mechanism for making such "inheritable" fields for (String field : ll("print_log", "print_silent", "androidContext", "_userHome")) setOptIfNotNull(c, field, getOpt(mc(), field)); setOptIfNotNull(c, "mainBot" , getMainBot()); setOpt(c, "creator_class" , new WeakReference(mc())); pcallFAll(hotwire_copyOver_after, c); } static String[] dropFirst(int n, String[] a) { return drop(n, a); } static String[] dropFirst(String[] a) { return drop(1, a); } static Object[] dropFirst(Object[] a) { return drop(1, a); } static List dropFirst(List l) { return dropFirst(1, l); } static List dropFirst(int n, Iterable i) { return dropFirst(n, toList(i)); } static List dropFirst(Iterable i) { return dropFirst(toList(i)); } static List dropFirst(int n, List l) { return n <= 0 ? l : new ArrayList(l.subList(Math.min(n, l.size()), l.size())); } static List dropFirst(List l, int n) { return dropFirst(n, l); } static String dropFirst(int n, String s) { return substring(s, n); } static String dropFirst(String s, int n) { return substring(s, n); } static String dropFirst(String s) { return substring(s, 1); } static Chain dropFirst(Chain c) { return c == null ? null : c.next; } static int indexOfNonDigit(String s) { int n = l(s); for (int i = 0; i < n; i++) if (!isDigit(s.charAt(i))) return i; return -1; } static String shortenClassName(String name) { if (name == null) return null; int i = lastIndexOf(name, "$"); if (i < 0) i = lastIndexOf(name, "."); return i < 0 ? name : substring(name, i+1); } // BREAKING CHANGE! // Also NOTE: Iterators of these sync-wrapped collections // after generally NOT thread-safe! // TODO: change that? static Set synchroLinkedHashSet() { return synchronizedSet(new CompactLinkedHashSet()); } static boolean endsWithLetterOrDigit(String s) { return s != null && s.length() > 0 && Character.isLetterOrDigit(s.charAt(s.length()-1)); } static A or(A a, A b) { return a != null ? a : b; } // PersistableThrowable doesn't hold GC-disturbing class references in backtrace static volatile PersistableThrowable lastException_lastException; static PersistableThrowable lastException() { return lastException_lastException; } static void lastException(Throwable e) { lastException_lastException = persistableThrowable(e); } static String hideCredentials(URL url) { return url == null ? null : hideCredentials(str(url)); } static String hideCredentials(String url) { try { if (startsWithOneOf(url, "http://", "https://") && isAGIBlueDomain(hostNameFromURL(url))) return url; } catch (Throwable e) { print("HideCredentials", e); } return url.replaceAll("([&?])(_pass|key|cookie)=[^&\\s\"]*", "$1$2="); } static String hideCredentials(Object o) { return hideCredentials(str(o)); } static void rotateStringBuffer(StringBuffer buf, int max) { try { if (buf == null) return; synchronized(buf) { if (buf.length() <= max) return; try { int newLength = max/2; int ofs = buf.length()-newLength; String newString = buf.substring(ofs); buf.setLength(0); buf.append("[...] ").append(newString); } catch (Exception e) { buf.setLength(0); } buf.trimToSize(); } } catch (Exception __e) { throw rethrow(__e); } } static void rotateStringBuilder(StringBuilder buf, int max) { try { if (buf == null) return; synchronized(buf) { if (buf.length() <= max) return; try { int newLength = max/2; int ofs = buf.length()-newLength; String newString = buf.substring(ofs); buf.setLength(0); buf.append("[...] ").append(newString); } catch (Exception e) { buf.setLength(0); } buf.trimToSize(); } } catch (Exception __e) { throw rethrow(__e); } } static Object vmBus_wrapArgs(Object... args) { return empty(args) ? null : l(args) == 1 ? args[0] : args; } static void pcallFAll_minimalExceptionHandling(Collection l, Object... args) { if (l != null) for (Object f : cloneList(l)) { ping(); pcallF_minimalExceptionHandling(f, args); } } static void pcallFAll_minimalExceptionHandling(Iterator it, Object... args) { while (it.hasNext()) { ping(); pcallF_minimalExceptionHandling(it.next(), args); } } static Set vm_busListeners_live_cache; static Set vm_busListeners_live() { if (vm_busListeners_live_cache == null) vm_busListeners_live_cache = vm_busListeners_live_load(); return vm_busListeners_live_cache;} static Set vm_busListeners_live_load() { return vm_generalIdentityHashSet("busListeners"); } static Map vm_busListenersByMessage_live_cache; static Map vm_busListenersByMessage_live() { if (vm_busListenersByMessage_live_cache == null) vm_busListenersByMessage_live_cache = vm_busListenersByMessage_live_load(); return vm_busListenersByMessage_live_cache;} static Map vm_busListenersByMessage_live_load() { return vm_generalHashMap("busListenersByMessage"); } static String joinStrings(String sep, Object... strings) { return joinStrings(sep, Arrays.asList(strings)); } static String joinStrings(String sep, Iterable strings) { StringBuilder buf = new StringBuilder(); for (Object o : unnull(strings)) { String s = strOrNull(o); if (nempty(s)) { if (nempty(buf)) buf.append(sep); buf.append(s); } } return str(buf); } static Map synchroMap() { return synchroHashMap(); } static Map synchroMap(Map map) { return Collections.synchronizedMap(map); } static A setDyn(A o, String key, Object value) { setDynObjectValue(o, key, value); return o; } static void setDyn(IMeta o, String key, Object value) { metaMapPut(o, key, value); } // allows null keys but not null values static B getOrCreate(Map map, A key, Class c) { try { B b = map.get(key); if (b == null) map.put(key, b = c.newInstance()); return b; } catch (Exception __e) { throw rethrow(__e); } } // f : func -> B static B getOrCreate(Map map, A key, Object f) { try { B b = map.get(key); if (b == null) map.put(key, b = (B) callF(f)); return b; } catch (Exception __e) { throw rethrow(__e); } } static B getOrCreate(IF0 f, Map map, A key) { return getOrCreate(map, key, f); } static B getOrCreate(Map map, A key, IF0 f) { B b = map.get(key); if (b == null) map.put(key, b = f.get()); return b; } static B getOrCreate(Class c, Map map, A key) { return getOrCreate(map, key, c); } // TODO: JDK 17!! ?? No! Yes? Yes!! static Object collectionMutex(List l) { return l; } static Object collectionMutex(Object o) { if (o instanceof List) return o; // TODO: actually use our own maps so we can get the mutex properly String c = className(o); return o; } static BufferedImage whiteImage(int w, int h) { return newBufferedImage(w, h, Color.white); } static BufferedImage whiteImage(int size) { return whiteImage(size, size); } static BufferedImage whiteImage(WidthAndHeight size) { return whiteImage(size.getWidth(), size.getHeight()); } static CloseableIterableIterator linesFromFile(File f) { return linesFromFile(f, null); } static CloseableIterableIterator linesFromFile(File f, IResourceHolder resourceHolder) { try { if (!f.exists()) return emptyCloseableIterableIterator(); if (ewic(f.getName(), ".gz")) return linesFromReader(utf8bufferedReader(newGZIPInputStream(f)), resourceHolder); return linesFromReader(utf8bufferedReader(f), resourceHolder); } catch (Exception __e) { throw rethrow(__e); } } static CloseableIterableIterator linesFromFile(String path) { return linesFromFile(path, null); } static CloseableIterableIterator linesFromFile(String path, IResourceHolder resourceHolder) { return linesFromFile(newFile(path), resourceHolder); } 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(IMultiMap mm) { return mm.keySet(); } static Comparator caseInsensitiveComparator() { return betterCIComparator(); } static Map newWeakHashMap() { return _registerWeakMap(synchroMap(new WeakHashMap())); } static void newPing() { var tl = newPing_actionTL(); Runnable action = tl == null ? null : tl.get(); { if (action != null) action.run(); } } // TODO: test if android complains about this static boolean isAWTThread() { if (isAndroid()) return false; if (isHeadless()) return false; return isAWTThread_awt(); } static boolean isAWTThread_awt() { return SwingUtilities.isEventDispatchThread(); } static boolean isTrue(Object o) { if (o instanceof Boolean) return ((Boolean) o).booleanValue(); if (o == null) return false; if (o instanceof ThreadLocal) // TODO: remove this return isTrue(((ThreadLocal) o).get()); throw fail(getClassName(o)); } static boolean isTrue(Boolean b) { return b != null && b.booleanValue(); } static void failIfUnlicensed() { assertTrue("license off", licensed()); } static Thread currentThread() { return Thread.currentThread(); } static String formatWithThousandsSeparator(long l) { return NumberFormat.getInstance(new Locale("en_US")).format(l); } static double parseDouble(String s) { return empty(s) ? 0.0 : Double.parseDouble(s); } static Set> _entrySet(Map map) { return map == null ? Collections.EMPTY_SET : map.entrySet(); } static byte[] bytesFromHex(String s) { return hexToBytes(s); } static String dropPrefix(String prefix, String s) { return s == null ? null : s.startsWith(prefix) ? s.substring(l(prefix)) : s; } // o is either a map already (string->object) or an arbitrary object, // in which case its fields are converted into a map. static Map objectToMap(Object o) { try { if (o instanceof Map) return (Map) o; TreeMap map = new TreeMap(); Class c = o.getClass(); while (c != Object.class) { Field[] fields = c.getDeclaredFields(); for (final Field field : fields) { if ((field.getModifiers() & Modifier.STATIC) != 0) continue; field.setAccessible(true); final Object value = field.get(o); if (value != null) map.put(field.getName(), value); } c = c.getSuperclass(); } // XXX NEW - hopefully this doesn't break anything if (o instanceof DynamicObject) putAll(map, ((DynamicObject) o).fieldValues); return map; } catch (Exception __e) { throw rethrow(__e); } } // same for a collection (convert each element) static List> objectToMap(Iterable l) { if (l == null) return null; List x = new ArrayList(); for (Object o : l) x.add(objectToMap(o)); return x; } static Field setOpt_findField(Class c, String field) { HashMap map; synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } return map.get(field); } static void setOpt(Object o, String field, Object value) { try { if (o == null) return; Class c = o.getClass(); HashMap map; if (getOpt_cache == null) map = getOpt_makeCache(c); // in class init else synchronized(getOpt_cache) { map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); } if (map == getOpt_special) { if (o instanceof Class) { setOpt((Class) o, field, value); return; } // It's probably a subclass of Map. Use raw method. TODO: huh? setOpt_raw(o, field, value); return; } Field f = map.get(field); if (f != null) { smartSet(f, o, value); return; } // possible improvement: skip setAccessible if (o instanceof DynamicObject) { setDyn(((DynamicObject) o), field, value); return; } if (o instanceof IMeta) setDyn(((IMeta) o), field, value); } catch (Exception __e) { throw rethrow(__e); } } static void setOpt(Class c, String field, Object value) { if (c == null) return; try { Field f = setOpt_findStaticField(c, field); // TODO: optimize if (f != null) smartSet(f, null, value); } catch (Exception e) { throw new RuntimeException(e); } } static Field setOpt_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) { makeAccessible(f); return f; } _c = _c.getSuperclass(); } while (_c != null); return null; } static String[] asStringArray(Collection c) { return toStringArray(c); } static String[] asStringArray(Object o) { return toStringArray(o); } // TODO: convert to regularly cleared normal map static Map getDeclaredConstructors_cached_cache = newDangerousWeakHashMap(); static Constructor[] getDeclaredConstructors_cached(Class c) { Constructor[] ctors; synchronized(getDeclaredConstructors_cached_cache) { ctors = getDeclaredConstructors_cached_cache.get(c); if (ctors == null) { getDeclaredConstructors_cached_cache.put(c, ctors = c.getDeclaredConstructors()); for (var ctor : ctors) makeAccessible(ctor); } } return ctors; } static Object callOpt_withVarargs(Object o, String method, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Class c = (Class) o; _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(method, args); if (me == null) { // TODO: varargs return null; } if ((me.getModifiers() & Modifier.STATIC) == 0) return null; return invokeMethod(me, null, args); } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(method, args); if (me != null) return invokeMethod(me, o, args); // try varargs List methods = cache.cache.get(method); if (methods != null) methodSearch: for (Method m : methods) { { if (!(m.isVarArgs())) continue; } Object[] newArgs = massageArgsForVarArgsCall(m, args); if (newArgs != null) return invokeMethod(m, o, newArgs); } return null; } } catch (Exception __e) { throw rethrow(__e); } } //static final Map> getOpt_cache = newDangerousWeakHashMap(f getOpt_special_init); static class getOpt_Map extends WeakHashMap { getOpt_Map() { if (getOpt_special == null) getOpt_special = new HashMap(); clear(); } public void clear() { super.clear(); //print("getOpt clear"); put(Class.class, getOpt_special); put(String.class, getOpt_special); } } static final Map> getOpt_cache = _registerDangerousWeakMap(synchroMap(new getOpt_Map())); static HashMap getOpt_special; // just a marker /*static void getOpt_special_init(Map map) { map.put(Class.class, getOpt_special); map.put(S.class, getOpt_special); }*/ static Map getOpt_getFieldMap(Object o) { Class c = _getClass(o); HashMap map = getOpt_cache.get(c); if (map == null) map = getOpt_makeCache(c); return map; } static Object getOpt_cached(Object o, String field) { try { if (o == null) return null; Map map = getOpt_getFieldMap(o); if (map == getOpt_special) { if (o instanceof Class) return getOpt((Class) o, field); /*if (o instanceof S) ret getOpt(getBot((S) o), field);*/ if (o instanceof Map) return ((Map) o).get(field); } Field f = map.get(field); if (f != null) return f.get(o); if (o instanceof DynamicObject) return syncMapGet2(((DynamicObject) o).fieldValues, field); return null; } catch (Exception __e) { throw rethrow(__e); } } // used internally - we are in synchronized block static HashMap getOpt_makeCache(Class c) { HashMap map; if (isSubtypeOf(c, Map.class)) map = getOpt_special; else { map = new HashMap(); if (!reflection_classesNotToScan().contains(c.getName())) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) { makeAccessible(f); String name = f.getName(); if (!map.containsKey(name)) map.put(name, f); } _c = _c.getSuperclass(); } while (_c != null); } } if (getOpt_cache != null) getOpt_cache.put(c, map); return map; } static boolean isURL(String s) { return startsWithOneOf(s, "http://", "https://", "file:"); } static BufferedImage imageIO_readURL(String url) { try { return ImageIO.read(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } public static boolean isSnippetID(String s) { try { parseSnippetID(s); return true; } catch (RuntimeException e) { return false; } } public static long parseSnippetID(String snippetID) { long id = Long.parseLong(shortenSnippetID(snippetID)); if (id == 0) throw fail("0 is not a snippet ID"); return id; } static IResourceLoader vm_getResourceLoader() { return proxy(IResourceLoader.class, vm_generalMap_get("_officialResourceLoader")); } static File imageSnippetsCacheDir() { return javaxCachesDir("Image-Snippets"); } static String snippetImageURL_http(String snippetID) { return snippetImageURL_http(snippetID, "png"); } static String snippetImageURL_http(String snippetID, String contentType) { return replacePrefix("https://", "http://", snippetImageURL(snippetID, contentType)).replace(":8443", ":8080"); } static BufferedImage loadBufferedImageFile(File file) { try { return isFile(file) ? ImageIO.read(file) : null; } catch (Exception __e) { throw rethrow(__e); } } // this copies to RGBA static BufferedImage copyImage(Image img) { if (img == null) return null; if (img instanceof BufferedImage) return copyImage((BufferedImage) img); int w = img.getWidth(null), h = img.getHeight(null); BufferedImage bi = newBufferedImage(w, h); drawImage(bi, img); return bi; } // this one stays in color model. inconsistent i guess static BufferedImage copyImage(BufferedImage bi) { if (bi == null) return null; ColorModel cm = bi.getColorModel(); boolean isAlphaPremultiplied = cm.isAlphaPremultiplied(); WritableRaster raster = bi.copyData(bi.getRaster().createCompatibleWritableRaster()); return new BufferedImage(cm, raster, isAlphaPremultiplied, null); } static boolean eqOneOf(Object o, Object... l) { if (l != null) for (Object x : l) if (eq(o, x)) return true; return false; } static IF0 ping_v3_pingSourceMaker_cache; static IF0 ping_v3_pingSourceMaker() { if (ping_v3_pingSourceMaker_cache == null) ping_v3_pingSourceMaker_cache = ping_v3_pingSourceMaker_load(); return ping_v3_pingSourceMaker_cache;} static IF0 ping_v3_pingSourceMaker_load() { return or((IF0) vm_generalMap_get("ping_v3_pingSourceMaker"), () -> null); } static BufferedImage byteArrayToGrayBufferedImage(byte[] pixels, int w, int h) { PixelInterleavedSampleModel sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, w, h, 1, // pixelStride w, // scanlineStride, new int[] {0} // bandOffsets ); DataBufferByte db = new DataBufferByte(pixels, pixels.length); WritableRaster wr = Raster.createWritableRaster(sm, db, new Point()); return new BufferedImage(grayColorModel(), wr, false, null); } static List immutableEmptyList() { return Collections.emptyList(); } static byte[] emptyByteArray_a = new byte[0]; static byte[] emptyByteArray() { return emptyByteArray_a; } static short[] emptyShortArray = new short[0]; static short[] emptyShortArray() { return emptyShortArray; } static Map immutableEmptyMap() { return Collections.emptyMap(); } static boolean isTrueOrYes(Object o) { return isTrueOpt(o) || o instanceof String && (eqicOneOf(((String) o), "1", "t", "true") || isYes(((String) o))); } static LinkedHashMap asLinkedHashMap(Map map) { if (map instanceof LinkedHashMap) return (LinkedHashMap) map; LinkedHashMap m = new LinkedHashMap(); if (map != null) synchronized(collectionMutex(map)) { m.putAll(map); } return m; } static void swingAndWait(Runnable r) { try { if (isAWTThread()) r.run(); else EventQueue.invokeAndWait(addThreadInfoToRunnable(r)); } catch (Exception __e) { throw rethrow(__e); } } static Object swingAndWait(final Object f) { if (isAWTThread()) return callF(f); else { final Var result = new Var(); swingAndWait(new Runnable() { public void run() { try { result.set(callF(f)); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "result.set(callF(f));"; }}); return result.get(); } } static Object pcallF(Object f, Object... args) { return pcallFunction(f, args); } static A pcallF(F0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null; } static B pcallF(F1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null; } static void pcallF(VF1 f, A a) { try { { if (f != null) f.get(a); } } catch (Throwable __e) { printStackTrace(__e); } } static Object pcallF(Runnable r) { try { { if (r != null) r.run(); } } catch (Throwable __e) { printStackTrace(__e); } return null; } static A pcallF(IF0 f) { try { return f == null ? null : f.get(); } catch (Throwable __e) { printStackTrace(__e); } return null; } static B pcallF(IF1 f, A a) { try { return f == null ? null : f.get(a); } catch (Throwable __e) { printStackTrace(__e); } return null; } static DocumentListener runnableToDocumentListener(Runnable r) { return new DocumentListener() { public void insertUpdate(DocumentEvent e) { pcallF(r); } public void removeUpdate(DocumentEvent e) { pcallF(r); } public void changedUpdate(DocumentEvent e) { pcallF(r); } }; } static JTextField onEnter(JTextField tf, JButton btn) { if (btn != null) onEnter(tf, new Runnable() { public void run() { try { clickButton(btn) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "clickButton(btn)"; }}); return tf; } static JTextField onEnter(JTextField tf, Object action) { if (action == null || tf == null) return tf; tf.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent _evt) { try { tf.selectAll(); callF(action); } catch (Throwable __e) { messageBox(__e); }}}); return tf; } static JButton onEnter(JButton btn, final Object action) { if (action == null || btn == null) return btn; btn.addActionListener(actionListener(action)); return btn; } static JList onEnter(JList list, Object action) { list.addKeyListener(enterKeyListener(rCallOnSelectedListItem(list, action))); return list; } static JComboBox onEnter(final JComboBox cb, Runnable action) { { swing(() -> { if (cb.isEditable()) { JTextField text = (JTextField) cb.getEditor().getEditorComponent(); onEnter(text, action); } else { cb.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "enter"); cb.getActionMap().put("enter", abstractAction("", new Runnable() { public void run() { try { cb.hidePopup(); callF(action); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "cb.hidePopup(); callF(action);"; }})); } }); } return cb; } static JTable onEnter(final JTable table, final Object action) { table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter"); table.getActionMap().put("Enter", new AbstractAction() { public void actionPerformed(ActionEvent e) { callF(action, table.getSelectedRow()); } }); return table; } /*static JTextArea onEnter(final JTextArea ta, fO action) { addKeyListener(ta, enterKeyListener(action)); ret ta; }*/ static JTextField onEnter(Runnable action, JTextField tf) { return onEnter(tf, action); } static ActionListener actionListener(final Object runnable) { return actionListener(runnable, null); } static ActionListener actionListener(final Object runnable, final Object instanceToHold) { if (runnable instanceof ActionListener) return (ActionListener) runnable; final Object info = _threadInfo(); return new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent _evt) { try { _threadInheritInfo(info); AutoCloseable __1 = holdInstance(instanceToHold); try { pcallF(runnable); } finally { _close(__1); }} catch (Throwable __e) { messageBox(__e); }}}; } static String selectedItem(JList l) { return getSelectedItem(l); } static String selectedItem(JComboBox cb) { return getSelectedItem(cb); } static A revalidate(final A c) { if (c == null || !c.isShowing()) return c; { swing(() -> { // magic combo to actually relayout and repaint c.revalidate(); c.repaint(); }); } return c; } static void revalidate(JFrame f) { revalidate((Component) f); } static void revalidate(JInternalFrame f) { revalidate((Component) f); } static HashMap litmap(Object... x) { HashMap map = new HashMap(); litmap_impl(map, x); return map; } static void litmap_impl(Map map, Object... x) { if (x != null) for (int i = 0; i < x.length-1; i += 2) if (x[i+1] != null) map.put(x[i], x[i+1]); } static TreeSet litciset(String... items) { TreeSet set = caseInsensitiveSet(); for (String a : items) set.add(a); return set; } static TreeSet litciset(Symbol... items) { TreeSet set = treeSet(); // HashSet would also do, but we might have the return type fixed somewhere, and they might want a NavigableMap. for (Symbol a : items) set.add(a); return set; } static String dropSuffix(String suffix, String s) { return nempty(suffix) && endsWith(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static String afterLastSpace(String s) { return s == null ? null : substring(s, s.lastIndexOf(' ')+1); } static boolean endsWith(String a, String b) { return a != null && a.endsWith(b); } static boolean endsWith(String a, char c) { return nempty(a) && lastChar(a) == c; } static boolean endsWith(String a, String b, Matches m) { if (!endsWith(a, b)) return false; m.m = new String[] {dropLast(l(b), a)}; return true; } static A[] dropLast(A[] a) { return dropLast(a, 1); } static A[] dropLast(A[] a, int n) { if (a == null) return null; n = Math.min(n, a.length); A[] b = arrayOfSameType(a, a.length-n); System.arraycopy(a, 0, b, 0, b.length); return b; } static List dropLast(List l) { return subList(l, 0, l(l)-1); } static List dropLast(int n, List l) { return subList(l, 0, l(l)-n); } static List dropLast(Iterable l) { return dropLast(asList(l)); } static String dropLast(String s) { return substring(s, 0, l(s)-1); } static String dropLast(String s, int n) { return substring(s, 0, l(s)-n); } static String dropLast(int n, String s) { return dropLast(s, n); } static boolean ewic(String a, String b) { return endsWithIgnoreCase(a, b); } static boolean ewic(String a, String b, Matches m) { return endsWithIgnoreCase(a, b, m); } static String dropSuffixIgnoreCase(String suffix, String s) { return ewic(s, suffix) ? s.substring(0, l(s)-l(suffix)) : s; } static boolean ewicOneOf(String s, String... l) { if (s != null) for (String x : l) if (ewic(s, x)) return true; return false; } static A[] makeArray(Class type, int n) { return (A[]) Array.newInstance(type, n); } static List newSubList(List l, int startIndex, int endIndex) { return cloneList(subList(l, startIndex, endIndex)); } static List newSubList(List l, int startIndex) { return cloneList(subList(l, startIndex)); } static List subList(List l, int startIndex) { return subList(l, startIndex, l(l)); } static List subList(int startIndex, List l) { return subList(l, startIndex); } static List subList(int startIndex, int endIndex, List l) { return subList(l, startIndex, endIndex); } static List subList(List l, int startIndex, int endIndex) { if (l == null) return null; int n = l(l); startIndex = Math.max(0, startIndex); endIndex = Math.min(n, endIndex); if (startIndex > endIndex) return ll(); if (startIndex == 0 && endIndex == n) return l; return l.subList(startIndex, endIndex); } static List subList(List l, IntRange r) { return subList(l, r.start, r.end); } static String fileName(File f) { return f == null ? null : f.getName(); } public static File mkdirsFor(File file) { return mkdirsForFile(file); } static BufferedImage reconstructBufferedImage(BufferedImage img) { if (img == null) return null; RGBImage rgb = new RGBImage(img); rgb.uncacheBufferedImage(); return rgb.getBufferedImage(); } static List beginCriticalAction_inFlight = synchroList(); static class CriticalAction implements AutoCloseable { String description; CriticalAction() {} CriticalAction(String description) { this.description = description;} final public void close(){ done(); } public void done() { beginCriticalAction_inFlight.remove(this); } } static CriticalAction beginCriticalAction(String description) { ping(); CriticalAction c = new CriticalAction(description); beginCriticalAction_inFlight.add(c); return c; } static void cleanMeUp_beginCriticalAction() { int n = 0; while (nempty(beginCriticalAction_inFlight)) { int m = l(beginCriticalAction_inFlight); if (m != n) { n = m; try { print("Waiting for " + n2(n, "critical actions") + ": " + join(", ", collect(beginCriticalAction_inFlight, "description"))); } catch (Throwable __e) { printStackTrace(__e); } } sleepInCleanUp(10); } } static String f2s(File f) { return f == null ? null : f.getAbsolutePath(); } static String f2s(String s) { return f2s(newFile(s)); } static String f2s(java.nio.file.Path p) { return p == null ? null : f2s(p.toFile()); } static File toFile(Object o) { if (o instanceof File) return (File) o; if (o instanceof String) return new File((String) o); throw fail("Not a file: " + o); } static ClassLoader classLoaderForObject(Object o) { if (o instanceof ClassLoader) return ((ClassLoader) o); if (o == null) return null; return _getClass(o).getClassLoader(); } // Note: This is actually broken. Inner classes must stay with a $ separator static String classNameToVM(String name) { return name.replace(".", "$"); } // Use like this: renderVars(+x, +y) // Or like this: renderVars("bla", +x, +y) static String renderVars_str(Object... params) { List l = new ArrayList(); int i = 0; if (odd(l(params))) { l.add(strOrNull(first(params))); ++i; } for (; i+1 < l(params); i += 2) l.add(params[i] + "=" + params[i+1]); return trim(joinWithComma(l)); } static IterableIterator emptyIterableIterator_instance = new IterableIterator() { public Object next() { throw fail(); } public boolean hasNext() { return false; } }; static IterableIterator emptyIterableIterator() { return emptyIterableIterator_instance; } static List allToString(Iterable c) { List l = new ArrayList(); for (Object o : unnull(c)) l.add(str(o)); return l; } static List allToString(Object[] c) { List l = new ArrayList(); for (Object o : unnull(c)) l.add(str(o)); return l; } static IMeta initMetaOfJComponent(JComponent c) { if (c == null) return null; IMeta meta = (IMeta) (c.getClientProperty(IMeta.class)); if (meta == null) c.putClientProperty(IMeta.class, meta = new Meta()); return meta; } static SimpleDateFormat simpleDateFormat_local(String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); sdf.setTimeZone(localTimeZone()); return sdf; } static List _registerDangerousWeakMap_preList; static A _registerDangerousWeakMap(A map) { return _registerDangerousWeakMap(map, null); } static A _registerDangerousWeakMap(A map, Object init) { callF(init, map); if (init instanceof String) { final String f = (String) init; init = new VF1() { public void get(Map map) { try { callMC(f, map) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callMC(f, map)"; }}; } if (javax() == null) { // We're in class init if (_registerDangerousWeakMap_preList == null) _registerDangerousWeakMap_preList = synchroList(); _registerDangerousWeakMap_preList.add(pair(map, init)); return map; } call(javax(), "_registerDangerousWeakMap", map, init); return map; } static void _onLoad_registerDangerousWeakMap() { assertNotNull(javax()); if (_registerDangerousWeakMap_preList == null) return; for (Pair p : _registerDangerousWeakMap_preList) _registerDangerousWeakMap(p.a, p.b); _registerDangerousWeakMap_preList = null; } static Throwable getExceptionCause(Throwable e) { Throwable c = e.getCause(); return c != null ? c : e; } static List classNames(Collection l) { return getClassNames(l); } static List classNames(Object[] l) { return getClassNames(asList(l)); } static boolean isInstanceX(Class type, Object arg) { if (type == boolean.class) return arg instanceof Boolean; if (type == int.class) return arg instanceof Integer; if (type == long.class) return arg instanceof Long; if (type == float.class) return arg instanceof Float; if (type == short.class) return arg instanceof Short; if (type == char.class) return arg instanceof Character; if (type == byte.class) return arg instanceof Byte; if (type == double.class) return arg instanceof Double; return type.isInstance(arg); } static WeakHasherMap symbol_map = new WeakHasherMap(new Hasher() { public int hashCode(Symbol symbol) { return symbol.text.hashCode(); } public boolean equals(Symbol a, Symbol b) { if (a == null) return b == null; return b != null && eq(a.text, b.text); } }); static Symbol symbol(String s) { if (s == null) return null; synchronized(symbol_map) { // TODO: avoid object creation by passing the string to findKey Symbol symbol = new Symbol(s, true); Symbol existingSymbol = symbol_map.findKey(symbol); if (existingSymbol == null) symbol_map.put(existingSymbol = symbol, true); return existingSymbol; } } static Symbol symbol(CharSequence s) { if (s == null) return null; if (s instanceof Symbol) return (Symbol) s; if (s instanceof String) return symbol((String) s); return symbol(str(s)); } static Symbol symbol(Object o) { return symbol((CharSequence) o); } static String repeat(char c, int n) { n = Math.max(n, 0); char[] chars = new char[n]; for (int i = 0; i < n; i++) chars[i] = c; return new String(chars); } static List repeat(A a, int n) { n = Math.max(n, 0); List l = new ArrayList(n); for (int i = 0; i < n; i++) l.add(a); return l; } static List repeat(int n, A a) { return repeat(a, n); } static IterableIterator countIterator_exclusive(int b) { return countIterator_exclusive(0, b); } static IterableIterator countIterator_exclusive(int a, int b) { return new IterableIterator() { int i = a; public boolean hasNext() { return i < b; } public Integer next() { return i++; } }; } static IterableIterator countIterator_exclusive(int b, IF1 f) { return countIterator_exclusive(0, b, f); } static IterableIterator countIterator_exclusive(int a, int b, IF1 f) { return mapI_if1(f, countIterator_exclusive(a, b)); } static IterableIterator countIterator_exclusive_step(final int a, final int b, final int step) { assertTrue("step > 0", step > 0); return new IterableIterator() { int i = a; public boolean hasNext() { return i < b; } public Integer next() { var j = i; i += step; return j; } }; } static IterableIterator countIterator_exclusive_step(double a, double b, double step) { assertTrue("step > 0", step > 0); return new IterableIterator() { double i = a; public boolean hasNext() { return i < b; } public Double next() { var j = i; i += step; return j; } }; } static IterableIterator countIterator_exclusive_step(double a, double b, double step, IF1 f) { return mapI_if1(f, countIterator_exclusive_step(a, b, step)); } static IterableIterator iteratorFromFunction_withEndMarker_f0(final F0 f) { class IFF2 extends IterableIterator { A a; boolean have, done; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; have = false; return _a; } void getNext() { if (done || have) return; Object o = f.get(); if (o == iteratorFromFunction_endMarker) { done = true; return; } a = (A) o; have = true; } }; return new IFF2(); } static AutoCloseable tempSetThreadLocal(final ThreadLocal tl, A a) { if (tl == null) return null; final A prev = setThreadLocal(tl, a); return new AutoCloseable() { public String toString() { return "tl.set(prev);"; } public void close() throws Exception { tl.set(prev); }}; } static String appendColonIfNempty(String s) { return empty(s) ? "" : s + ": "; } static boolean isEmpty(Collection c) { return c == null || c.isEmpty(); } static boolean isEmpty(CharSequence s) { return s == null || s.length() == 0; } static boolean isEmpty(Object[] a) { return a == null || a.length == 0; } static boolean isEmpty(byte[] a) { return a == null || a.length == 0; } static boolean isEmpty(Map map) { return map == null || map.isEmpty(); } static boolean isEmpty(DoubleRange r) { return r == null || r.isEmpty(); } static boolean isEmpty(AppendableChain c) { return c == null; } static void setMeta(IMeta o, Object key, Object value) { metaMapPut(o, key, value); } static void setMeta(Object o, Object key, Object value) { metaMapPut(o, key, value); } static void assertSame(Object a, Object b) { assertSame("", a, b); } static void assertSame(String msg, Object a, Object b) { if (a != b) throw fail(joinNemptiesWithColon(msg, a + " != " + b + " (" + identityHash(a) + "/" + identityHash(b) + ")")); } static void assertSame(IF0 msg, Object a, Object b) { if (a != b) throw fail(joinNemptiesWithColon(msg.get(), a + " != " + b + " (" + identityHash(a) + "/" + identityHash(b) + ")")); } static void _onJavaXSet() {} static Object call_withVarargs(Object o, String methodName, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Class c = (Class) o; _MethodCache cache = callOpt_getCache(c); Method me = cache.findStaticMethod(methodName, args); if (me != null) return invokeMethod(me, null, args); // try varargs List methods = cache.cache.get(methodName); if (methods != null) methodSearch: for (Method m : methods) { { if (!(m.isVarArgs())) continue; } { if (!(isStaticMethod(m))) continue; } Object[] newArgs = massageArgsForVarArgsCall(m, args); if (newArgs != null) return invokeMethod(m, null, newArgs); } throw fail("Method " + c.getName() + "." + methodName + "(" + joinWithComma(classNames(args)) + ") not found"); } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(methodName, args); if (me != null) return invokeMethod(me, o, args); // try varargs List methods = cache.cache.get(methodName); if (methods != null) methodSearch: for (Method m : methods) { { if (!(m.isVarArgs())) continue; } Object[] newArgs = massageArgsForVarArgsCall(m, args); if (newArgs != null) return invokeMethod(m, o, newArgs); } throw fail("Method " + c.getName() + "." + methodName + "(" + joinWithComma(classNames(args)) + ") not found"); } } catch (Exception __e) { throw rethrow(__e); } } static List synchroList() { return synchroList(new ArrayList()); } static List synchroList(List l) { return Collections.synchronizedList(l); } static void setOptIfNotNull(Object o, String field, Object value) { if (value != null) setOpt(o, field, value); } static Object mainBot; static Object getMainBot() { return mainBot; } static void pcallFAll(Collection l, Object... args) { if (l != null) for (Object f : cloneList(l)) pcallF(f, args); } static void pcallFAll(Iterator it, Object... args) { while (it.hasNext()) pcallF(it.next(), args); } static String[] drop(int n, String[] a) { n = Math.min(n, a.length); String[] b = new String[a.length-n]; System.arraycopy(a, n, b, 0, b.length); return b; } static Object[] drop(int n, Object[] a) { n = Math.min(n, a.length); Object[] b = new Object[a.length-n]; System.arraycopy(a, n, b, 0, b.length); return b; } static String substring(String s, int x) { return substring(s, x, strL(s)); } static String substring(String s, int x, int y) { if (s == null) return null; if (x < 0) x = 0; int n = s.length(); if (y < x) y = x; if (y > n) y = n; if (x >= y) return ""; return s.substring(x, y); } static String substring(String s, IntRange r) { return r == null ? null : substring(s, r.start, r.end); } // convenience method for quickly dropping a prefix static String substring(String s, CharSequence l) { return substring(s, lCharSequence(l)); } static boolean isDigit(char c) { return Character.isDigit(c); } static int lastIndexOf(String a, String b) { return a == null || b == null ? -1 : a.lastIndexOf(b); } static int lastIndexOf(String a, char b) { return a == null ? -1 : a.lastIndexOf(b); } // starts searching from i-1 static int lastIndexOf(List l, int i, A a) { if (l == null) return -1; for (i = min(l(l), i)-1; i >= 0; i--) if (eq(l.get(i), a)) return i; return -1; } static int lastIndexOf(List l, A a) { if (l == null) return -1; for (int i = l(l)-1; i >= 0; i--) if (eq(l.get(i), a)) return i; return -1; } static Set synchronizedSet() { return synchroHashSet(); } static Set synchronizedSet(Set set) { return Collections.synchronizedSet(set); } static PersistableThrowable persistableThrowable(Throwable e) { return e == null ? null : new PersistableThrowable(e); } static boolean startsWithOneOf(String s, String... l) { for (String x : l) if (startsWith(s, x)) return true; return false; } static boolean startsWithOneOf(String s, Matches m, String... l) { for (String x : l) if (startsWith(s, x, m)) return true; return false; } static boolean isAGIBlueDomain(String domain) { return domainIsUnder(domain, theAGIBlueDomain()); } static String hostNameFromURL(String url) { try { return empty(url) ? null : new URL(url).getHost(); } catch (Exception __e) { throw rethrow(__e); } } static Object pcallF_minimalExceptionHandling(Object f, Object... args) { try { return callFunction(f, args); } catch (Throwable e) { System.out.println(getStackTrace(e)); _storeException(e); } return null; } static Set vm_generalIdentityHashSet(Object name) { synchronized(vm_generalMap()) { Set set = (Set) (vm_generalMap_get(name)); if (set == null) vm_generalMap_put(name, set = syncIdentityHashSet()); return set; } } static Map vm_generalHashMap(Object name) { synchronized(vm_generalMap()) { Map m = (Map) (vm_generalMap_get(name)); if (m == null) vm_generalMap_put(name, m = syncHashMap()); return m; } } static String strOrNull(Object o) { return o == null ? null : str(o); } static void setDynObjectValue(DynamicObject o, String field, Object value) { dynamicObject_setRawFieldValue(o, field, value); } static CloseableIterableIterator emptyCloseableIterableIterator_instance = new CloseableIterableIterator() { public Object next() { throw fail(); } public boolean hasNext() { return false; } }; static CloseableIterableIterator emptyCloseableIterableIterator() { return emptyCloseableIterableIterator_instance; } static CloseableIterableIterator linesFromReader(Reader r) { return linesFromReader(r, null); } static CloseableIterableIterator linesFromReader(Reader r, IResourceHolder resourceHolder) { final BufferedReader br = bufferedReader(r); return holdResource(resourceHolder, iteratorFromFunction_f0_autoCloseable(new F0() { public String get() { try { return readLineFromReaderWithClose(br); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "return readLineFromReaderWithClose(br);"; }}, _wrapIOCloseable(r))); } static BufferedReader utf8bufferedReader(InputStream in) { try { return in == null ? null : bufferedReader(_registerIOWrap(new InputStreamReader(in, "UTF-8"), in)); } catch (Exception __e) { throw rethrow(__e); } } static BufferedReader utf8bufferedReader(File f) { try { return utf8bufferedReader(newFileInputStream(f)); } catch (Exception __e) { throw rethrow(__e); } } static GZIPInputStream newGZIPInputStream(File f) { return gzInputStream(f); } static GZIPInputStream newGZIPInputStream(InputStream in) { return gzInputStream(in); } static File newFile(File base, String... names) { for (String name : names) base = new File(base, name); return base; } static File newFile(String name) { return name == null ? null : new File(name); } static File newFile(String base, String... names) { return newFile(newFile(base), names); } static betterCIComparator_C betterCIComparator_instance; static betterCIComparator_C betterCIComparator() { if (betterCIComparator_instance == null) betterCIComparator_instance = new betterCIComparator_C(); return betterCIComparator_instance; } final static class betterCIComparator_C implements Comparator { public int compare(String s1, String s2) { if (s1 == null) return s2 == null ? 0 : -1; if (s2 == null) return 1; int n1 = s1.length(); int n2 = s2.length(); int min = Math.min(n1, n2); for (int i = 0; i < min; i++) { char c1 = s1.charAt(i); char c2 = s2.charAt(i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); if (c1 != c2) { c1 = Character.toLowerCase(c1); c2 = Character.toLowerCase(c2); if (c1 != c2) { // No overflow because of numeric promotion return c1 - c2; } } } } return n1 - n2; } } static List _registerWeakMap_preList; static A _registerWeakMap(A map) { if (javax() == null) { // We're in class init if (_registerWeakMap_preList == null) _registerWeakMap_preList = synchroList(); _registerWeakMap_preList.add(map); return map; } try { call(javax(), "_registerWeakMap", map); } catch (Throwable e) { printException(e); print("Upgrade JavaX!!"); } return map; } static void _onLoad_registerWeakMap() { assertNotNull(javax()); if (_registerWeakMap_preList == null) return; for (Object o : _registerWeakMap_preList) _registerWeakMap(o); _registerWeakMap_preList = null; } static x30_pkg.x30_util.BetterThreadLocal newPing_actionTL; static x30_pkg.x30_util.BetterThreadLocal newPing_actionTL() { if (newPing_actionTL == null) newPing_actionTL = vm_generalMap_getOrCreate("newPing_actionTL", () -> { Runnable value = (Runnable) (callF_gen(vm_generalMap_get("newPing_valueForNewThread"))); var tl = new x30_pkg.x30_util.BetterThreadLocal(); tl.set(value); return tl; }); return newPing_actionTL; } static Boolean isHeadless_cache; static boolean isHeadless() { if (isHeadless_cache != null) return isHeadless_cache; if (isAndroid()) return isHeadless_cache = true; if (GraphicsEnvironment.isHeadless()) return isHeadless_cache = true; // Also check if AWT actually works. // If DISPLAY variable is set but no X server up, this will notice. try { SwingUtilities.isEventDispatchThread(); return isHeadless_cache = false; } catch (Throwable e) { return isHeadless_cache = true; } } static void assertTrue(Object o) { if (!(eq(o, true) /*|| isTrue(pcallF(o))*/)) throw fail(str(o)); } static boolean assertTrue(String msg, boolean b) { if (!b) throw fail(msg); return b; } static boolean assertTrue(boolean b) { if (!b) throw fail("oops"); return b; } static volatile boolean licensed_yes = true; static boolean licensed() { if (!licensed_yes) return false; ping_okInCleanUp(); return true; } static void licensed_off() { licensed_yes = false; } static byte[] hexToBytes(String s) { if (odd(l(s))) throw fail("Hex string has odd length: " + quote(shorten(10, s))); int n = l(s) / 2; byte[] bytes = new byte[n]; for (int i = 0; i < n; i++) { int a = parseHexChar(s.charAt(i*2)); int b = parseHexChar(s.charAt(i*2+1)); if (a < 0 || b < 0) throw fail("Bad hex byte: " + quote(substring(s, i*2, i*2+2)) + " at " + i*2 + "/" + l(s)); bytes[i] = (byte) ((a << 4) | b); } return bytes; } static Map putAll(Map a, Map b) { if (a != null && b != null) a.putAll(b); return a; } static MultiMap putAll(MultiMap a, Map 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; } // TODO: optimize (use getOpt_cache) static void setOpt_raw(Object o, String field, Object value) { try { if (o == null) return; if (o instanceof Class) setOpt_raw((Class) o, field, value); else { Field f = setOpt_raw_findField(o.getClass(), field); if (f != null) { makeAccessible(f); smartSet(f, o, value); } } } catch (Exception __e) { throw rethrow(__e); } } static void setOpt_raw(Class c, String field, Object value) { try { if (c == null) return; Field f = setOpt_raw_findStaticField(c, field); if (f != null) { makeAccessible(f); smartSet(f, null, value); } } catch (Exception __e) { throw rethrow(__e); } } static Field setOpt_raw_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 Field setOpt_raw_findField(Class c, String field) { Class _c = c; do { for (Field f : _c.getDeclaredFields()) if (f.getName().equals(field)) return f; _c = _c.getSuperclass(); } while (_c != null); return null; } static String[] toStringArray(Collection c) { String[] a = new String[l(c)]; Iterator it = c.iterator(); for (int i = 0; i < l(a); i++) a[i] = it.next(); return a; } static String[] toStringArray(Object o) { if (o instanceof String[]) return (String[]) o; else if (o instanceof Collection) return toStringArray((Collection) o); else throw fail("Not a collection or array: " + getClassName(o)); } static final Map callOpt_cache = newDangerousWeakHashMap(); static Object callOpt_cached(Object o, String methodName, Object... args) { try { if (o == null) return null; if (o instanceof Class) { Class c = (Class) o; _MethodCache cache = callOpt_getCache(c); // TODO: (super-rare) case where method exists static and non-static // with different args Method me = cache.findMethod(methodName, args); if (me == null || (me.getModifiers() & Modifier.STATIC) == 0) return null; return invokeMethod(me, null, args); } else { Class c = o.getClass(); _MethodCache cache = callOpt_getCache(c); Method me = cache.findMethod(methodName, args); if (me == null) return null; return invokeMethod(me, o, args); } } catch (Exception __e) { throw rethrow(__e); } } // no longer synchronizes! (see #1102990) static _MethodCache callOpt_getCache(Class c) { _MethodCache cache = callOpt_cache.get(c); if (cache == null) callOpt_cache.put(c, cache = new _MethodCache(c)); return cache; } static Object[] massageArgsForVarArgsCall(Executable m, Object[] args) { Class[] types = m.getParameterTypes(); int n = types.length-1, nArgs = l(args); if (nArgs < n) return null; for (int i = 0; i < n; i++) if (!argumentCompatibleWithType(args[i], types[i])) return null; Class varArgType = types[n].getComponentType(); for (int i = n; i < nArgs; i++) if (!argumentCompatibleWithType(args[i], varArgType)) return null; Object[] newArgs = new Object[n+1]; arraycopy(args, 0, newArgs, 0, n); // TODO: optimize int nVarArgs = nArgs-n; Object varArgs = Array.newInstance(varArgType, nVarArgs); for (int i = 0; i < nVarArgs; i++) Array.set(varArgs, i, args[n+i]); newArgs[n] = varArgs; return newArgs; } static void clear(Collection c) { if (c != null) c.clear(); } static void clear(Map map) { if (map != null) map.clear(); } 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 B syncMapGet2(Map map, A a) { if (map == null) return null; synchronized(collectionMutex(map)) { return map.get(a); } } static B syncMapGet2(A a, Map map) { return syncMapGet2(map, a); } static boolean isSubtypeOf(Class a, Class b) { return a != null && b != null && b.isAssignableFrom(a); // << always hated that method, let's replace it! } static Set reflection_classesNotToScan_value = litset( "jdk.internal.loader.URLClassPath" ); static Set reflection_classesNotToScan() { return reflection_classesNotToScan_value; } static String shortenSnippetID(String snippetID) { if (snippetID.startsWith("#")) snippetID = snippetID.substring(1); String httpBlaBla = "http://tinybrain.de/"; if (snippetID.startsWith(httpBlaBla)) snippetID = snippetID.substring(httpBlaBla.length()); return "" + parseLong(snippetID); } static A proxy(Class intrface, final Object target) { if (target == null) return null; if (isInstance(intrface, target)) return (A) target; return (A) java.lang.reflect.Proxy.newProxyInstance(intrface.getClassLoader(), new Class[] { intrface }, new proxy_InvocationHandler(target)); } static A proxy(Object target, Class intrface) { return proxy(intrface, target); } static Object vm_generalMap_get(Object key) { return vm_generalMap().get(key); } static File javaxCachesDir_dir; // can be set to work on different base dir static File javaxCachesDir() { return javaxCachesDir_dir != null ? javaxCachesDir_dir : new File(userHome(), "JavaX-Caches"); } static File javaxCachesDir(String sub) { return newFile(javaxCachesDir(), sub); } static String replacePrefix(String prefix, String replacement, String s) { if (!startsWith(s, prefix)) return s; return replacement + substring(s, l(prefix)); } static String snippetImageURL(long snippetID) { return snippetImageURL(fsI(snippetID)); } static String snippetImageURL(String snippetID) { return snippetImageURL(snippetID, "png"); } static String snippetImageURL(String snippetID, String contentType) { if (snippetID == null || isURL(snippetID)) return snippetID; long id = parseSnippetID(snippetID); String url; if (isImageServerSnippet(id)) url = imageServerLink(id); else //url = "http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_" + id + "&contentType=image/" + contentType; url = "https://botcompany.de/img/" + id; return url; } static boolean isFile(File f) { return f != null && f.isFile(); } static boolean isFile(String path) { return isFile(newFile(path)); } // changes & returns canvas static BufferedImage drawImage(BufferedImage canvas, Image img, Pt p) { return drawImageOnImage(img, canvas, p.x, p.y); } static void drawImage(BufferedImage g, Image img) { drawImage(graphics(g), img); } static void drawImage(Graphics2D g, Image img) { drawImage(g, img, 0, 0); } static void drawImage(Graphics2D g, Image img, Pt p) { drawImage(g, img, p.x, p.y); } static void drawImage(Graphics2D g, Image img, int x, int y) { { if (g != null) g.drawImage(img, x, y, null); } } static ColorModel grayColorModel_cache; static ColorModel grayColorModel() { if (grayColorModel_cache == null) grayColorModel_cache = grayColorModel_load(); return grayColorModel_cache;} static ColorModel grayColorModel_load() { return newGrayBufferedImage(1, 1).getColorModel(); } static boolean isTrueOpt(Object o) { if (o instanceof Boolean) return ((Boolean) o).booleanValue(); return false; } static boolean isTrueOpt(String field, Object o) { return isTrueOpt(getOpt(field, o)); } static boolean eqicOneOf(String s, String... l) { for (String x : l) if (eqic(s, x)) return true; return false; } static List isYes_yesses = litlist("y", "yes", "yeah", "y", "yup", "yo", "corect", "sure", "ok", "afirmative"); // << collapsed words, so "corect" means "correct" static boolean isYes(String s) { return isYes_yesses.contains(collapseWord(toLowerCase(firstWord2(s)))); } static Runnable addThreadInfoToRunnable(final Object r) { final Object info = _threadInfo(); return info == null ? asRunnable(r) : new Runnable() { public void run() { try { _inheritThreadInfo(info); callF(r); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_inheritThreadInfo(info); callF(r);"; }}; } static Object pcallFunction(Object f, Object... args) { try { return callFunction(f, args); } catch (Throwable __e) { printStackTrace(__e); } return null; } static void clickButton(final JButton b) { if (b != null) { swing(() -> { if (b.isEnabled()) b.doClick(); }); } } static void messageBox(final String msg) { print(msg); { swing(() -> { JOptionPane.showMessageDialog(null, msg, "JavaX", JOptionPane.INFORMATION_MESSAGE); }); } } static void messageBox(Throwable e) { //showConsole(); printStackTrace(e); messageBox(hideCredentials(innerException2(e))); } static KeyListener enterKeyListener(final Object action) { return new KeyAdapter() { public void keyPressed(KeyEvent ke) { if (ke.getKeyCode() == KeyEvent.VK_ENTER) pcallF(action); } }; } static Runnable rCallOnSelectedListItem(final JList list, final Object action) { return new Runnable() { public void run() { try { pcallF(action, getSelectedItem(list)) ; } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "pcallF(action, getSelectedItem(list))"; }}; } static AbstractAction abstractAction(String name, final Object runnable) { return new AbstractAction(name) { public void actionPerformed(ActionEvent evt) { pcallF(runnable); } }; } static List> _threadInfo_makers = synchroList(); static Object _threadInfo() { if (empty(_threadInfo_makers)) return null; HashMap map = new HashMap(); pcallFAll(_threadInfo_makers, map); return map; } static List> _threadInheritInfo_retrievers = synchroList(); static void _threadInheritInfo(Object info) { if (info == null) return; pcallFAll(_threadInheritInfo_retrievers, (Map) info); } static ThreadLocal> holdInstance_l = new ThreadLocal(); static AutoCloseable holdInstance(Object o) { if (o == null) return null; listThreadLocalAdd(holdInstance_l, o); return new AutoCloseable() { public void close() { listThreadLocalPopLast(holdInstance_l); } }; } static String getSelectedItem(JList l) { return (String) l.getSelectedValue(); } static String getSelectedItem(JComboBox cb) { return strOrNull(cb.getSelectedItem()); } static TreeSet caseInsensitiveSet() { return caseInsensitiveSet_treeSet(); } static TreeSet caseInsensitiveSet(Collection c) { return caseInsensitiveSet_treeSet(c); } static TreeSet treeSet() { return new TreeSet(); } static char lastChar(String s) { return empty(s) ? '\0' : s.charAt(l(s)-1); } static A[] arrayOfSameType(A[] a, int n) { return newObjectArrayOfSameType(a, n); } static boolean endsWithIgnoreCase(String a, String b) { int la = l(a), lb = l(b); return la >= lb && regionMatchesIC(a, la-lb, b, 0, lb); } static boolean endsWithIgnoreCase(String a, String b, Matches m) { if (!endsWithIgnoreCase(a, b)) return false; if (m != null) m.m = new String[] { substring(a, 0, l(a)-l(b)) }; return true; } public static File mkdirsForFile(File file) { File dir = file.getParentFile(); if (dir != null) { // is null if file is in current dir dir.mkdirs(); if (!dir.isDirectory()) if (dir.isFile()) throw fail("Please delete the file " + f2s(dir) + " - it is supposed to be a directory!"); else throw fail("Unknown IO exception during mkdirs of " + f2s(file)); } return file; } public static String mkdirsForFile(String path) { mkdirsForFile(new File(path)); return path; } static int done_minPrint = 10; static long done(long startTime, String desc) { long time = now()-startTime; if (time >= done_minPrint) print(desc + " [" + time + " ms]"); return time; } static long done(String desc, long startTime) { return done(startTime, desc); } static long done(long startTime) { return done(startTime, ""); } static List collect(Iterable c, String field) { return collectField(c, field); } static List collect(String field, Iterable c) { return collectField(c, field); } /*ifclass Concept static L collect(Class c, S field) { ret collect(list(c), field); } endif TODO: make translator ignore stuff in ifclass until resolved */ static void sleepInCleanUp(long ms) { try { if (ms < 0) return; Thread.sleep(ms); } catch (Exception __e) { throw rethrow(__e); } } static TimeZone localTimeZone() { return getTimeZone(standardTimeZone()); // TimeZone.getDefault()? } static HashMap> callMC_cache = new HashMap(); static String callMC_key; static Method callMC_value; // varargs assignment fixer for a single string array argument static Object callMC(String method, String[] arg) { return callMC(method, new Object[] {arg}); } static Object callMC(String method, Object... args) { try { Method me; if (callMC_cache == null) callMC_cache = new HashMap(); // initializer time workaround synchronized(callMC_cache) { me = method == callMC_key ? callMC_value : null; } if (me != null) try { return invokeMethod(me, null, args); } catch (IllegalArgumentException e) { throw new RuntimeException("Can't call " + me + " with arguments " + classNames(args), e); } List m; synchronized(callMC_cache) { m = callMC_cache.get(method); } if (m == null) { if (callMC_cache.isEmpty()) { callMC_makeCache(); m = callMC_cache.get(method); } if (m == null) throw fail("Method named " + method + " not found in main"); } int n = m.size(); if (n == 1) { me = m.get(0); synchronized(callMC_cache) { callMC_key = method; callMC_value = me; } try { return invokeMethod(me, null, args); } catch (IllegalArgumentException e) { throw new RuntimeException("Can't call " + me + " with arguments " + classNames(args), e); } } for (int i = 0; i < n; i++) { me = m.get(i); if (call_checkArgs(me, args, false)) return invokeMethod(me, null, args); } throw fail("No method called " + method + " with arguments (" + joinWithComma(getClasses(args)) + ") found in main"); } catch (Exception __e) { throw rethrow(__e); } } static void callMC_makeCache() { synchronized(callMC_cache) { callMC_cache.clear(); Class _c = (Class) mc(), c = _c; while (c != null) { for (Method m : c.getDeclaredMethods()) if ((m.getModifiers() & java.lang.reflect.Modifier.STATIC) != 0) { makeAccessible(m); multiMapPut(callMC_cache, m.getName(), m); } c = c.getSuperclass(); } } } static Class javax() { return getJavaX(); } static A assertNotNull(A a) { assertTrue(a != null); return a; } static A assertNotNull(String msg, A a) { assertTrue(msg, a != null); return a; } static List getClassNames(Collection l) { List out = new ArrayList(); if (l != null) for (Object o : l) out.add(o == null ? null : getClassName(o)); return out; } static A setThreadLocal(ThreadLocal tl, A value) { if (tl == null) return null; A old = tl.get(); tl.set(value); return old; } static int identityHash(Object o) { return identityHashCode(o); } static boolean isStaticMethod(Method m) { return methodIsStatic(m); } static int strL(String s) { return s == null ? 0 : s.length(); } static int lCharSequence(CharSequence s) { return s == null ? 0 : s.length(); } static Set synchroHashSet() { return synchronizedSet(new HashSet()); } static boolean startsWith(String a, String b) { return a != null && a.startsWith(unnull(b)); } static boolean startsWith(String a, char c) { return nemptyString(a) && a.charAt(0) == c; } static boolean startsWith(String a, String b, Matches m) { if (!startsWith(a, b)) return false; if (m != null) m.m = new String[] {substring(a, strL(b))}; return true; } static boolean startsWith(List a, List b) { if (a == null || listL(b) > listL(a)) return false; for (int i = 0; i < listL(b); i++) if (neq(a.get(i), b.get(i))) return false; return true; } static boolean domainIsUnder(String domain, String mainDomain) { return eqic(domain, mainDomain) || ewic(domain, "." + mainDomain); } static String theAGIBlueDomain() { return "agi.blue"; } static Object callFunction(Object f, Object... args) { return callF(f, args); } static Throwable _storeException_value; static void _storeException(Throwable e) { _storeException_value = e; } static Map vm_generalMap_map; static Map vm_generalMap() { if (vm_generalMap_map == null) vm_generalMap_map = (Map) get(javax(), "generalMap"); return vm_generalMap_map; } static Object vm_generalMap_put(Object key, Object value) { return mapPutOrRemove(vm_generalMap(), key, value); } static Set syncIdentityHashSet() { return (Set) synchronizedSet(identityHashSet()); } static Map syncHashMap() { return synchroHashMap(); } static void dynamicObject_setRawFieldValue(DynamicObject o, Object key, Object value) { if (o == null) return; // double sync, but should be OK here because of locking order o > o.fieldValues synchronized(o) { o.fieldValues = syncMapPut2_createLinkedHashMap((LinkedHashMap) o.fieldValues, key, value); } } static BufferedReader bufferedReader(Reader r) { return bufferedReader(r, 8192); } static BufferedReader bufferedReader(Reader r, int bufSize) { if (r == null) return null; return r instanceof BufferedReader ? (BufferedReader) r : _registerIOWrap(new BufferedReader(r, bufSize), r); } static BufferedReader bufferedReader(File f) { return utf8bufferedReader(f); } static A holdResource(IResourceHolder holder, A a) { { if (holder != null) holder.add(a); } return a; } static CloseableIterableIterator iteratorFromFunction_f0_autoCloseable(final F0 f, final AutoCloseable closeable) { class IFF2 extends CloseableIterableIterator { A a; boolean done = false; public boolean hasNext() { getNext(); return !done; } public A next() { getNext(); if (done) throw fail(); A _a = a; a = null; return _a; } void getNext() { if (done || a != null) return; a = f.get(); done = a == null; } public void close() throws Exception { if (closeable != null) closeable.close(); } }; return new IFF2(); } static String readLineFromReaderWithClose(BufferedReader r) { try { String s = r.readLine(); if (s == null) r.close(); return s; } catch (Exception __e) { throw rethrow(__e); } } static AutoCloseable _wrapIOCloseable(final AutoCloseable c) { return c == null ? null : new AutoCloseable() { public String toString() { return "c.close();\r\n _registerIO(c, null, false);"; } public void close() throws Exception { c.close(); _registerIO(c, null, false); }}; } static A _registerIOWrap(A wrapper, Object wrapped) { return wrapper; } static FileInputStream newFileInputStream(File path) throws IOException { return newFileInputStream(path.getPath()); } static FileInputStream newFileInputStream(String path) throws IOException { FileInputStream f = new FileInputStream(path); _registerIO(f, path, true); return f; } static int gzInputStream_defaultBufferSize = 65536; static GZIPInputStream gzInputStream(File f) { try { return gzInputStream(new FileInputStream(f)); } catch (Exception __e) { throw rethrow(__e); } } static GZIPInputStream gzInputStream(File f, int bufferSize) { try { return gzInputStream(new FileInputStream(f), bufferSize); } catch (Exception __e) { throw rethrow(__e); } } static GZIPInputStream gzInputStream(InputStream in) { return gzInputStream(in, gzInputStream_defaultBufferSize); } static GZIPInputStream gzInputStream(InputStream in, int bufferSize) { try { return _registerIOWrap(new GZIPInputStream(in, gzInputStream_defaultBufferSize), in); } catch (Exception __e) { throw rethrow(__e); } } static A printException(A e) { printStackTrace(e); return e; } static A vm_generalMap_getOrCreate(Object key, F0 create) { return vm_generalMap_getOrCreate(key, f0ToIF0(create)); } static A vm_generalMap_getOrCreate(Object key, IF0 create) { Map generalMap = vm_generalMap(); if (generalMap == null) return null; // must be x30 init synchronized(generalMap) { // should switch to locks here A a = (A) (vm_generalMap_get(key)); if (a == null) vm_generalMap_put(key, a = create == null ? null : create.get()); return a; } } static A callF_gen(F0 f) { return f == null ? null : f.get(); } static B callF_gen(F1 f, A a) { return f == null ? null : f.get(a); } static A callF_gen(IF0 f) { return f == null ? null : f.get(); } static B callF_gen(IF1 f, A a) { return f == null ? null : f.get(a); } static B callF_gen(A a, IF1 f) { return f == null ? null : f.get(a); } static C callF_gen(IF2 f, A a, B b) { return f == null ? null : f.get(a, b); } static void callF_gen(VF1 f, A a) { { if (f != null) f.get(a); } } static void callF_gen(A a, IVF1 f) { { if (f != null) f.get(a); } } static void callF_gen(IVF1 f, A a) { { if (f != null) f.get(a); } } static Object callF_gen(Runnable r) { { if (r != null) r.run(); } return null; } static Object callF_gen(Object f, Object... args) { return callF(f, args); } static int shorten_default = 100; static String shorten(CharSequence s) { return shorten(s, shorten_default); } static String shorten(CharSequence s, int max) { return shorten(s, max, "..."); } static String shorten(CharSequence s, int max, String shortener) { if (s == null) return ""; if (max < 0) return str(s); return s.length() <= max ? str(s) : subCharSequence(s, 0, min(s.length(), max-l(shortener))) + shortener; } static String shorten(int max, CharSequence s) { return shorten(s, max); } static int parseHexChar(char c) { if (c >= '0' && c <= '9') return charDiff(c, '0'); if (c >= 'a' && c <= 'f') return charDiff(c, 'a')+10; if (c >= 'A' && c <= 'F') return charDiff(c, 'A')+10; return -1; } static boolean argumentCompatibleWithType(Object arg, Class type) { return arg == null ? !type.isPrimitive() : isInstanceX(type, arg); } static HashSet litset(A... items) { return lithashset(items); } static long parseLong(String s) { if (empty(s)) return 0; return Long.parseLong(dropSuffix("L", s)); } static long parseLong(Object s) { return Long.parseLong((String) s); } static String _userHome; static String userHome() { if (_userHome == null) return actualUserHome(); return _userHome; } static File userHome(String path) { return new File(userDir(), path); } static String fsI(String id) { return formatSnippetID(id); } static String fsI(long id) { return formatSnippetID(id); } static boolean isImageServerSnippet(long id) { return id >= 1100000 && id < 1200000; } static String imageServerLink(String md5OrID) { if (possibleMD5(md5OrID)) return "https://botcompany.de/images/md5/" + md5OrID; return imageServerLink(parseSnippetID(md5OrID)); } static String imageServerLink(long id) { return "https://botcompany.de/images/" + id; } // changes & returns canvas static BufferedImage drawImageOnImage(Image img, BufferedImage canvas, int x, int y) { createGraphics(canvas).drawImage(img, x, y, null); return canvas; } static BufferedImage drawImageOnImage(Image img, BufferedImage canvas) { return drawImageOnImage(img, canvas, 0, 0); } static Graphics2D graphics(BufferedImage img) { return imageGraphics(img); } static boolean eqic(String a, String b) { if ((a == null) != (b == null)) return false; if (a == null) return true; return a.equalsIgnoreCase(b); } static boolean eqic(Symbol a, Symbol b) { return eq(a, b); } static boolean eqic(Symbol a, String b) { return eqic(asString(a), b); } static boolean eqic(char a, char b) { if (a == b) return true; char u1 = Character.toUpperCase(a); char u2 = Character.toUpperCase(b); if (u1 == u2) return true; return Character.toLowerCase(u1) == Character.toLowerCase(u2); } static ArrayList litlist(A... a) { ArrayList l = new ArrayList(a.length); for (A x : a) l.add(x); return l; } static String collapseWord(String s) { if (s == null) return ""; StringBuilder buf = new StringBuilder(); for (int i = 0; i < l(s); i++) if (i == 0 || !charactersEqualIC(s.charAt(i), s.charAt(i-1))) buf.append(s.charAt(i)); return buf.toString(); } static List toLowerCase(List strings) { List x = new ArrayList(); for (String s : strings) x.add(s.toLowerCase()); return x; } static String[] toLowerCase(String[] strings) { String[] x = new String[l(strings)]; for (int i = 0; i < l(strings); i++) x[i] = strings[i].toLowerCase(); return x; } static String toLowerCase(String s) { return s == null ? "" : s.toLowerCase(); } static String firstWord2(String s) { s = xltrim(s); if (empty(s)) return ""; if (isLetterOrDigit(first(s))) return takeCharsWhile(__33 -> isLetterOrDigit(__33), s); else return "" + first(s); } static Runnable asRunnable(Object o) { return toRunnable(o); } static void _inheritThreadInfo(Object info) { _threadInheritInfo(info); } static Throwable innerException2(Throwable e) { if (e == null) return null; while (empty(e.getMessage()) && e.getCause() != null) e = e.getCause(); return e; } static void listThreadLocalAdd(ThreadLocal> tl, A a) { List l = tl.get(); if (l == null) tl.set(l = new ArrayList()); l.add(a); } static A listThreadLocalPopLast(ThreadLocal> tl) { List l = tl.get(); if (l == null) return null; A a = popLast(l); if (empty(l)) tl.set(null); return a; } static TreeSet caseInsensitiveSet_treeSet() { return new TreeSet(caseInsensitiveComparator()); } static TreeSet caseInsensitiveSet_treeSet(Collection c) { return toCaseInsensitiveSet_treeSet(c); } static A[] newObjectArrayOfSameType(A[] a) { return newObjectArrayOfSameType(a, a.length); } static A[] newObjectArrayOfSameType(A[] a, int n) { return (A[]) Array.newInstance(a.getClass().getComponentType(), n); } static boolean regionMatchesIC(String a, int offsetA, String b, int offsetB, int len) { return a != null && a.regionMatches(true, offsetA, b, offsetB, len); } static List collectField(Iterable c, String field) { List l = new ArrayList(); if (c != null) for (Object a : c) l.add(getOpt(a, field)); return l; } static List collectField(String field, Iterable c) { return collectField(c, field); } static TimeZone getTimeZone(String name) { return TimeZone.getTimeZone(name); } static String standardTimeZone_name = "Europe/Berlin"; static String standardTimeZone() { return standardTimeZone_name; } static List getClasses(Object[] array) { List l = emptyList(l(array)); for (Object o : array) l.add(_getClass(o)); return l; } static void multiMapPut(Map> map, A a, B b) { List l = map.get(a); if (l == null) map.put(a, l = new ArrayList()); l.add(b); } static void multiMapPut(MultiMap mm, A key, B value) { if (mm != null && key != null && value != null) mm.put(key, value); } static int identityHashCode(Object o) { return System.identityHashCode(o); } static boolean methodIsStatic(Method m) { return (m.getModifiers() & Modifier.STATIC) != 0; } static boolean nemptyString(String s) { return s != null && s.length() > 0; } static int listL(Collection l) { return l == null ? 0 : l.size(); } static boolean neq(Object a, Object b) { return !eq(a, b); } static B mapPutOrRemove(Map map, A key, B value) { if (map != null && key != null) if (value != null) return map.put(key, value); else return map.remove(key); return null; } static Set identityHashSet() { return Collections.newSetFromMap(new IdentityHashMap()); } static LinkedHashMap syncMapPut2_createLinkedHashMap(LinkedHashMap map, A key, B value) { if (key != null) if (value != null) { if (map == null) map = new LinkedHashMap(); synchronized(collectionMutex(map)) { map.put(key, value); } } else if (map != null) synchronized(collectionMutex(map)) { map.remove(key); } return map; } static void _registerIO(Object object, String path, boolean opened) { } static IF0 f0ToIF0(F0 f) { return f == null ? null : () -> f.get(); } static CharSequence subCharSequence(CharSequence s, int x) { return subCharSequence(s, x, s == null ? 0 : s.length()); } static CharSequence subCharSequence(CharSequence s, int x, int y) { if (s == null) return null; if (x < 0) x = 0; if (x >= s.length()) return ""; if (y < x) y = x; if (y > s.length()) y = s.length(); return s.subSequence(x, y); } static int charDiff(char a, char b) { return (int) a-(int) b; } static int charDiff(String a, char b) { return charDiff(stringToChar(a), b); } static HashSet lithashset(A... items) { HashSet set = new HashSet(); for (A a : items) set.add(a); return set; } static String actualUserHome_value; static String actualUserHome() { if (actualUserHome_value == null) { if (isAndroid()) actualUserHome_value = "/storage/emulated/0/"; else actualUserHome_value = System.getProperty("user.home"); } return actualUserHome_value; } static File actualUserHome(String sub) { return newFile(new File(actualUserHome()), sub); } static File userDir() { return new File(userHome()); } static File userDir(String path) { return new File(userHome(), path); } static String formatSnippetID(String id) { return "#" + parseSnippetID(id); } static String formatSnippetID(long id) { return "#" + id; } static boolean possibleMD5(String s) { return isMD5(s); } static Map createGraphics_modulators = synchroIdentityHashMap(); static Graphics2D createGraphics(BufferedImage img) { Graphics2D g = img.createGraphics(); Object mod = createGraphics_modulators.get(img); if (mod != null) callF(mod, g); return g; } // mod: voidfunc(Graphics2D) static void createGraphics_modulate(BufferedImage img, Object mod) { mapPut2(createGraphics_modulators, img, mod); } static ThreadLocal imageGraphics_antiAlias = new ThreadLocal(); static Graphics2D imageGraphics(BufferedImage img) { return !isFalse(imageGraphics_antiAlias.get()) ? antiAliasGraphics(img) : createGraphics(img); } static String asString(Object o) { return o == null ? null : o.toString(); } static boolean charactersEqualIC(char c1, char c2) { if (c1 == c2) return true; char u1 = Character.toUpperCase(c1); char u2 = Character.toUpperCase(c2); if (u1 == u2) return true; return Character.toLowerCase(u1) == Character.toLowerCase(u2); } static String xltrim(String s) { int i = 0, n = l(s); while (i < n && contains(" \t\r\n", s.charAt(i))) ++i; return substr(s, i); } static boolean isLetterOrDigit(char c) { return Character.isLetterOrDigit(c); } // pred: char -> bool static String takeCharsWhile(String s, Object pred) { int i = 0; while (i < l(s) && isTrue(callF(pred, s.charAt(i)))) ++i; return substring(s, 0, i); } static String takeCharsWhile(IF1 f, String s) { return takeCharsWhile(s, f); } static TreeSet toCaseInsensitiveSet_treeSet(Iterable c) { if (isCISet(c)) return (TreeSet) c; TreeSet set = caseInsensitiveSet_treeSet(); addAll(set, c); return set; } static TreeSet toCaseInsensitiveSet_treeSet(String... x) { TreeSet set = caseInsensitiveSet_treeSet(); addAll(set, x); return set; } static char stringToChar(String s) { if (l(s) != 1) throw fail("bad stringToChar: " + s); return firstChar(s); } static boolean isMD5(String s) { return l(s) == 32 && isLowerHexString(s); } static Map synchroIdentityHashMap() { return synchroMap(new IdentityHashMap()); } static void mapPut2(Map map, A key, B value) { if (map != null && key != null) if (value != null) map.put(key, value); else map.remove(key); } static Graphics2D antiAliasGraphics(BufferedImage img) { return antiAliasOn(createGraphics(img)); } static String substr(String s, int x) { return substring(s, x); } static String substr(String s, int x, int y) { return substring(s, x, y); } static boolean isCISet(Iterable l) { return l instanceof TreeSet && ((TreeSet) l).comparator() == caseInsensitiveComparator(); } static char firstChar(String s) { return s.charAt(0); } static boolean isLowerHexString(String s) { for (int i = 0; i < l(s); i++) { char c = s.charAt(i); if (c >= '0' && c <= '9' || c >= 'a' && c <= 'f') { // ok } else return false; } return true; } static Graphics2D antiAliasOn(Graphics2D g) { g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); return g; } static abstract class VF1 implements IVF1 { public abstract void get(A a); } // immutable, has strong refs // Do not run in a synchronized block - it goes wrong in the presence // of elaborate classloaders (like in Gazelle BEA) // see #1102990 and #1102991 final static class _MethodCache { final Class c; final HashMap> cache = new HashMap(); _MethodCache(Class c) { this.c = c; _init(); } void _init() { Class _c = c; java.lang.Module myModule = getClass().getModule(); boolean anyHiddenClasses = false; while (_c != null) { boolean exported = classIsExportedTo(_c, myModule); if (!exported) anyHiddenClasses = true; else for (Method m : _c.getDeclaredMethods()) if ((anyHiddenClasses || !isAbstract(m)) && !reflection_isForbiddenMethod(m)) multiMapPut(cache, m.getName(), makeAccessible(m)); _c = _c.getSuperclass(); } // add default methods - this might lead to a duplication // because the overridden method is also added, but it's not // a problem except for minimal performance loss. // If any classes in the hierarchy were inaccessible, we add // all interface methods (see test_callForbiddenMethodByReflection for a test) for (Class intf : allInterfacesImplementedBy(c)) for (Method m : intf.getDeclaredMethods()) if ((anyHiddenClasses || m.isDefault()) && !reflection_isForbiddenMethod(m)) multiMapPut(cache, m.getName(), makeAccessible(m)); } // Returns only matching methods Method findMethod(String method, Object[] args) { try { List m = cache.get(method); if (m == null) return null; int n = m.size(); for (int i = 0; i < n; i++) { Method me = m.get(i); if (call_checkArgs(me, args, false)) return me; } return null; } catch (Exception __e) { throw rethrow(__e); } } Method findStaticMethod(String method, Object[] args) { try { List m = cache.get(method); if (m == null) return null; int n = m.size(); for (int i = 0; i < n; i++) { Method me = m.get(i); if (isStaticMethod(me) && call_checkArgs(me, args, false)) return me; } return null; } catch (Exception __e) { throw rethrow(__e); } } //Cl allMethods() { ret allValues(cache); } } static class Matches { String[] m; Matches() {} Matches(String... m) { this.m = m;} String get(int i) { return i < m.length ? m[i] : null; } String unq(int i) { return unquote(get(i)); } String tlc(int i) { return unq(i).toLowerCase(); } boolean bool(int i) { return "true".equals(unq(i)); } String rest() { return m[m.length-1]; } // for matchStart int psi(int i) { return Integer.parseInt(unq(i)); } public String toString() { return "Matches(" + joinWithComma(quoteAll(asList(m))) + ")"; } public int hashCode() { return _hashCode(toList(m)); } public boolean equals(Object o) { return o instanceof Matches && arraysEqual(m, ((Matches) o).m); } } // both the outer outline and the outline of a hole are called a "trace" // should return outer outline first and then outline of holes // seems to return first point again at the end sometimes static class RegionBorder_innerPoints_v2 extends AbstractBorderTracer implements IFieldsToList{ IImageRegion region; RegionBorder_innerPoints_v2() {} RegionBorder_innerPoints_v2(IImageRegion region) { this.region = region;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + region + ")"; }public Object[] _fieldsToList() { return new Object[] {region}; } Rect bounds; // region bounds int x, y, dir; Iterator it; byte[] reachedInDirections; // bits 0 to 3 boolean tracing = false; int nTrace; // 1 for outline, 2+ for hole // directions (0 = right, 1 = down, 2 = left, 3 = up) static Pt[] directions = { pt(1, 0), pt(0, 1), pt(-1, 0), pt(0, -1) // r, d, l, u }; void init() { bounds = region.bounds(); reachedInDirections = new byte[area(bounds)]; it = region.pixelIterator(); } public boolean step() { if (reachedInDirections == null) init(); if (tracing) return walkOnePixel(); else return findFirstPixel(); } boolean walkOnePixel() { int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; if ((reachedInDirections[posInBounds] & (1 << dir)) != 0) { traceDone(); tracing = false; return includeHoles; } reachedInDirections[posInBounds] |= (byte) 1 << dir; foundPoint(x, y); for (int turn = 3; turn <= 6; turn++) { // try left, straight, right, back int newDir = (dir+turn) & 3; Pt d = directions[newDir]; int x2 = x+d.x, y2 = y+d.y; boolean b = region.contains(x2, y2); if (b) { x = x2; y = y2; dir = newDir; return true; } } return true; // no black pixels found in any direction - region must be a single pixel } boolean findFirstPixel() { // search for first border pixel if (!it.hasNext()) return false; // done Pt p = it.next(); x = p.x; y = p.y; int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; if (reachedInDirections[posInBounds] != 0) return true; // seen pixel before // if pixel above is empty, walk to the right if (!region.contains(x, y-1)) { startTrace(0); return true; } // if pixel on the left is empty, walk upwards if (!region.contains(x-1, y)) { startTrace(3); return true; } // if pixel on the right is empty, walk downwards if (!region.contains(x+1, y)) { startTrace(1); return true; } // if pixel below is empty, walk left if (!region.contains(x, y+1)) { startTrace(2); return true; } // not a border pixel, continue search return true; } void startTrace(int dir) { this.dir = dir; // mark point reached from all directions in next step int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; reachedInDirections[posInBounds] = (byte) (15 & ~(1 << dir)); tracing = true; newTrace(++nTrace > 1); } void foundPoint(int x, int y) { foundPoint(pt(x, y)); } // get all points as list List allPoints_cache; List allPoints() { if (allPoints_cache == null) allPoints_cache = allPoints_load(); return allPoints_cache;} List allPoints_load() { PtBuffer l = new PtBuffer(); onFoundPoint(p -> l.add(p)); run(); return l; } // get outline as OnePath OnePath onePath_cache; OnePath onePath() { if (onePath_cache == null) onePath_cache = onePath_load(); return onePath_cache;} OnePath onePath_load() { includeHoles(false); return new OnePath(allPoints(), true); } // or as OnePathWithOrigin OnePathWithOrigin onePathWithOrigin_cache; OnePathWithOrigin onePathWithOrigin() { if (onePathWithOrigin_cache == null) onePathWithOrigin_cache = onePathWithOrigin_load(); return onePathWithOrigin_cache;} OnePathWithOrigin onePathWithOrigin_load() { includeHoles(false); return new OnePathWithOrigin(allPoints(), true); } // for debugging void runAndPrint() { onNewTrace(hole -> print(!hole ? "new outline" : "new hole")); onTraceDone(() -> print("traceDone")); onFoundPoint(p -> print("foundPoint " + p)); stepMaxWithStats(this, 10000); } boolean tracingHole() { return nTrace > 1; } } // for the version with MasterSymbol (used WAY back in "Smart Bot"!) see #1010608 static class Symbol implements CharSequence { String text; Symbol() {} Symbol(String text, boolean dummy) { this.text = text;} // weird signature to prevent accidental calling public int hashCode() { return _hashCode(text); } public String toString() { return text; } public boolean equals(Object o) { return this == o; } // implementation of CharSequence methods public int length() { return text.length(); } public char charAt(int index) { return text.charAt(index); } public CharSequence subSequence(int start, int end) { return text.substring(start, end); } } // size: // 64 bytes for 0 to 1 elements // 96 bytes for 2 to 4 elements /* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * !# */ static class CompactHashMap extends CompactAbstractMap { final static int INITIAL_SIZE = 3; final static double LOAD_FACTOR = 0.6; // This object is used to represent a null KEY (null value are kept as is) final static Object nullObject = new Object(); /** * When a key is deleted this object is put into the hashtable in * its place, so that other entries with the same key (collisions) * further down the hashtable are not lost after we delete an object * in the collision chain. */ final static Object deletedObject = new Object(); int elements; int freecells; Object[] table; // key, value, key, value, ... //int modCount; CompactHashMap() { this(INITIAL_SIZE); } CompactHashMap(int size) { table = new Object[(size==0 ? 1 : size)*2]; elements = 0; freecells = tableSize(); //modCount = 0; } // TODO: allocate smarter CompactHashMap(Map map) { this(0); if (map != null) putAll(map); } // ===== MAP IMPLEMENTATION ============================================= /** * Returns the number of key/value mappings in this map. */ public synchronized int size() { return elements; } /** * Returns true if this map contains no mappings. */ public synchronized boolean isEmpty() { return elements == 0; } /** * Removes all key/value mappings in the map. */ public synchronized void clear() { elements = 0; for (int ix = 0; ix < tableSize(); ix++) { key(ix, null); value(ix, null); } freecells = tableSize(); //modCount++; } /** * Returns true if this map contains the specified key. */ public synchronized boolean containsKey(Object k) { return key(findKeyIndex(k)) != null; } /** * Returns true if this map contains the specified value. */ public synchronized boolean containsValue(Object v) { if (v == null) v = (V)nullObject; for (int ix = 0; ix < tableSize(); ix++) if (value(ix) != null && value(ix).equals(v)) return true; return false; } /** * Returns a read-only set view of the map's keys. */ public synchronized Set> entrySet() { return new EntrySet(); } /** * Removes the mapping with key k, if there is one, and returns its * value, if there is one, and null if there is none. */ public synchronized V remove(Object k) { int index = findKeyIndex(k); // we found the right position, now do the removal if (key(index) != null) { // we found the object // same problem here as with put V v = value(index); key(index, deletedObject); value(index, deletedObject); //modCount++; elements--; return v; } else // we did not find the key return null; } /** * Adds the specified mapping to this map, returning the old value for * the mapping, if there was one. */ public synchronized V put(K k, V v) { if (k == null) k = (K)nullObject; int hash = k.hashCode(); int index = (hash & 0x7FFFFFFF) % tableSize(); int offset = 1; int deletedix = -1; // search for the key (continue while !null and !this key) while(key(index) != null && !(key(index).hashCode() == hash && key(index).equals(k))) { // if there's a deleted mapping here we can put this mapping here, // provided it's not in here somewhere else already if (key(index) == deletedObject) deletedix = index; index = ((index + offset) & 0x7FFFFFFF) % tableSize(); offset = offset*2 + 1; if (offset == -1) offset = 2; } if (key(index) == null) { // wasn't present already if (deletedix != -1) // reusing a deleted cell index = deletedix; else freecells--; //modCount++; elements++; key(index, k); value(index, v); // rehash with increased capacity if (1 - (freecells / (double) tableSize()) > LOAD_FACTOR) rehash(tableSize()*2 + 1); return null; } else { // was there already //modCount++; V oldv = value(index); value(index, v); return oldv; } } /** * INTERNAL: Rehashes the hashmap to a bigger size. */ void rehash(int newCapacity) { int oldCapacity = tableSize(); Object[] newTable = new Object[newCapacity*2]; for (int ix = 0; ix < oldCapacity; ix++) { Object k = key(ix); if (k == null || k == deletedObject) continue; int hash = k.hashCode(); int index = (hash & 0x7FFFFFFF) % newCapacity; int offset = 1; // search for the key while(newTable[index*2] != null) { // no need to test for duplicates index = ((index + offset) & 0x7FFFFFFF) % newCapacity; offset = offset*2 + 1; if (offset == -1) offset = 2; } newTable[index*2] = k; newTable[index*2+1] = value(ix); } table = newTable; freecells = tableSize() - elements; } /** * Returns the value for the key k, if there is one, and null if * there is none. */ public synchronized V get(Object k) { return value(findKeyIndex(k)); } /** * Returns a virtual read-only collection containing all the values * in the map. */ public synchronized Collection values() { return new ValueCollection(); } /** * Returns a virtual read-only set of all the keys in the map. */ public synchronized Set keySet() { return new KeySet(); } // --- Internal utilities final int findKeyIndex(Object k) { if (k == null) k = nullObject; int hash = k.hashCode(); int index = (hash & 0x7FFFFFFF) % tableSize(); int offset = 1; // search for the key (continue while !null and !this key) while(key(index) != null && !(key(index).hashCode() == hash && key(index).equals(k))) { index = ((index + offset) & 0x7FFFFFFF) % tableSize(); offset = offset*2 + 1; if (offset == -1) offset = 2; } return index; } // --- Key set class KeySet extends AbstractSet { public int size() { synchronized(CompactHashMap.this) { return elements; }} public boolean contains(Object k) { synchronized(CompactHashMap.this) { return containsKey(k); }} public Iterator iterator() { synchronized(CompactHashMap.this) { return new KeyIterator(); }} } class KeyIterator implements Iterator { private int ix; private KeyIterator() { synchronized(CompactHashMap.this) { // walk up to first value, so that hasNext() and next() return // correct results for (; ix < tableSize(); ix++) if (value(ix) != null && key(ix) != deletedObject) break; } } public boolean hasNext() { synchronized(CompactHashMap.this) { return ix < tableSize(); }} public void remove() { throw new UnsupportedOperationException("Collection is read-only"); } public K next() { synchronized(CompactHashMap.this) { if (ix >= tableSize()) throw new NoSuchElementException(); K key = (K) key(ix++); // walk up to next value for (; ix < tableSize(); ix++) if (key(ix) != null && key(ix) != deletedObject) break; // ix now either points to next key, or outside array (if no next) return key; }} } // --- Entry set class EntrySet extends AbstractSet> { public int size() { synchronized(CompactHashMap.this) { return elements; }} public boolean contains(Object o) { synchronized(CompactHashMap.this) { if (o instanceof Map.Entry) { Object key = ((Map.Entry) o).getKey(); if (!containsKey((Map.Entry) o)) return false; return eq(((Map.Entry) o).getValue(), get(key)); } return false; }} public Iterator> iterator() { return new EntryIterator(); } } class EntryIterator implements Iterator> { private int ix; private EntryIterator() { synchronized(CompactHashMap.this) { // walk up to first value, so that hasNext() and next() return // correct results for (; ix < tableSize(); ix++) if (value(ix) != null && key(ix) != deletedObject) break; } } public boolean hasNext() { synchronized(CompactHashMap.this) { return ix < tableSize(); }} public void remove() { throw new UnsupportedOperationException("Collection is read-only"); } public Map.Entry next() { synchronized(CompactHashMap.this) { if (ix >= tableSize()) throw new NoSuchElementException(); K key = key(ix); V val = value(ix); ++ix; // walk up to next value for (; ix < tableSize(); ix++) if (key(ix) != null && key(ix) != deletedObject) break; // ix now either points to next key, or outside array (if no next) return simpleMapEntry(key, val); }} } // --- Value collection class ValueCollection extends AbstractCollection { public int size() { synchronized(CompactHashMap.this) { return elements; }} public Iterator iterator() { return new ValueIterator(); } public boolean contains(Object v) { return containsValue(v); } } class ValueIterator implements Iterator { private int ix; private ValueIterator() { synchronized(CompactHashMap.this) { // walk up to first value, so that hasNext() and next() return // correct results for (; ix < table.length/2; ix++) if (value(ix) != null && value(ix) != deletedObject) break; } } public boolean hasNext() { synchronized(CompactHashMap.this) { return ix < tableSize(); }} public void remove() { throw new UnsupportedOperationException("Collection is read-only"); } public V next() { synchronized(CompactHashMap.this) { if (ix >= tableSize()) throw new NoSuchElementException(); V value = (V) value(ix++); // walk up to next value for (; ix < tableSize(); ix++) if (value(ix) != null && value(ix) != deletedObject) break; // ix now either points to next value, or outside array (if no next) return value; }} } K key(int i) { return (K) table[i*2]; } void key(int i, Object key) { table[i*2] = key; } V value(int i) { return (V) table[i*2+1]; } void value(int i, Object value) { table[i*2+1] = value; } int tableSize() { return table.length/2; } } static class WidthAndHeightImpl extends Meta implements WidthAndHeight , IFieldsToList{ int width; int height; WidthAndHeightImpl() {} WidthAndHeightImpl(int width, int height) { this.height = height; this.width = width;}public Object[] _fieldsToList() { return new Object[] {width, height}; } public int getWidth() { return width; } public int getHeight() { return height; } public String toString() { return n2(width) + "*" + n2(height) + " px"; } } interface IMakeEmptyClone { Object makeEmptyClone(); } static class Complex implements IFieldsToList{ static final String _fieldOrder = "re im"; double re; double im; Complex() {} Complex(double re, double im) { this.im = im; this.re = re;} public boolean equals(Object o) { if (!(o instanceof Complex)) return false; Complex __1 = (Complex) o; return re == __1.re && im == __1.im; } public int hashCode() { int h = -1679819632; h = boostHashCombine(h, _hashCode(re)); h = boostHashCombine(h, _hashCode(im)); return h; } public Object[] _fieldsToList() { return new Object[] {re, im}; } double abs() { return sqrt(re*re+im*im); } double re() { return re; } double im() { return im; } final double angle(){ return phase(); } double phase() { return Math.atan2(im, re); } double fracAngle() { return fracNonNeg(angle()/twoPi()); } // angle as 0 to 1 public String toString() { if (im != 0) return re == 0 ? im + "i" : re + plusPrefixUnlessMinus(str(im)) + "i"; else return str(re); } } // In the newest pinging system (with flag PingV3), a ping source // is the object that "allows" some code to run. // When that code calls ping(), the ping source's action (if defined) // is triggered. // This allows randomly interrupting code execution, for example. static class PingSource { // returns true if it slept final public PingSource setAction(IF0 action){ return action(action); } public PingSource action(IF0 action) { this.action = action; return this; } final public IF0 getAction(){ return action(); } public IF0 action() { return action; } volatile IF0 action; // optional description of this ping source String text; // optional thread pool that this ping source likes to run in ThreadPool threadPool; PingSource() {} PingSource(ThreadPool threadPool) { this.threadPool = threadPool;} PingSource(ThreadPool threadPool, String text) { this.text = text; this.threadPool = threadPool;} PingSource(IF0 action) { this.action = action;} // returns true if it slept final boolean get() { var a = action; return a != null && a.get(); } final void ping() { var a = action; if (a != null) a.get(); } void cancel() { action = new Cancelled(); } class Cancelled implements IF0 { public Boolean get() { throw new PingSourceCancelledException(PingSource.this); } } class Encapsulated implements Runnable , IFieldsToList{ Runnable r; Encapsulated() {} Encapsulated(Runnable r) { this.r = r;}public Object[] _fieldsToList() { return new Object[] {r}; } public void run() { try { //System.out.println("Encapsulated running: " + r); try { pingSource_tl().set(PingSource.this); //System.out.println("Ping source set"); ping(); r.run(); //System.out.println("Done running"); } finally { //System.out.println("Finally"); pingSource_tl().set(null); } } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return PingSource.this + ": " + r; } } void dO(Runnable r) { if (r == null) return; threadPool.acquireThreadOrQueue(new Encapsulated(r)); } public String toString() { String t = text; return nempty(t) ? t : super.toString(); } ISleeper_v2 sleeper() { return threadPool.sleeper(); } } // 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); } } // Note: This does have the values problem (complicated values can cause memory leaks) static class BetterThreadLocal { Map map = newWeakHashMap(); BetterThreadLocal() {} BetterThreadLocal(A value) { set(value); } boolean isSet() { return map.containsKey(currentThread()); } A get() { if (map.containsKey(currentThread())) return map.get(currentThread()); A value = initialValue(); set(value); return value; } A get(Thread thread) { return thread == null ? null : map.get(thread); } void set(A a) { map.put(currentThread(), a); } public A initialValue() { return null; } } // AppendableChain has one "smart" head element (with size counter // and pointer to the chain's last element), all the other nodes are // maximally simple (MinimalChain). // This allows O(1) front insertion, front removal and back insertion // (not removal at the back though) which is fine for what I need this // for (event queues). // // Stefan Reich, Oct 21 static class AppendableChain extends MinimalChain implements Iterable, IntSize { MinimalChain last; // pointer to last element in chain (which may be us) final public int getSize(){ return size(); } public int size() { return size; } int size; // total length of chain AppendableChain() {} // only used internally AppendableChain(A element) { this.element = element; size = 1; last = this; } // intermediate constructor called by itemPlusChain() AppendableChain(A element, AppendableChain next) { this.next = next; this.element = element; if (next == null) return; MinimalChain b = new MinimalChain(); b.element = next.element; b.next = next.next; this.next = b; last = next.last; size = next.size+1; } public String toString() { return str(toList()); } // append at the end boolean add(A a) { MinimalChain newLast = new MinimalChain(a); last.next = newLast; last = newLast; ++size; return true; } // drop first element AppendableChain popFirst() { if (next == null) return null; element = next.element; if (last == next) last = this; next = next.next; --size; return this; } ArrayList toList() { ArrayList l = emptyList(size); MinimalChain c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } //public Iterator iterator() { ret toList().iterator(); } class ACIt extends IterableIterator < A > { MinimalChain c = AppendableChain.this; public boolean hasNext() { return c != null; } public A next() { var a = c.element; c = c.next; return a; } } public IterableIterator iterator() { return new ACIt(); } } static interface IResourceLoader { String loadSnippet(String snippetID); String getTranspiled(String snippetID); // with libs int getSnippetType(String snippetID); String getSnippetTitle(String snippetID); File loadLibrary(String snippetID); //ifndef NoJavaXJar default File pathToJavaXJar() { return pathToJavaxJar_noResourceLoader(); } //endifndef // may return null, then caller compiles themselves default File getSnippetJar(String snippetID, String transpiledSrc) { return null; } } /* * @(#)WeakHashMap.java 1.5 98/09/30 * * Copyright 1998 by Sun Microsystems, Inc., * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. * All rights reserved. * * This software is the confidential and proprietary information * of Sun Microsystems, Inc. ("Confidential Information"). You * shall not disclose such Confidential Information and shall use * it only in accordance with the terms of the license agreement * you entered into with Sun. */ // From https://github.com/mernst/plume-lib/blob/df0bfafc3c16848d88f4ea0ef3c8bf3367ae085e/java/src/plume/WeakHasherMap.java static final class WeakHasherMap extends AbstractMap implements Map { private Hasher hasher = null; /*@Pure*/ private boolean keyEquals(Object k1, Object k2) { return (hasher==null ? k1.equals(k2) : hasher.equals(k1, k2)); } /*@Pure*/ private int keyHashCode(Object k1) { return (hasher==null ? k1.hashCode() : hasher.hashCode(k1)); } // The WeakKey class can't be static because it depends on the hasher. // That in turn means that its methods can't be static. // However, I need to be able to call the methods such as create() that // were static in the original version of this code. // This finesses that. private /*@Nullable*/ WeakKey WeakKeyCreate(K k) { if (k == null) return null; else return new WeakKey(k); } private /*@Nullable*/ WeakKey WeakKeyCreate(K k, ReferenceQueue q) { if (k == null) return null; else return new WeakKey(k, q); } // Cannot be a static class: uses keyHashCode() and keyEquals() private final class WeakKey extends WeakReference { private int hash; /* Hashcode of key, stored here since the key may be tossed by the GC */ private WeakKey(K k) { super(k); hash = keyHashCode(k); } private /*@Nullable*/ WeakKey create(K k) { if (k == null) return null; else return new WeakKey(k); } private WeakKey(K k, ReferenceQueue q) { super(k, q); hash = keyHashCode(k); } private /*@Nullable*/ WeakKey create(K k, ReferenceQueue q) { if (k == null) return null; else return new WeakKey(k, q); } /* A WeakKey is equal to another WeakKey iff they both refer to objects that are, in turn, equal according to their own equals methods */ /*@Pure*/ @Override public boolean equals(/*@Nullable*/ Object o) { if (o == null) return false; // never happens if (this == o) return true; // This test is illegal because WeakKey is a generic type, // so use the getClass hack below instead. // if (!(o instanceof WeakKey)) return false; if (!(o.getClass().equals(WeakKey.class))) return false; Object t = this.get(); @SuppressWarnings("unchecked") Object u = ((WeakKey)o).get(); if ((t == null) || (u == null)) return false; if (t == u) return true; return keyEquals(t, u); } /*@Pure*/ @Override public int hashCode() { return hash; } } /* Hash table mapping WeakKeys to values */ private HashMap hash; /* Reference queue for cleared WeakKeys */ private ReferenceQueue queue = new ReferenceQueue(); /* Remove all invalidated entries from the map, that is, remove all entries whose keys have been discarded. This method should be invoked once by each public mutator in this class. We don't invoke this method in public accessors because that can lead to surprising ConcurrentModificationExceptions. */ @SuppressWarnings("unchecked") private void processQueue() { WeakKey wk; while ((wk = (WeakKey)queue.poll()) != null) { // unchecked cast hash.remove(wk); } } /* -- Constructors -- */ /** * Constructs a new, empty WeakHashMap with the given * initial capacity and the given load factor. * * @param initialCapacity the initial capacity of the * WeakHashMap * * @param loadFactor the load factor of the WeakHashMap * * @throws IllegalArgumentException If the initial capacity is less than * zero, or if the load factor is * nonpositive */ public WeakHasherMap(int initialCapacity, float loadFactor) { hash = new HashMap(initialCapacity, loadFactor); } /** * Constructs a new, empty WeakHashMap with the given * initial capacity and the default load factor, which is * 0.75. * * @param initialCapacity the initial capacity of the * WeakHashMap * * @throws IllegalArgumentException If the initial capacity is less than * zero */ public WeakHasherMap(int initialCapacity) { hash = new HashMap(initialCapacity); } /** * Constructs a new, empty WeakHashMap with the default * capacity and the default load factor, which is 0.75. */ public WeakHasherMap() { hash = new HashMap(); } /** * Constructs a new, empty WeakHashMap with the default * capacity and the default load factor, which is 0.75. * The WeakHashMap uses the specified hasher for hashing * keys and comparing them for equality. * @param h the Hasher to use when hashing values for this map */ public WeakHasherMap(Hasher h) { hash = new HashMap(); hasher = h; } /* -- Simple queries -- */ /** * Returns the number of key-value mappings in this map. * Note: In contrast to most implementations of the * Map interface, the time required by this operation is * linear in the size of the map. */ /*@Pure*/ @Override public int size() { return entrySet().size(); } /** * Returns true if this map contains no key-value mappings. */ /*@Pure*/ @Override public boolean isEmpty() { return entrySet().isEmpty(); } /** * Returns true if this map contains a mapping for the * specified key. * * @param key the key whose presence in this map is to be tested */ /*@Pure*/ @Override public boolean containsKey(Object key) { @SuppressWarnings("unchecked") K kkey = (K) key; return hash.containsKey(WeakKeyCreate(kkey)); } /* -- Lookup and modification operations -- */ /** * Returns the value to which this map maps the specified key. * If this map does not contain a value for this key, then return * null. * * @param key the key whose associated value, if any, is to be returned */ /*@Pure*/ @Override public /*@Nullable*/ V get(Object key) { // type of argument is Object, not K @SuppressWarnings("unchecked") K kkey = (K) key; return hash.get(WeakKeyCreate(kkey)); } /** * Updates this map so that the given key maps to the given * value. If the map previously contained a mapping for * key then that mapping is replaced and the previous value is * returned. * * @param key the key that is to be mapped to the given * value * @param value the value to which the given key is to be * mapped * * @return the previous value to which this key was mapped, or * null if if there was no mapping for the key */ @Override public V put(K key, V value) { processQueue(); return hash.put(WeakKeyCreate(key, queue), value); } /** * Removes the mapping for the given key from this map, if * present. * * @param key the key whose mapping is to be removed * * @return the value to which this key was mapped, or null if * there was no mapping for the key */ @Override public V remove(Object key) { // type of argument is Object, not K processQueue(); @SuppressWarnings("unchecked") K kkey = (K) key; return hash.remove(WeakKeyCreate(kkey)); } /** * Removes all mappings from this map. */ @Override public void clear() { processQueue(); hash.clear(); } /* -- Views -- */ /* Internal class for entries */ // This can't be static, again because of dependence on hasher. @SuppressWarnings("TypeParameterShadowing") private final class Entry implements Map.Entry { private Map.Entry ent; private K key; /* Strong reference to key, so that the GC will leave it alone as long as this Entry exists */ Entry(Map.Entry ent, K key) { this.ent = ent; this.key = key; } /*@Pure*/ @Override public K getKey() { return key; } /*@Pure*/ @Override public V getValue() { return ent.getValue(); } @Override public V setValue(V value) { return ent.setValue(value); } /*@Pure*/ private boolean keyvalEquals(K o1, K o2) { return (o1 == null) ? (o2 == null) : keyEquals(o1, o2); } /*@Pure*/ private boolean valEquals(V o1, V o2) { return (o1 == null) ? (o2 == null) : o1.equals(o2); } /*@Pure*/ @SuppressWarnings("NonOverridingEquals") public boolean equals(Map.Entry e /* Object o*/) { // if (! (o instanceof Map.Entry)) return false; // Map.Entry e = (Map.Entry)o; return (keyvalEquals(key, e.getKey()) && valEquals(getValue(), e.getValue())); } /*@Pure*/ @Override public int hashCode() { V v; return (((key == null) ? 0 : keyHashCode(key)) ^ (((v = getValue()) == null) ? 0 : v.hashCode())); } } /* Internal class for entry sets */ private final class EntrySet extends AbstractSet> { Set> hashEntrySet = hash.entrySet(); @Override public Iterator> iterator() { return new Iterator>() { Iterator> hashIterator = hashEntrySet.iterator(); Map.Entry next = null; @Override public boolean hasNext() { while (hashIterator.hasNext()) { Map.Entry ent = hashIterator.next(); WeakKey wk = ent.getKey(); K k = null; if ((wk != null) && ((k = wk.get()) == null)) { /* Weak key has been cleared by GC */ continue; } next = new Entry(ent, k); return true; } return false; } @Override public Map.Entry next() { if ((next == null) && !hasNext()) throw new NoSuchElementException(); Map.Entry e = next; next = null; return e; } @Override public void remove() { hashIterator.remove(); } }; } /*@Pure*/ @Override public boolean isEmpty() { return !(iterator().hasNext()); } /*@Pure*/ @Override public int size() { int j = 0; for (Iterator> i = iterator(); i.hasNext(); i.next()) j++; return j; } @Override public boolean remove(Object o) { processQueue(); if (!(o instanceof Map.Entry)) return false; @SuppressWarnings("unchecked") Map.Entry e = (Map.Entry)o; // unchecked cast Object ev = e.getValue(); WeakKey wk = WeakKeyCreate(e.getKey()); Object hv = hash.get(wk); if ((hv == null) ? ((ev == null) && hash.containsKey(wk)) : hv.equals(ev)) { hash.remove(wk); return true; } return false; } /*@Pure*/ @Override public int hashCode() { int h = 0; for (Iterator> i = hashEntrySet.iterator(); i.hasNext(); ) { Map.Entry ent = i.next(); WeakKey wk = ent.getKey(); Object v; if (wk == null) continue; h += (wk.hashCode() ^ (((v = ent.getValue()) == null) ? 0 : v.hashCode())); } return h; } } private /*@Nullable*/ Set> entrySet = null; /** * Returns a Set view of the mappings in this map. */ /*@SideEffectFree*/ @Override public Set> entrySet() { if (entrySet == null) entrySet = new EntrySet(); return entrySet; } // find matching key K findKey(Object key) { processQueue(); K kkey = (K) key; // TODO: use replacement for HashMap to avoid reflection WeakKey wkey = WeakKeyCreate(kkey); WeakKey found = hashMap_findKey(hash, wkey); return found == null ? null : found.get(); } } static interface IF2_IntInt_Double { double get(int a, int b); } static class proxy_InvocationHandler implements InvocationHandler { Object target; proxy_InvocationHandler() {} proxy_InvocationHandler(Object target) { this.target = target;} public Object invoke(Object proxy, Method method, Object[] args) { return call(target, method.getName(), unnull(args)); } } // -has fast nextElement() and prevElement() // -design allows for more functions like reordering the list // -Saves up to 34% in space over LinkedHashSet // (e.g. 22% for a set of 1,000 Ints) static class CompactLinkedHashSet extends AbstractSet { UnsynchronizedCompactHashSet> entries = new UnsynchronizedCompactHashSet(); Entry head, tail; static class Entry { A value; Entry prev, next; public int hashCode() { return _hashCode(value); } // "magic" equals function for CompactHashSet lookup without temp object public boolean equals(Object o) { return o == this || eq(value, o); } } public boolean add(A a) { if (entries.contains(a)) return false; Entry n = new Entry(); n.value = a; n.prev = tail; if (tail != null) tail.next = n; tail = n; if (head == null) head = n; entries.add(n); return true; } public boolean remove(Object a) { return remove(entries.find(a)); } public boolean remove(Entry node) { if (node == null) return false; if (node.next != null) node.next.prev = node.prev; else tail = node.prev; if (node.prev != null) node.prev.next = node.next; else head = node.next; entries.remove(node); return true; } public int size() { return entries.size(); } public IterableIterator iterator() { return new IterableIterator() { Entry entry = head, prev = null; public boolean hasNext() { return entry != null; } public A next() { A a = entry.value; prev = entry; entry = entry.next; return a; } // untested public void remove() { if (prev == null) throw new IllegalStateException(); CompactLinkedHashSet.this.remove(prev); prev = null; } }; } public void clear() { entries.clear(); head = tail = null; } public boolean contains(Object a) { return entries.contains(a); } public A find(Object o) { Entry e = entries.find(o); return e == null ? null : e.value; } public A prevElement(A a) { Entry e = entries.find(a); if (e == null || e.prev == null) return null; return e.prev.value; } public A nextElement(A a) { Entry e = entries.find(a); if (e == null || e.next == null) return null; return e.next.value; } public A first() { return head == null ? null : head.value; } public A last() { return tail == null ? null : tail.value; } boolean removeIfSame(Object o) { A value = find(o); if (value == o) { remove(value); return true; } return false; } } // both the outer outline and the outline of a hole are called a "trace" // should return outer outline first and then outline of holes // seems to return first point again at the end sometimes static class RegionBorder_innerPoints_withDiagonals extends AbstractBorderTracer implements IFieldsToList{ IImageRegion region; RegionBorder_innerPoints_withDiagonals() {} RegionBorder_innerPoints_withDiagonals(IImageRegion region) { this.region = region;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + region + ")"; }public Object[] _fieldsToList() { return new Object[] {region}; } Rect bounds; // region bounds int x, y; int dir; // current direction (1 to 8) Iterator it; byte[] reachedInDirections; // bits 0 to 7 boolean tracing = false; int nTrace; // 1 for outline, 2+ for hole void init() { bounds = region.bounds(); reachedInDirections = new byte[area(bounds)]; it = region.pixelIterator(); } public boolean step() { if (reachedInDirections == null) init(); if (tracing) return walkOnePixel(); else return findFirstPixel(); } boolean walkOnePixel() { int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; if ((reachedInDirections[posInBounds] & (1 << (dir-1))) != 0) { traceDone(); tracing = false; return includeHoles; } reachedInDirections[posInBounds] |= (byte) 1 << (dir-1); foundPoint(x, y); // try left, half left, straight, half right, right, back for (int turn = -2; turn <= 4; turn++) { int newDir = modRange_incl(dir+turn, 1, 8); Pt d = onePathDirection(newDir); int x2 = x+d.x, y2 = y+d.y; boolean b = region.contains(x2, y2); if (b) { x = x2; y = y2; dir = newDir; return true; } } return true; // no black pixels found in any direction - region must be a single pixel } boolean findFirstPixel() { // search for first border pixel if (!it.hasNext()) return false; // done Pt p = it.next(); x = p.x; y = p.y; int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; if (reachedInDirections[posInBounds] != 0) return true; // seen pixel before // if pixel above is empty, walk to the right if (!region.contains(x, y-1)) { startTrace(4); return true; } // if pixel on the left is empty, walk upwards if (!region.contains(x-1, y)) { startTrace(2); return true; } // if pixel on the right is empty, walk downwards if (!region.contains(x+1, y)) { startTrace(6); return true; } // if pixel below is empty, walk left if (!region.contains(x, y+1)) { startTrace(8); return true; } // not a border pixel, continue search return true; } void startTrace(int dir) { this.dir = dir; // mark point reached from all directions in next step int posInBounds = (y-bounds.y)*bounds.w+x-bounds.x; reachedInDirections[posInBounds] = (byte) ~(1 << (dir-1)); tracing = true; newTrace(++nTrace > 1); } void foundPoint(int x, int y) { foundPoint(pt(x, y)); } // get all points as list List allPoints_cache; List allPoints() { if (allPoints_cache == null) allPoints_cache = allPoints_load(); return allPoints_cache;} List allPoints_load() { PtBuffer l = new PtBuffer(); onFoundPoint(p -> l.add(p)); run(); return l; } // get outline as OnePath OnePath onePath_cache; OnePath onePath() { if (onePath_cache == null) onePath_cache = onePath_load(); return onePath_cache;} OnePath onePath_load() { includeHoles(false); return new OnePath(allPoints(), true); } // or as OnePathWithOrigin OnePathWithOrigin onePathWithOrigin_cache; OnePathWithOrigin onePathWithOrigin() { if (onePathWithOrigin_cache == null) onePathWithOrigin_cache = onePathWithOrigin_load(); return onePathWithOrigin_cache;} OnePathWithOrigin onePathWithOrigin_load() { includeHoles(false); return new OnePathWithOrigin(allPoints(), true); } // for debugging void runAndPrint() { onNewTrace(hole -> print(!hole ? "new outline" : "new hole")); onTraceDone(() -> print("traceDone")); onFoundPoint(p -> print("foundPoint " + p)); stepMaxWithStats(this, 10000); } boolean tracingHole() { return nTrace > 1; } } interface G2Drawable { void drawOn(Graphics2D g); default void drawOn(BufferedImage img) { drawOn(img.createGraphics()); } } // has equals semantics so passing it to onChange() etc is idempotent static class ChangeTrigger implements Runnable , IFieldsToList{ ChangeTriggerable target; ChangeTrigger() {} ChangeTrigger(ChangeTriggerable target) { this.target = target;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + target + ")"; } public boolean equals(Object o) { if (!(o instanceof ChangeTrigger)) return false; ChangeTrigger __1 = (ChangeTrigger) o; return eq(target, __1.target); } public int hashCode() { int h = -1239972920; h = boostHashCombine(h, _hashCode(target)); return h; } public Object[] _fieldsToList() { return new Object[] {target}; } public void run() { try { { if (target != null) target.change(); } } catch (Exception __e) { throw rethrow(__e); } } } 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 class T3 { A a; B b; C c; T3() {} T3(A a, B b, C c) { this.c = c; this.b = b; this.a = a;} T3(T3 t) { a = t.a; b = t.b; c = t.c; } public int hashCode() { return _hashCode(a) + 2*_hashCode(b) - 4*_hashCode(c); } public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof T3)) return false; T3 t = (T3) o; return eq(a, t.a) && eq(b, t.b) && eq(c, t.c); } public String toString() { return "(" + quoteBorderless(a) + ", " + quoteBorderless(b) + ", " + quoteBorderless(c) + ")"; } } static interface IResourceHolder { A add(A a); Collection takeAll(); } static class BoundsFinder { int x1 = Integer.MAX_VALUE, y1 = Integer.MAX_VALUE, x2 = 0, y2 = 0; BoundsFinder() {} BoundsFinder(int w, int h) { x1 = w; y1 = h; } final void add(Pt p){ addPt(p); } void addPt(Pt p) { if (p != null) addPt(p.x, p.y); } final void add(int x, int y){ addPt(x, y); } void addPt(int x, int y) { if (x < x1) x1 = x; if (x > x2) x2 = x; if (y < y1) y1 = y; if (y > y2) y2 = y; } Rect get() { return rectFromPointsOrNull(x1, y1, x2+1, y2+1); } } static class BufferedImageWithMeta extends BufferedImage implements IMeta { BufferedImageWithMeta(ColorModel cm, WritableRaster raster, boolean isRasterPremultiplied, Hashtable properties) { super(cm, raster, isRasterPremultiplied, properties); } // 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); } } // it's unclear whether the end is inclusive or exclusive // (usually exclusive I guess) static class IntRange { int start, end; IntRange() {} IntRange(int start, int end) { this.end = end; this.start = start;} IntRange(IntRange r) { start = r.start; end = r.end; } public boolean equals(Object o) { return stdEq2(this, o); } public int hashCode() { return stdHash2(this); } final int length() { return end-start; } final boolean empty() { return start >= end; } final boolean isEmpty() { return start >= end; } static String _fieldOrder = "start end"; public String toString() { return "[" + start + ";" + end + "]"; } } static abstract class F0 { abstract A get(); } static class CachedPosterizer implements IPosterizer { IPosterizer posterizer; int[] cache; CachedPosterizer(IPosterizer posterizer) { this.posterizer = posterizer; makeCache(); } // 0..255 to 0..255 public int get(int brightness) { return cache[brightness]; } void makeCache() { cache = new int[256]; for (int brightness = 0; brightness < 256; brightness++) cache[brightness] = posterizer.get(brightness); } } static abstract class F1 { abstract B get(A a); } static final class IntPair implements Comparable , IFieldsToList{ int a; int b; IntPair() {} IntPair(int a, int b) { this.b = b; this.a = a;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + a + ", " + b + ")"; }public Object[] _fieldsToList() { return new Object[] {a, b}; } public boolean equals(Object o) { if (!(o instanceof IntPair)) return false; IntPair x = (IntPair) o; return a == x.a && b == x.b; } public int hashCode() { int h = -672893111; h = boostHashCombine(h, _hashCode(a)); h = boostHashCombine(h, _hashCode(b)); return h; } public int compareTo(IntPair p) { if (p == null) return 1; int pa = p.a; if (a < pa) return -1; if (a > pa) return 1; return Integer.compare(b, p.b); } } final static class DoubleRange implements Comparable { final public double getStart(){ return start(); } public double start() { return start; } double start; final public double getEnd(){ return end(); } public double end() { return end; } double end; DoubleRange() {} DoubleRange(double start, double end) { this.end = end; this.start = start;} public boolean equals(Object o) { return stdEq2(this, o); } public int hashCode() { return stdHash2(this); } double length() { return end-start; } boolean isEmpty() { return start >= end; } double center() { return (start+end)/2; } static String _fieldOrder = "start end"; public String toString() { return "[" + start + ";" + end + "]"; } @Override public int compareTo(DoubleRange r) { int c = cmp(start, r.start); if (c != 0) return c; return cmp(end, r.end); } } static interface Hasher { int hashCode(A a); boolean equals(A a, A b); } static abstract class CloseableIterableIterator extends IterableIterator implements AutoCloseable { public void close() throws Exception {} } static interface IF2 { C get(A a, B b); } static interface IF1 { B get(A a); } static class PersistableThrowable extends DynamicObject { String className; String msg; String stacktrace; PersistableThrowable() {} PersistableThrowable(Throwable e) { if (e == null) className = "Crazy Null Error"; else { className = getClassName(e).replace('/', '.'); msg = e.getMessage(); stacktrace = getStackTrace_noRecord(e); } } public String toString() { return nempty(msg) ? className + ": " + msg : className; } RuntimeException asRuntimeException() { return new Fail(this); } } static interface IVF2 { void get(A a, B b); } static class ScaledBWImageFromBWIntegralImage implements ScalingImageOpResult , IFieldsToList{ IBWIntegralImage img; int w; int h; ScaledBWImageFromBWIntegralImage() {} ScaledBWImageFromBWIntegralImage(IBWIntegralImage img, int w, int h) { this.h = h; this.w = w; this.img = img;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + img + ", " + w + ", " + h + ")"; }public Object[] _fieldsToList() { return new Object[] {img, w, h}; } BWImage result; public void run() { try { byte[] pixels = img instanceof BWIntegralImage ? scaledGrayBytesFromBWIntegralImage(((BWIntegralImage) img), w, h) : scaledGrayBytesFromBWIntegralImage(img, w, h); result = new BWImage(w, h, pixels); } catch (Exception __e) { throw rethrow(__e); } } public IBWIntegralImage scalingSrcImage() { return img; } } interface ScalingImageOpResult { Img scalingSrcImage(); } static class PingSourceCancelledException extends RuntimeException implements IFieldsToList{ PingSource pingSource; PingSourceCancelledException() {} PingSourceCancelledException(PingSource pingSource) { this.pingSource = pingSource;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + pingSource + ")"; }public Object[] _fieldsToList() { return new Object[] {pingSource}; } } static interface ISleeper_v2 { Sleeping doLater(Timestamp targetTime, Runnable r); public default Sleeping doAfter(double seconds, Runnable r) { return doLater(tsNow().plusSeconds(seconds), r); } } interface IntSize { int size(); } static class MinimalChain implements Iterable { A element; MinimalChain next; MinimalChain() {} MinimalChain(A element) { this.element = element;} MinimalChain(A element, MinimalChain next) { this.next = next; this.element = element;} public String toString() { return str(toList()); } ArrayList toList() { ArrayList l = new ArrayList(); MinimalChain c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } void setElement(A a) { element = a; } void setNext(MinimalChain next) { this.next = next; } // TODO: optimize public Iterator iterator() { return toList().iterator(); } A get() { return element; } } abstract static class AbstractBorderTracer extends AbstractSteppable { transient Set> onNewTrace; public AbstractBorderTracer onNewTrace(IVF1 f) { onNewTrace = createOrAddToSyncLinkedHashSet(onNewTrace, f); return this; } public AbstractBorderTracer removeNewTraceListener(IVF1 f) { main.remove(onNewTrace, f); return this; } public void newTrace(boolean isHole) { if (onNewTrace != null) for (var listener : onNewTrace) pcallF_typed(listener, isHole); } transient Set> onFoundPoint; public AbstractBorderTracer onFoundPoint(IVF1 f) { onFoundPoint = createOrAddToSyncLinkedHashSet(onFoundPoint, f); return this; } public AbstractBorderTracer removeFoundPointListener(IVF1 f) { main.remove(onFoundPoint, f); return this; } public void foundPoint(Pt p) { if (onFoundPoint != null) for (var listener : onFoundPoint) pcallF_typed(listener, p); } transient Set onTraceDone; public AbstractBorderTracer onTraceDone(Runnable r) { onTraceDone = createOrAddToSyncLinkedHashSet(onTraceDone, r); return this; } public AbstractBorderTracer removeTraceDoneListener(Runnable r) { main.remove(onTraceDone, r); return this; } public void traceDone() { if (onTraceDone != null) for (var listener : onTraceDone) pcallF_typed(listener); } final public AbstractBorderTracer setIncludeHoles(boolean includeHoles){ return includeHoles(includeHoles); } public AbstractBorderTracer includeHoles(boolean includeHoles) { this.includeHoles = includeHoles; return this; } final public boolean getIncludeHoles(){ return includeHoles(); } public boolean includeHoles() { return includeHoles; } boolean includeHoles = true; } // The idea is to leave max as the actual number of cores the system // has (numberOfCores()), and in case of being fully booked, raise an // alert (customerMustWaitAlert) which can be handled by a strategy // object (different reactions are possible). // If nothing is done in such an event, clients are processed serially // (no guarantees of order), split up among the available threads. /* SYNChronisation order: 1. PooledThread 2. ThreadPool */ static class ThreadPool implements AutoCloseable { int max = numberOfCores(); List all = new ArrayList(); Set used = new HashSet(); Set free = new HashSet(); boolean verbose, retired; // our own ping surce so we can start threads & keep them running class InternalPingSource extends PingSource {} InternalPingSource internalPingSource = new InternalPingSource(); MultiSleeper sleeper = new MultiSleeper(); ThreadPool() {} ThreadPool(int max) { this.max = max;} synchronized int maxSize() { return max; } synchronized int total() { return l(used)+l(free); } transient Set onCustomerMustWaitAlert; public ThreadPool onCustomerMustWaitAlert(Runnable r) { onCustomerMustWaitAlert = createOrAddToSyncLinkedHashSet(onCustomerMustWaitAlert, r); return this; } public ThreadPool removeCustomerMustWaitAlertListener(Runnable r) { main.remove(onCustomerMustWaitAlert, r); return this; } public void customerMustWaitAlert() { if (onCustomerMustWaitAlert != null) for (var listener : onCustomerMustWaitAlert) pcallF_typed(listener); } void fireCustomerMustWaitAlert() { vmBus_send("customerMustWaitAlert", this, currentThread()); customerMustWaitAlert(); } // DOESN'T WAIT. adds action to a thread's queue if nothing is // available immediately. PooledThread acquireThreadOrQueue(Runnable action) { if (action == null) return null; PooledThread t; synchronized(this) { if (_hasFreeAfterCreating()) { t = _firstFreeThread(); markUsed(t); } else t = _anyThread(); } t.addWork(action); // will move it from free to used return t; } // run in synchronized block boolean _hasFreeAfterCreating() { checkNotRetired(); if (nempty(free)) return true; if (total() < max) { PooledThread t = newThread(); all.add(t); free.add(t); return true; } return false; } // WAITS until thread is available PooledThread acquireThreadOrWait(Runnable action) { try { if (action == null) return null; PooledThread t; while (true) { synchronized(this) { if (_hasFreeAfterCreating()) { t = _firstFreeThread(); break; } else _waitWaitWait(); } } t.addWork(action); return t; } catch (Exception __e) { throw rethrow(__e); } } PooledThread _firstFreeThread() { return first(free); } PooledThread _anyThread() { return random(used); } class PooledThread extends Thread { PooledThread(String name) { super(name); } AppendableChain q; synchronized Runnable _grabWorkOrSleep() { try { Runnable r = first(q); if (r == null) { markFree(this); if (verbose) print("Thread sleeps"); synchronized(this) { wait(); } if (verbose) print("Thread woke up"); return null; } q = popFirst(q); return r; } catch (Exception __e) { throw rethrow(__e); } } public void run() { try { pingSource_tl().set(internalPingSource); while (!retired()) { ping(); Runnable r = _grabWorkOrSleep(); if (verbose) print(this + " work: " + r); if (r != null) try { if (verbose) print(this + " running: " + r); r.run(); pingSource_tl().set(internalPingSource); if (verbose) print(this + " done"); } catch (Throwable e) { pingSource_tl().set(internalPingSource); if (verbose) print(this + " error"); printStackTrace(e); } finally { pingSource_tl().set(internalPingSource); if (verbose) print("ThreadPool finally"); } } } catch (Exception __e) { throw rethrow(__e); } } synchronized boolean isEmpty() { return empty(q); } // append to q (do later) void addWork(Runnable r) { if (verbose) print("Added work to " + this + ": " + r); synchronized(this) { q = chainPlus(q, r); notifyAll(); } } } PooledThread newThread() { PooledThread t = new PooledThread("Thread Pool Inhabitant " + n2(total()+1)); t.start(); return t; } synchronized void markFree(PooledThread t) { used.remove(t); free.add(t); notifyAll(); } synchronized void markUsed(PooledThread t) { free.remove(t); used.add(t); } synchronized public String toString() { return retired() ? "Retired ThreadPool" : "ThreadPool " + roundBracket(commaCombine( n2(used) + " used out of " + n2(total()), max <= total() ? null : "could grow to " + n2(max))); } synchronized boolean retired() { return retired; } synchronized void retire() { if (verbose) print("ThreadPool Retiring"); retired = true; for (var thread : free) syncNotifyAll(thread); // wake it up so it exits } void checkNotRetired() { if (retired()) throw fail("retired"); } // We could do a soft-close here (stop the idle threads, let running threads finish, then end those too, stop accepting new orders) // or a hard close (interrupt all threads, stop accepting new orders) synchronized public void close() { try { retire(); } catch (Exception __e) { throw rethrow(__e); } } // run in synchronized block void _waitWaitWait() { try { do { fireCustomerMustWaitAlert(); wait(); checkNotRetired(); } while (empty(free)); } catch (Exception __e) { throw rethrow(__e); } } void dO(String text, Runnable r) { if (r == null) return; new PingSource(this, text).dO(r); } ISleeper_v2 sleeper() { return sleeper; } } abstract static class CompactAbstractMap implements Map { public int size() { return entrySet().size(); } public boolean isEmpty() { return size() == 0; } public boolean containsValue(Object value) { Iterator> i = entrySet().iterator(); if (value == null) { while (i.hasNext()) { Entry e = i.next(); if (e.getValue() == null) return true; } } else { while (i.hasNext()) { Entry e = i.next(); if (value.equals(e.getValue())) return true; } } return false; } public boolean containsKey(Object key) { Iterator> i = entrySet().iterator(); if (key == null) { while (i.hasNext()) { Entry e = i.next(); if (e.getKey() == null) return true; } } else { while (i.hasNext()) { Entry e = i.next(); if (key.equals(e.getKey())) return true; } } return false; } public V get(Object key) { Iterator> i = entrySet().iterator(); if (key == null) { while (i.hasNext()) { Entry e = i.next(); if (e.getKey() == null) return e.getValue(); } } else { while (i.hasNext()) { Entry e = i.next(); if (key.equals(e.getKey())) return e.getValue(); } } return null; } public V put(K key, V value) { throw new UnsupportedOperationException(); } public V remove(Object key) { Iterator> i = entrySet().iterator(); Entry correctEntry = null; if (key == null) { while (correctEntry == null && i.hasNext()) { Entry e = i.next(); if (e.getKey() == null) correctEntry = e; } } else { while (correctEntry == null && i.hasNext()) { Entry e = i.next(); if (key.equals(e.getKey())) correctEntry = e; } } V oldValue = null; if (correctEntry != null) { oldValue = correctEntry.getValue(); i.remove(); } return oldValue; } public void putAll(Map m) { for (Entry e : m.entrySet()) put(e.getKey(), e.getValue()); } public void clear() { entrySet().clear(); } public Set keySet() { return new AbstractSet() { public Iterator iterator() { return new Iterator() { private Iterator> i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public K next() { return i.next().getKey(); } public void remove() { i.remove(); } }; } public int size() { return CompactAbstractMap.this.size(); } public boolean isEmpty() { return CompactAbstractMap.this.isEmpty(); } public void clear() { CompactAbstractMap.this.clear(); } public boolean contains(Object k) { return CompactAbstractMap.this.containsKey(k); } }; } public Collection values() { return new AbstractCollection() { public Iterator iterator() { return new Iterator() { private Iterator> i = entrySet().iterator(); public boolean hasNext() { return i.hasNext(); } public V next() { return i.next().getValue(); } public void remove() { i.remove(); } }; } public int size() { return CompactAbstractMap.this.size(); } public boolean isEmpty() { return CompactAbstractMap.this.isEmpty(); } public void clear() { CompactAbstractMap.this.clear(); } public boolean contains(Object v) { return CompactAbstractMap.this.containsValue(v); } }; } public abstract Set> entrySet(); public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Map)) return false; Map m = (Map) o; if (m.size() != size()) return false; try { for (Entry e : entrySet()) { K key = e.getKey(); V value = e.getValue(); if (value == null) { if (!(m.get(key) == null && m.containsKey(key))) return false; } else { if (!value.equals(m.get(key))) return false; } } } catch (ClassCastException unused) { return false; } catch (NullPointerException unused) { return false; } return true; } public int hashCode() { int h = 0; for (Entry entry : entrySet()) h += entry.hashCode(); return h; } public String toString() { Iterator> i = entrySet().iterator(); if (!i.hasNext()) return "{}"; StringBuilder sb = new StringBuilder(); sb.append('{'); for (; ; ) { Entry e = i.next(); K key = e.getKey(); V value = e.getValue(); sb.append(key == this ? "(this Map)" : key); sb.append('='); sb.append(value == this ? "(this Map)" : value); if (!i.hasNext()) return sb.append('}').toString(); sb.append(',').append(' '); } } protected Object clone() throws CloneNotSupportedException { CompactAbstractMap result = (CompactAbstractMap) super.clone(); return result; } public static class SimpleEntry implements Entry, java.io.Serializable { @java.io.Serial private static final long serialVersionUID = -8499721149061103585L; @SuppressWarnings("serial") private final K key; @SuppressWarnings("serial") private V value; public SimpleEntry(K key, V value) { this.key = key; this.value = value; } public SimpleEntry(Entry entry) { this.key = entry.getKey(); this.value = entry.getValue(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Entry e = (Entry) o; return eq(key, e.getKey()) && eq(value, e.getValue()); } public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } public String toString() { return key + "=" + value; } } public static class SimpleImmutableEntry implements Entry, java.io.Serializable { @java.io.Serial private static final long serialVersionUID = 7138329143949025153L; @SuppressWarnings("serial") private final K key; @SuppressWarnings("serial") private final V value; public SimpleImmutableEntry(K key, V value) { this.key = key; this.value = value; } public SimpleImmutableEntry(Entry entry) { this.key = entry.getKey(); this.value = entry.getValue(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { throw new UnsupportedOperationException(); } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Entry e = (Entry) o; return eq(key, e.getKey()) && eq(value, e.getValue()); } public int hashCode() { return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); } public String toString() { return key + "=" + value; } } } interface IMultiMap { public Set keySet(); public Collection get(A a); public int size(); public int keyCount(); } /* * #! * Ontopia Engine * #- * Copyright (C) 2001 - 2013 The Ontopia Project * #- * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * !# */ // modified by Stefan Reich // Implements the Set interface more compactly than // java.util.HashSet by using a closed hashtable. // Note: equals is always called on the _stored_ object, not the one // passed as an argument to find(), contains() etc. // (In case you want to put special magic in your equals() function) static class UnsynchronizedCompactHashSet extends java.util.AbstractSet { protected final static int INITIAL_SIZE = 3; public final static double LOAD_FACTOR = 0.75; protected final static Object nullObject = new Object(); protected final static Object deletedObject = new Object(); protected int elements; protected int freecells; protected A[] objects; protected int modCount; UnsynchronizedCompactHashSet() { this(INITIAL_SIZE); } UnsynchronizedCompactHashSet(int size) { // NOTE: If array size is 0, we get a // "java.lang.ArithmeticException: / by zero" in add(Object). objects = (A[]) new Object[(size==0 ? 1 : size)]; elements = 0; freecells = objects.length; modCount = 0; } UnsynchronizedCompactHashSet(Collection c) { this(c.size()); addAll(c); } @Override public Iterator iterator() { return new CompactHashIterator(); } @Override public int size() { return elements; } @Override public boolean isEmpty() { return elements == 0; } @Override public boolean contains(Object o) { return find(o) != null; } A find(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } return objects[index]; } boolean removeIfSame(Object o) { A value = find(o); if (value == o) { remove(value); return true; } return false; } @Override public boolean add(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; int deletedix = -1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { // if there's a deleted object here we can put this object here, // provided it's not in here somewhere else already if (objects[index] == deletedObject) deletedix = index; index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } if (objects[index] == null) { // wasn't present already if (deletedix != -1) // reusing a deleted cell index = deletedix; else freecells--; modCount++; elements++; // here we face a problem regarding generics: // add(A o) is not possible because of the null Object. We cant do 'new A()' or '(A) new Object()' // so adding an empty object is a problem here // If (! o instanceof A) : This will cause a class cast exception // If (o instanceof A) : This will work fine objects[index] = (A) o; // do we need to rehash? if (1 - (freecells / (double) objects.length) > LOAD_FACTOR) rehash(); return true; } else // was there already return false; } @Override public boolean remove(Object o) { if (o == null) o = nullObject; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % objects.length; int offset = 1; // search for the object (continue while !null and !this object) while(objects[index] != null && !(objects[index].hashCode() == hash && objects[index].equals(o))) { index = ((index + offset) & 0x7FFFFFFF) % objects.length; offset = offset*2 + 1; if (offset == -1) offset = 2; } // we found the right position, now do the removal if (objects[index] != null) { // we found the object // same problem here as with add objects[index] = (A) deletedObject; modCount++; elements--; return true; } else // we did not find the object return false; } @Override public void clear() { elements = 0; for (int ix = 0; ix < objects.length; ix++) objects[ix] = null; freecells = objects.length; modCount++; } @Override public Object[] toArray() { Object[] result = new Object[elements]; Object[] objects = this.objects; int pos = 0; for (int i = 0; i < objects.length; i++) if (objects[i] != null && objects[i] != deletedObject) { if (objects[i] == nullObject) result[pos++] = null; else result[pos++] = objects[i]; } // unchecked because it should only contain A return result; } // not sure if this needs to have generics @Override public T[] toArray(T[] a) { int size = elements; if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); A[] objects = this.objects; int pos = 0; for (int i = 0; i < objects.length; i++) if (objects[i] != null && objects[i] != deletedObject) { if (objects[i] == nullObject) a[pos++] = null; else a[pos++] = (T) objects[i]; } return a; } protected void rehash() { int garbagecells = objects.length - (elements + freecells); if (garbagecells / (double) objects.length > 0.05) // rehash with same size rehash(objects.length); else // rehash with increased capacity rehash(objects.length*2 + 1); } protected void rehash(int newCapacity) { int oldCapacity = objects.length; @SuppressWarnings("unchecked") A[] newObjects = (A[]) new Object[newCapacity]; for (int ix = 0; ix < oldCapacity; ix++) { Object o = objects[ix]; if (o == null || o == deletedObject) continue; int hash = o.hashCode(); int index = (hash & 0x7FFFFFFF) % newCapacity; int offset = 1; // search for the object while(newObjects[index] != null) { // no need to test for duplicates index = ((index + offset) & 0x7FFFFFFF) % newCapacity; offset = offset*2 + 1; if (offset == -1) offset = 2; } newObjects[index] = (A) o; } objects = newObjects; freecells = objects.length - elements; } private class CompactHashIterator implements Iterator { private int index; private int lastReturned = -1; private int expectedModCount; @SuppressWarnings("empty-statement") public CompactHashIterator() { for (index = 0; index < objects.length && (objects[index] == null || objects[index] == deletedObject); index++) ; expectedModCount = modCount; } @Override public boolean hasNext() { return index < objects.length; } @SuppressWarnings("empty-statement") @Override public T next() { /*if (modCount != expectedModCount) throw new ConcurrentModificationException();*/ int length = objects.length; if (index >= length) { lastReturned = -2; throw new NoSuchElementException(); } lastReturned = index; for (index += 1; index < length && (objects[index] == null || objects[index] == deletedObject); index++) ; if (objects[lastReturned] == nullObject) return null; else return (T) objects[lastReturned]; } @Override public void remove() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (lastReturned == -1 || lastReturned == -2) throw new IllegalStateException(); // delete object if (objects[lastReturned] != null && objects[lastReturned] != deletedObject) { objects[lastReturned] = (A) deletedObject; elements--; modCount++; expectedModCount = modCount; // this is expected; we made the change } } } int capacity() { return objects.length; } // returns true if there was a shrink boolean shrinkToFactor(double factor) { if (factor > LOAD_FACTOR) throw fail("Shrink factor must be equal to or smaller than load factor: " + factor + " / " + LOAD_FACTOR); int newCapacity = max(INITIAL_SIZE, iround(size()/factor)); if (newCapacity >= capacity()) return false; rehash(newCapacity); return true; } } abstract static class Sleeping implements AutoCloseable , IFieldsToList{ Timestamp targetTime; Runnable action; Sleeping() {} Sleeping(Timestamp targetTime, Runnable action) { this.action = action; this.targetTime = targetTime;} public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + targetTime + ", " + action + ")"; }public Object[] _fieldsToList() { return new Object[] {targetTime, action}; } long remainingMS() { return targetTime.minus(tsNow()); } } static class MultiSleeper extends RestartableCountdown implements ISleeper_v2 { TreeMultiMap entries = new TreeMultiMap(); void check() { var time = nextWakeUpTime(); var action = firstValue(entries); setTargetTime(time == null ? 0 : time.sysTime(), new Runnable() { public void run() { try { List toCall; synchronized(MultiSleeper.this) { toCall = entries.get(time); entries.remove(time); } check(); pcallFAll(toCall); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "List toCall;\r\n synchronized(MultiSleeper.this) {\r\n toCa..."; }}); } synchronized void removeEntry(Timestamp targetTime, Runnable action) { entries.remove(targetTime, action); } // API synchronized Timestamp nextWakeUpTime() { return firstKey(entries); } public synchronized Sleeping doLater(Timestamp targetTime, Runnable r) { if (r == null || targetTime == null) return null; targetTime = max(targetTime, tsNow()); entries.put(targetTime, r); check(); return new Sleeping(targetTime, r) { public void close() { try { removeEntry(targetTime, r); } catch (Exception __e) { throw rethrow(__e); } } }; } } static class RestartableCountdown implements AutoCloseable { java.util.Timer timer; long targetTime; // in sys time long /*firings,*/ totalSleepTime; // stats synchronized void setTargetTime(long targetTime, Runnable action) { if (targetTime <= 0) stop(); else if (targetTime != this.targetTime) { start(targetTime-sysNow(), action); this.targetTime = targetTime; } } // stops the countdown and restarts it synchronized void start(long delayMS, Object action) { stop(); if (delayMS <= 0) { startThread(new Runnable() { public void run() { try { callF(action); } catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "callF(action);"; }}); } else { totalSleepTime += delayMS; timer = doLater_daemon(delayMS, action); targetTime = sysNow()+delayMS; } } void start(double delaySeconds, Object action) { start(toMS(delaySeconds), action); } synchronized void stop() { cancelTimer(timer); timer = null; targetTime = 0; } public void close() { stop(); } } static class TreeMultiMap extends MultiMap { TreeMultiMap() { super(true); } TreeMultiMap(MultiMap map) { this(); putAll(map); } } static boolean scaffoldingEnabled(Object o) { return metaGet(o, "scaffolding") != null; } static boolean classIsExportedTo(Class c, java.lang.Module destModule) { if (c == null || destModule == null) return false; java.lang.Module srcModule = c.getModule(); String packageName = c.getPackageName(); return srcModule.isExported(packageName, destModule); } static boolean isAbstract(Class c) { return (c.getModifiers() & Modifier.ABSTRACT) != 0; } static boolean isAbstract(Method m) { return (m.getModifiers() & Modifier.ABSTRACT) != 0; } static boolean reflection_isForbiddenMethod(Method m) { return m.getDeclaringClass() == Object.class && eqOneOf(m.getName(), "finalize", "clone", "registerNatives"); } static Set allInterfacesImplementedBy(Object o) { return allInterfacesImplementedBy(_getClass(o)); } static Set allInterfacesImplementedBy(Class c) { if (c == null) return null; HashSet set = new HashSet(); allInterfacesImplementedBy_find(c, set); return set; } static void allInterfacesImplementedBy_find(Class c, Set set) { if (c.isInterface() && !set.add(c)) return; do { for (Class intf : c.getInterfaces()) allInterfacesImplementedBy_find(intf, set); } while ((c = c.getSuperclass()) != null); } static Method findMethod(Object o, String method, Object... args) { return findMethod_cached(o, method, args); } static boolean findMethod_checkArgs(Method m, Object[] args, boolean debug) { Class[] types = m.getParameterTypes(); if (types.length != args.length) { if (debug) System.out.println("Bad parameter length: " + args.length + " vs " + types.length); return false; } for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) { if (debug) System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]); return false; } return true; } static Method findStaticMethod(Class c, String method, Object... args) { Class _c = c; while (c != null) { for (Method m : c.getDeclaredMethods()) { if (!m.getName().equals(method)) continue; if ((m.getModifiers() & Modifier.STATIC) == 0 || !findStaticMethod_checkArgs(m, args)) continue; return m; } c = c.getSuperclass(); } return null; } static boolean findStaticMethod_checkArgs(Method m, Object[] args) { Class[] types = m.getParameterTypes(); if (types.length != args.length) return false; for (int i = 0; i < types.length; i++) if (!(args[i] == null || isInstanceX(types[i], args[i]))) return false; return true; } static String unquote(String s) { if (s == null) return null; if (startsWith(s, '[')) { int i = 1; while (i < s.length() && s.charAt(i) == '=') ++i; if (i < s.length() && s.charAt(i) == '[') { String m = s.substring(1, i); if (s.endsWith("]" + m + "]")) return s.substring(i+1, s.length()-i-1); } } return unquoteSingleOrDoubleQuotes(s); } static List quoteAll(String[] l) { return quoteAll(asList(l)); } static List quoteAll(Collection l) { List x = new ArrayList(); for (String s : l) x.add(quote(s)); return x; } static boolean arraysEqual(Object[] a, Object[] b) { if (a.length != b.length) return false; for (int i = 0; i < a.length; i++) if (neq(a[i], b[i])) return false; return true; } static int area(Rect r) { return rectArea(r); } static double area(DoubleRect r) { return r == null ? 0 : r.w*r.h; } static int area(WidthAndHeight img) { return img == null ? 0 : img.getWidth()*img.getHeight(); } static int area(BufferedImage img) { return img == null ? 0 : img.getWidth()*img.getHeight(); } static Value value(A a) { return new Value(a); } 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 Set keySet(Map map) { return map == null ? new HashSet() : map.keySet(); } static Set keySet(Object map) { return keys((Map) map); } static Set keySet(MultiMap mm) { return mm.keySet(); } static boolean containsKey(Map map, A key) { return map != null && map.containsKey(key); } static Map.Entry simpleMapEntry(A key, B value) { return new Map.Entry() { public A getKey() { return key; } public B getValue() { return value; } public B setValue(B newValue) { throw unimplemented(); } }; } static double fracNonNeg(double d) { return frac_nonNeg(d); } static double twoPi() { return Math.PI*2; } static String plusPrefixUnlessMinus(String s) { return startsWith(s, "-") ? s : "+" + s; } static A getAndClear(IVar v) { A a = v.get(); v.set(null); return a; } 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 > 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> lists) { List l = new ArrayList(); if (lists != null) for (Iterable list : lists) addAll(l, list); return l; } static File loadLibrary(String snippetID) { return loadBinarySnippet(snippetID); } // TODO: use actualUserHome()? // (there was a problem with onLocallyInferiorJavaX() always triggering inside #1013896) static File pathToJavaxJar() { IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.pathToJavaXJar(); return pathToJavaxJar_noResourceLoader(); } static File pathToJavaxJar_noResourceLoader() { try { int x = latestInstalledJavaX(); File xfile = new File(userHome(), ".javax/x" + Math.max(x, 30) + ".jar"); if (!xfile.isFile()) { print("Saving " + f2s(xfile)); String url = x30JarServerURL(); byte[] data = loadBinaryPage(url); if (data.length < 1000000) throw fail("Could not load " + url); saveBinaryFile(xfile.getPath(), data); } return xfile; } catch (Exception __e) { throw rethrow(__e); } } static Method hashMap_findKey_method; static A hashMap_findKey(HashMap map, Object key) { try { if (hashMap_findKey_method == null) hashMap_findKey_method = findMethodNamed(HashMap.class, "getNode"); Map.Entry entry = (Map.Entry) hashMap_findKey_method.invoke(map, hashMap_internalHash(key), key); // java.util.Map.Entry entry = (java.util.Map.Entry) call(hash, 'getNode, hashMap_internalHash(key), wkey); return entry == null ? null : entry.getKey(); } catch (Exception __e) { throw rethrow(__e); } } static String find(String pattern, String text) { Matcher matcher = Pattern.compile(pattern).matcher(text); if (matcher.find()) return matcher.group(1); return null; } static A find(Collection c, Object... data) { for (A x : c) if (checkFields(x, data)) return x; return null; } static int modRange_incl(int i, int start, int end) { return start+mod(i-start, end-start+1); } static Pt onePathDirection(int index) { return onePathDirections()[index]; } static Pt onePathDirection(Pt p, int index) { return ptPlus(p, onePathDirections()[index]); } 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 String quoteBorderless(Object o) { if (o == null) return "null"; return quoteBorderless(str(o)); } static String quoteBorderless(String s) { if (s == null) return "null"; StringBuilder out = new StringBuilder((int) (l(s)*1.5)); quoteBorderless_impl(s, out); return out.toString(); } static void quoteBorderless_impl(String s, StringBuilder out) { int l = s.length(); for (int i = 0; i < l; i++) { char c = s.charAt(i); if (c == '\\' || c == '"') out.append('\\').append(c); else if (c == '\r') out.append("\\r"); else if (c == '\n') out.append("\\n"); else out.append(c); } } static Rect rectFromPointsOrNull(int x1, int y1, int x2, int y2) { return nullIfEmptyRect(pointsRect(x1, y1, x2, y2)); } static boolean stdEq2(Object a, Object b) { if (a == null) return b == null; if (b == null) return false; if (a.getClass() != b.getClass()) return false; for (String field : allFields(a)) if (neq(getOpt(a, field), getOpt(b, field))) return false; return true; } static int stdHash2(Object a) { if (a == null) return 0; return stdHash(a, toStringArray(allFields(a))); } static byte[] scaledGrayBytesFromBWIntegralImage(int w, BWIntegralImage img) { return scaledGrayBytesFromBWIntegralImage(img, w); } static byte[] scaledGrayBytesFromBWIntegralImage(BWIntegralImage img, int w) { return scaledGrayBytesFromBWIntegralImage(img, w, heightForWidth(img, w)); } static byte[] scaledGrayBytesFromBWIntegralImage(BWIntegralImage img, int w, int h) { int w1 = img.getWidth(), h1 = img.getHeight(); double stepX = doubleRatio(w1, w), stepY = doubleRatio(h1, h); byte[] pixels = new byte[w*h]; int i = 0; double srcY = 0; if (stepX == 1 && stepY == 1) { // No scaling for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int pixel = img.getPixelBrightness(x, y); pixels[i++] = clampToUByte(pixel); } } else { // With arbitrary scaling for (int y = 0; y < h; y++) { double srcX = 0, nextSrcY = srcY+stepY; for (int x = 0; x < w; x++) { double nextSrcX = srcX+stepX; int pixel = iround(img.getPixelAverage(srcX, srcY, nextSrcX, nextSrcY)); pixels[i++] = clampToUByte(pixel); srcX = nextSrcX; } srcY = nextSrcY; } } return pixels; } static byte[] scaledGrayBytesFromBWIntegralImage(int w, IBWIntegralImage img) { return scaledGrayBytesFromBWIntegralImage(img, w); } static byte[] scaledGrayBytesFromBWIntegralImage(IBWIntegralImage img, int w) { return scaledGrayBytesFromBWIntegralImage(img, w, heightForWidth(img, w)); } static byte[] scaledGrayBytesFromBWIntegralImage(IBWIntegralImage img, int w, int h) { int w1 = img.getWidth(), h1 = img.getHeight(); double stepX = doubleRatio(w1, w), stepY = doubleRatio(h1, h); byte[] pixels = new byte[w*h]; int i = 0; double srcY = 0; if (stepX == 1 && stepY == 1) { // No scaling for (int y = 0; y < h; y++) for (int x = 0; x < w; x++) { int pixel = img.getPixelBrightness(x, y); pixels[i++] = clampToUByte(pixel); } } else { // With arbitrary scaling for (int y = 0; y < h; y++) { double srcX = 0, nextSrcY = srcY+stepY; for (int x = 0; x < w; x++) { double nextSrcX = srcX+stepX; int pixel = iround(img.getPixelAverage(srcX, srcY, nextSrcX, nextSrcY)); pixels[i++] = clampToUByte(pixel); srcX = nextSrcX; } srcY = nextSrcY; } } return pixels; } static java.util.Timer doLater(long delay, final Object r) { ping(); final java.util.Timer timer = new java.util.Timer(); timer.schedule(timerTask(r, timer), delay); return vmBus_timerStarted(timer); } static java.util.Timer doLater(double delaySeconds, final Object r) { return doLater(toMS(delaySeconds), r); } static Timestamp tsNow() { return new Timestamp(); } static volatile int numberOfCores_value; static int numberOfCores() { if (numberOfCores_value == 0) numberOfCores_value = Runtime.getRuntime().availableProcessors(); return numberOfCores_value; } // runnable = Runnable or String (method name) static Thread newThread(Object runnable) { return new BetterThread(_topLevelErrorHandling(toRunnable(runnable))); } static Thread newThread(Object runnable, String name) { if (name == null) name = defaultThreadName(); return new BetterThread(_topLevelErrorHandling(toRunnable(runnable)), name); } static Thread newThread(String name, Object runnable) { return newThread(runnable, name); } static int random(int n) { return random(n, defaultRandomGenerator()); } static int random(int n, Random r) { return random(r, n); } static int random(Random r, int n) { return n <= 0 ? 0 : getRandomizer(r).nextInt(n); } static double random(double max) { return random()*max; } static double random() { return defaultRandomGenerator().nextInt(100001)/100000.0; } static double random(double min, double max) { return min+random()*(max-min); } // min <= value < max static int random(int min, int max) { return min+random(max-min); } static int random(int min, int max, Random r) { return random(r, min, max); } static int random(Random r, int min, int max) { return min+random(r, max-min); } static A random(List l) { return oneOf(l); } static A random(Collection c) { if (c instanceof List) return random((List) c); int i = random(l(c)); return collectionGet(c, i); } static int random(IntRange r) { return random(r.start, r.end); } static Pair random(Map map) { return entryToPair(random(entries(map))); } // Yes the nomenclature is a bit illogical static Chain chainPlus(Chain chain, A a) { return new Chain(a, chain); } static Chain chainPlus(Chain chain, A... l) { for (A a : unnullForIteration(l)) chain = chainPlus(chain, a); return chain; } static ReverseChain chainPlus(ReverseChain chain, A a) { return new ReverseChain(chain, a); } static ReverseChain chainPlus(ReverseChain chain, A... l) { for (A a : unnullForIteration(l)) chain = chainPlus(chain, a); return chain; } static AppendableChain chainPlus(AppendableChain chain, A a) { if (chain == null) return new AppendableChain(a); chain.add(a); return chain; } static AppendableChain chainPlus(AppendableChain chain, A... l) { for (A a : unnullForIteration(l)) chain = chainPlus(chain, a); return chain; } static void syncNotifyAll(Object o) { if (o != null) synchronized(o) { o.notifyAll(); } } static B firstValue(Map map) { return first(values(map)); } static B firstValue(MultiMap map) { return map == null ? null : first(firstValue(map.data)); } static A firstKey(Map map) { return first(keys(map)); } static A firstKey(IMultiMap map) { return map == null ? null : first(map.keySet()); } static Thread startThread(Object runnable) { return startThread(defaultThreadName(), runnable); } static Thread startThread(String name, Runnable runnable) { runnable = wrapAsActivity(runnable); return startThread(newThread(runnable, name)); } static Thread startThread(String name, Object runnable) { runnable = wrapAsActivity(runnable); return startThread(newThread(toRunnable(runnable), name)); } static Thread startThread(Thread t) { _registerThread(t); t.start(); return t; } static java.util.Timer doLater_daemon(long delay, final Object r) { final java.util.Timer timer = new java.util.Timer(true); timer.schedule(timerTask(r, timer), delay); return timer; } static java.util.Timer doLater_daemon(double delaySeconds, final Object r) { return doLater_daemon(toMS(delaySeconds), r); } static void cancelTimer(javax.swing.Timer timer) { if (timer != null) timer.stop(); } static void cancelTimer(java.util.Timer timer) { if (timer != null) timer.cancel(); } static void cancelTimer(Object o) { if (o instanceof java.util.Timer) cancelTimer((java.util.Timer) o); else if (o instanceof javax.swing.Timer) cancelTimer((javax.swing.Timer) o); else if (o instanceof AutoCloseable) { try { ((AutoCloseable) o).close(); } catch (Throwable __e) { printStackTrace(__e); }} } static Method findMethod_cached(Object o, String method, Object... args) { try { if (o == null) return null; if (o instanceof Class) { _MethodCache cache = callOpt_getCache((Class) o); List methods = cache.cache.get(method); if (methods != null) for (Method m : methods) if (isStaticMethod(m) && findMethod_checkArgs(m, args, false)) return m; return null; } else { _MethodCache cache = callOpt_getCache(o.getClass()); List methods = cache.cache.get(method); if (methods != null) for (Method m : methods) if (findMethod_checkArgs(m, args, false)) return m; return null; } } catch (Exception __e) { throw rethrow(__e); } } static String unquoteSingleOrDoubleQuotes(String s) { if (s == null) return null; if (s.length() > 1) { char c = s.charAt(0); if (c == '\"' || c == '\'') { int l = endsWith(s, c) ? s.length()-1 : s.length(); StringBuilder sb = new StringBuilder(l-1); for (int i = 1; i < l; i++) { char ch = s.charAt(i); if (ch == '\\') { char nextChar = (i == l - 1) ? '\\' : s.charAt(i + 1); // Octal escape? if (nextChar >= '0' && nextChar <= '7') { String code = "" + nextChar; i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; if ((i < l - 1) && s.charAt(i + 1) >= '0' && s.charAt(i + 1) <= '7') { code += s.charAt(i + 1); i++; } } sb.append((char) Integer.parseInt(code, 8)); continue; } switch (nextChar) { case '\"': ch = '\"'; break; case '\\': ch = '\\'; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case '\'': ch = '\''; break; // Hex Unicode: u???? case 'u': if (i >= l - 5) { ch = 'u'; break; } int code = Integer.parseInt( "" + s.charAt(i + 2) + s.charAt(i + 3) + s.charAt(i + 4) + s.charAt(i + 5), 16); sb.append(Character.toChars(code)); i += 5; continue; default: ch = nextChar; // added by Stefan } i++; } sb.append(ch); } return sb.toString(); } } return s; // not quoted - return original } static double frac_nonNeg(double d) { return mod(d, 1); } static int lKeys(MultiMap mm) { return mm == null ? 0 : mm.keysSize(); } static File loadBinarySnippet(String snippetID) { IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.loadLibrary(snippetID); return loadBinarySnippet_noResourceLoader(snippetID); } static File loadBinarySnippet_noResourceLoader(String snippetID) { try { long id = parseSnippetID(snippetID); if (isImageServerSnippet(id)) return loadImageAsFile(snippetID); File f = DiskSnippetCache_getLibrary(id); if (fileSize(f) == 0) f = loadDataSnippetToFile_noResourceLoader(snippetID); return f; } catch (Exception __e) { throw rethrow(__e); } } static int latestInstalledJavaX() { File[] files = new File(userHome(), ".javax").listFiles(); int v = 0; if (files != null) for (File f : files) { Matcher m = regexpMatcher("x(\\d\\d\\d?)\\.jar", f.getName()); if (m.matches()) v = Math.max(v, Integer.parseInt(m.group(1))); } return v; } static String x30JarServerURL() { return "http://botcompany.de:8081/x30.jar"; } static ThreadLocal>> loadBinaryPage_responseHeaders = new ThreadLocal(); static ThreadLocal> loadBinaryPage_extraHeaders = new ThreadLocal(); static byte[] loadBinaryPage(String url) { try { print("Loading " + url); return loadBinaryPage(loadPage_openConnection(new URL(url))); } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage(URLConnection con) { try { Map extraHeaders = getAndClearThreadLocal(loadBinaryPage_extraHeaders); setHeaders(con); for (String key : keys(extraHeaders)) con.setRequestProperty(key, extraHeaders.get(key)); return loadBinaryPage_noHeaders(con); } catch (Exception __e) { throw rethrow(__e); } } static byte[] loadBinaryPage_noHeaders(URLConnection con) { try { ByteArrayOutputStream buf = new ByteArrayOutputStream(); InputStream inputStream = con.getInputStream(); loadBinaryPage_responseHeaders.set(con.getHeaderFields()); long len = 0; try { len = con.getContentLength/*Long*/(); } catch (Throwable e) { printStackTrace(e); } int n = 0; while (true) { int ch = inputStream.read(); if (ch < 0) break; buf.write(ch); if (++n % 100000 == 0) println(" " + n + (len != 0 ? "/" + len : "") + " bytes loaded."); } inputStream.close(); return buf.toByteArray(); } catch (Exception __e) { throw rethrow(__e); } } /** writes safely (to temp file, then rename) */ public static byte[] saveBinaryFile(String fileName, byte[] contents) { try { File file = new File(fileName); File parentFile = file.getParentFile(); if (parentFile != null) parentFile.mkdirs(); String tempFileName = fileName + "_temp"; FileOutputStream fileOutputStream = newFileOutputStream(tempFileName); fileOutputStream.write(contents); fileOutputStream.close(); if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (!new File(tempFileName).renameTo(file)) throw new IOException("Can't rename " + tempFileName + " to " + fileName); vmBus_send("wroteFile", file); return contents; } catch (Exception __e) { throw rethrow(__e); } } static byte[] saveBinaryFile(File fileName, byte[] contents) { return saveBinaryFile(fileName.getPath(), contents); } // This is a bit rough... finds static and non-static methods. static Method findMethodNamed(Object obj, String method) { if (obj == null) return null; if (obj instanceof Class) return findMethodNamed((Class) obj, method); return findMethodNamed(obj.getClass(), method); } static Method findMethodNamed(Class c, String method) { while (c != null) { for (Method m : c.getDeclaredMethods()) if (m.getName().equals(method)) { makeAccessible(m); return m; } c = c.getSuperclass(); } return null; } static int hashMap_internalHash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } static boolean checkFields(Object x, Object... data) { for (int i = 0; i < l(data); i += 2) if (neq(getOpt(x, (String) data[i]), data[i+1])) return false; return true; } // better modulo that gives positive numbers always static int mod(int n, int m) { return (n % m + m) % m; } static long mod(long n, long m) { return (n % m + m) % m; } static BigInteger mod(BigInteger n, int m) { return n.mod(bigint(m)); } static double mod(double n, double m) { return (n % m + m) % m; } static Pt ptPlus(Pt a, Pt b) { return addPts(a, b); } static String hfulltag(String tag) { return hfulltag(tag, ""); } static String hfulltag(String tag, Object contents, Object... params) { return hopeningTag(tag, params) + str(contents) + ""; } static String tag(String tag) { return htag(tag); } static String tag(String tag, Object contents, Object... params) { return htag(tag, str(contents), params); } static String tag(String tag, StringBuilder contents, Object... params) { return htag(tag, contents, params); } static String tag(String tag, StringBuffer contents, Object... params) { return htag(tag, contents, params); } static Rect nullIfEmptyRect(Rect r) { return rectEmpty(r) ? null : r; } static Map> allFields_cache = weakHashMap(); static Set allFields(Object o) { if (o == null) return emptySet(); Class _c = _getClass(o); Set fields = allFields_cache.get(_c); if (fields == null) allFields_cache.put(_c, fields = asTreeSet(keys(getOpt_getFieldMap(o)))); return fields; } static int stdHash(Object a, String... fields) { if (a == null) return 0; int hash = getClassName(a).hashCode(); for (String field : fields) hash = boostHashCombine(hash, hashCode(getOpt(a, field))); return hash; } static TimerTask timerTask(final Object r, final java.util.Timer timer) { return new TimerTask() { public void run() { if (!licensed()) timer.cancel(); else pcallF(r); } }; } static A vmBus_timerStarted(A timer) { vmBus_send("timerStarted", timer, costCenter()); return timer; } static Runnable _topLevelErrorHandling(Runnable r) { if (r == null) return null; // maybe we don't want this anymore. just dm_current_generic() Object info = _threadInfo(); Object mod = dm_current_generic(); Runnable r2 = r; if (info != null || mod == null) r2 = new Runnable() { public void run() { try { AutoCloseable __1 = (AutoCloseable) (rcall("enter", mod)); try { _threadInheritInfo(info); r.run(); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "temp (AutoCloseable) rcall enter(mod);\r\n _threadInheritInfo(info);\r\n ..."; }}; r2 = rPcall(r2); return r2; } static String defaultThreadName_name; static String defaultThreadName() { if (defaultThreadName_name == null) defaultThreadName_name = "A thread by " + programID(); return defaultThreadName_name; } static Random defaultRandomGenerator() { { Random r = customRandomizerForThisThread(); if (r != null) return r; } return ThreadLocalRandom.current(); } static Random getRandomizer(Random r) { return r != null ? r : defaultRandomGenerator(); } static A oneOf(List l) { if (empty(l)) return null; int n = l.size(); return n == 1 ? first(l) : l.get(defaultRandomizer().nextInt(n)); } static char oneOf(String s) { return empty(s) ? '?' : s.charAt(random(l(s))); } static A oneOf(A... l) { return oneOf(asList(l)); } static A collectionGet(Collection c, int idx) { if (c == null || idx < 0 || idx >= l(c)) return null; if (c instanceof List) return listGet((List) c, idx); Iterator it = c.iterator(); for (int i = 0; i < idx; i++) if (it.hasNext()) it.next(); else return null; return it.hasNext() ? it.next() : null; } static Pair entryToPair(Map.Entry e) { return mapEntryToPair(e); } static Set> entries(Map map) { return _entrySet(map); } static Runnable wrapAsActivity(Object r) { if (r == null) return null; Runnable r2 = toRunnable(r); Object mod = dm_current_generic(); if (mod == null) return r2; return new Runnable() { public void run() { try { AutoCloseable c = (AutoCloseable) (rcall("enter", mod)); AutoCloseable __1 = c; try { r2.run(); } finally { _close(__1); }} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "AutoCloseable c = (AutoCloseable) (rcall enter(mod));\r\n temp c;\r\n r2.r..."; }}; } static Map _registerThread_threads; static Object _onRegisterThread; // voidfunc(Thread) static Thread _registerThread(Thread t) { if (_registerThread_threads == null) _registerThread_threads = newWeakHashMap(); _registerThread_threads.put(t, true); vm_generalWeakSubMap("thread2mc").put(t, weakRef(mc())); callF(_onRegisterThread, t); return t; } static void _registerThread() { _registerThread(Thread.currentThread()); } static File loadImageAsFile(String snippetIDOrURL) { try { if (isURL(snippetIDOrURL)) throw fail("not implemented"); if (!isSnippetID(snippetIDOrURL)) throw fail("Not a URL or snippet ID: " + snippetIDOrURL); String snippetID = "" + parseSnippetID(snippetIDOrURL); File file = imageSnippetCacheFile(snippetID); if (fileSize(file) > 0) return file; String imageURL = snippetImageURL_noHttps(snippetID); System.err.println("Loading image: " + imageURL); byte[] data = loadBinaryPage(imageURL); saveBinaryFile(file, data); return file; } catch (Exception __e) { throw rethrow(__e); } } // If you change this, also change DiskSnippetCache_fileToLibID static File DiskSnippetCache_file(long snippetID) { return new File(getGlobalCache(), "data_" + snippetID + ".jar"); } // Data files are immutable, use centralized cache public static File DiskSnippetCache_getLibrary(long snippetID) throws IOException { File file = DiskSnippetCache_file(snippetID); return file.exists() ? file : null; } public static File DiskSnippetCache_getLibrary(String snippetID) { try { return DiskSnippetCache_getLibrary(psI(snippetID)); } catch (Exception __e) { throw rethrow(__e); } } public static void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException { saveBinaryFile(DiskSnippetCache_file(snippetID), data); } static byte[] loadDataSnippetImpl(String snippetID) throws IOException { byte[] data; try { URL url = new URL(dataSnippetLink(snippetID)); print("Loading library: " + hideCredentials(url)); try { data = loadBinaryPage(url.openConnection()); } catch (RuntimeException e) { data = null; } if (data == null || data.length == 0) { url = new URL(tb_mainServer() + "/blobs/" + parseSnippetID(snippetID)); print("Loading library: " + hideCredentials(url)); data = loadBinaryPage(url.openConnection()); } print("Bytes loaded: " + data.length); } catch (FileNotFoundException e) { throw new IOException("Binary snippet #" + snippetID + " not found or not public"); } return data; } static long fileSize(String path) { return getFileSize(path); } static long fileSize(File f) { return getFileSize(f); } static File loadDataSnippetToFile(String snippetID) { try { IResourceLoader rl = vm_getResourceLoader(); if (rl != null) return rl.loadLibrary(snippetID); return loadDataSnippetToFile_noResourceLoader(snippetID); } catch (Exception __e) { throw rethrow(__e); } } static File loadDataSnippetToFile_noResourceLoader(String snippetID) { try { snippetID = fsI(snippetID); File f = DiskSnippetCache_file(parseSnippetID(snippetID)); List urlsTried = new ArrayList(); List errors = new ArrayList(); try { URL url = addAndReturn(urlsTried, new URL(dataSnippetLink(snippetID))); print("Loading library: " + hideCredentials(url)); try { loadBinaryPageToFile(openConnection(url), f); if (fileSize(f) == 0) throw fail(); } catch (Throwable e) { errors.add(e); url = addAndReturn(urlsTried, new URL(tb_mainServer() + "/blobs/" + psI(snippetID))); print(e); print("Trying other server: " + hideCredentials(url)); loadBinaryPageToFile(openConnection(url), f); print("Got bytes: " + fileSize(f)); } // TODO: check if we hit the "LOADING" message if (fileSize(f) == 0) throw fail(); System.err.println("Bytes loaded: " + fileSize(f)); } catch (Throwable e) { //printStackTrace(e); errors.add(e); throw fail("Binary snippet " + snippetID + " not found or not public. URLs tried: " + allToString(urlsTried) + ", errors: " + allToString(errors)); } return f; } catch (Exception __e) { throw rethrow(__e); } } static Matcher regexpMatcher(String pat, String s) { return compileRegexp(pat).matcher(unnull(s)); } static Matcher regexpMatcher(java.util.regex.Pattern pat, String s) { return pat.matcher(unnull(s)); } static int loadPage_defaultTimeout = 60000; static ThreadLocal loadPage_charset = new ThreadLocal(); static boolean loadPage_allowGzip = true, loadPage_debug; static boolean loadPage_anonymous = false; // don't send computer ID static int loadPage_verboseness = 100000; static int loadPage_retries = 1; //60; // seconds static ThreadLocal loadPage_silent = new ThreadLocal(); static volatile int loadPage_forcedTimeout; // ms static ThreadLocal loadPage_forcedTimeout_byThread = new ThreadLocal(); // ms static ThreadLocal>> loadPage_responseHeaders = new ThreadLocal(); static ThreadLocal> loadPage_extraHeaders = new ThreadLocal(); static ThreadLocal loadPage_sizeLimit = new ThreadLocal(); public static String loadPageSilently(String url) { try { return loadPageSilently(new URL(loadPage_preprocess(url))); } catch (Exception __e) { throw rethrow(__e); } } public static String loadPageSilently(URL url) { try { if (!networkAllowanceTest(str(url))) throw fail("Not allowed: " + url); IOException e = null; for (int tries = 0; tries < loadPage_retries; tries++) try { URLConnection con = loadPage_openConnection(url); return loadPage(con, url); } catch (IOException _e) { e = _e; if (loadPage_debug) print(exceptionToStringShort(e)); if (tries < loadPage_retries-1) sleepSeconds(1); } throw e; } catch (Exception __e) { throw rethrow(__e); } } static String loadPage_preprocess(String url) { if (url.startsWith("tb/")) // don't think we use this anymore url = tb_mainServer() + "/" + url; if (url.indexOf("://") < 0) url = "http://" + url; return url; } static String loadPage(String url) { try { url = loadPage_preprocess(url); if (!isTrue(loadPage_silent.get())) printWithTime("Loading: " + hideCredentials(url)); return loadPageSilently(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } static String loadPage(URL url) { return loadPage(url.toExternalForm()); } static String loadPage(URLConnection con, URL url) throws IOException { return loadPage(con, url, true); } static String loadPage(URLConnection con, URL url, boolean addHeaders) { try { Map extraHeaders = getAndClearThreadLocal(loadPage_extraHeaders); if (addHeaders) try { if (!loadPage_anonymous) setHeaders(con); if (loadPage_allowGzip) con.setRequestProperty("Accept-Encoding", "gzip"); con.setRequestProperty("X-No-Cookies", "1"); for (String key : keys(extraHeaders)) con.setRequestProperty(key, extraHeaders.get(key)); } catch (Throwable e) {} // fails if within doPost return loadPage(con); } catch (Exception __e) { throw rethrow(__e); } } // just download as string, no shenanigans or extra headers or ANYTHING static String loadPage(URLConnection con) { try { Long limit = optPar(loadPage_sizeLimit); URL url = con.getURL(); vm_generalSubMap("URLConnection per thread").put(currentThread(), con); loadPage_responseHeaders.set(con.getHeaderFields()); InputStream in = null; try { in = urlConnection_getInputStream(con); //vm_generalSubMap("InputStream per thread").put(currentThread(), in); if (loadPage_debug) print("Put stream in map: " + currentThread()); String contentType = con.getContentType(); if (contentType == null) { //printStruct("Headers: ", con.getHeaderFields()); throw new IOException("Page could not be read: " + hideCredentials(url)); } //print("Content-Type: " + contentType); String charset = loadPage_charset == null ? null : loadPage_charset.get(); if (charset == null) charset = loadPage_guessCharset(contentType); if ("gzip".equals(con.getContentEncoding())) { if (loadPage_debug) print("loadPage: Using gzip."); in = newGZIPInputStream(in); } Reader r; try { r = new InputStreamReader(in, unquote(charset)); } catch (UnsupportedEncodingException e) { print(toHex(utf8(charset))); throw e; } boolean silent = isTrue(loadPage_silent.get()); StringBuilder buf = new StringBuilder(); int n = 0; while (limit == null || n < limit) { ping(); int ch = r.read(); if (ch < 0) break; buf.append((char) ch); ++n; if (!silent && (n % loadPage_verboseness) == 0) print(" " + n + " chars read"); } return buf.toString(); } finally { if (loadPage_debug) print("loadPage done"); //vm_generalSubMap("InputStream per thread").remove(currentThread()); vm_generalSubMap("URLConnection per thread").remove(currentThread()); if (in != null) in.close(); } } catch (Exception __e) { throw rethrow(__e); } } static String loadPage_guessCharset(String contentType) { Matcher m = regexpMatcher("text/[a-z]+;\\s*charset=([^\\s]+)\\s*", contentType); String match = m.matches() ? m.group(1) : null; if (loadPage_debug) print("loadPage: contentType=" + contentType + ", match: " + match); /* If Content-Type doesn't match this pre-conception, choose default and hope for the best. */ //return or(match, "ISO-8859-1"); return or(match, "UTF-8"); } static URLConnection loadPage_openConnection(URL url) { URLConnection con = openConnection(url); int timeout = toInt(loadPage_forcedTimeout_byThread.get()); if (timeout == 0) timeout = loadPage_forcedTimeout; if (timeout != 0) setURLConnectionTimeouts(con, loadPage_forcedTimeout); else setURLConnectionDefaultTimeouts(con, loadPage_defaultTimeout); return con; } static A getAndClearThreadLocal(ThreadLocal tl) { A a = tl.get(); tl.set(null); return a; } static void setHeaders(URLConnection con) throws IOException { String computerID = getComputerID_quick(); if (computerID != null) try { con.setRequestProperty("X-ComputerID", computerID); con.setRequestProperty("X-OS", System.getProperty("os.name") + " " + System.getProperty("os.version")); } catch (Throwable e) { //printShortException(e); } } static A println(A a) { return print(a); } static FileOutputStream newFileOutputStream(File path) throws IOException { return newFileOutputStream(path.getPath()); } static FileOutputStream newFileOutputStream(String path) throws IOException { return newFileOutputStream(path, false); } static FileOutputStream newFileOutputStream(File path, boolean append) throws IOException { return newFileOutputStream(path.getPath(), append); } static FileOutputStream newFileOutputStream(String path, boolean append) throws IOException { mkdirsForFile(path); FileOutputStream f = new FileOutputStream(path, append); _registerIO(f, path, true); return f; } static String hopeningTag(String tag, Map params) { return hopeningTag(tag, mapToParams(params)); } static String hopeningTag(String tag, Object... params) { StringBuilder buf = new StringBuilder(); buf.append("<" + tag); params = unrollParams(params); for (int i = 0; i < l(params); i += 2) { String name = (String) get(params, i); Object val = get(params, i+1); if (nempty(name) && val != null) { if (eqOneOf(val, html_valueLessParam(), true)) buf.append(" " + name); else { String s = str(val); if (!empty(s)) buf.append(" " + name + "=" + htmlQuote(s)); } } } buf.append(">"); return str(buf); } static String htag(String tag) { return htag(tag, ""); } static String htag(String tag, Object contents, Object... params) { String openingTag = hopeningTag(tag, params); String s = str(contents); if (empty(s) && neqic(tag, "script")) return dropLast(openingTag) + "/>"; return openingTag + s + ""; } static Map weakHashMap() { return newWeakHashMap(); } static Set emptySet() { return new HashSet(); } static TreeSet asTreeSet(Collection set) { return set == null ? null : set instanceof TreeSet ? (TreeSet) set : new TreeSet(set); } static int hashCode(Object a) { return a == null ? 0 : a.hashCode(); } static int hashCode(long l) { return Long.hashCode(l); } static int hashCode(double d) { return Double.hashCode(d); } static Object costCenter() { return mc(); } static Object dm_current_generic() { return getWeakRef(dm_current_generic_tl().get()); } static Object rcall(String method, Object o, Object... args) { return call_withVarargs(o, method, args); } static Runnable rPcall(Runnable r) { return r == null ? null : () -> { try { r.run(); } catch (Throwable __e) { printStackTrace(__e); } }; } static String programID() { return getProgramID(); } static String programID(Object o) { return getProgramID(o); } static Random customRandomizerForThisThread() { return customRandomizerForThisThread_tl().get(); } static Random defaultRandomizer() { return defaultRandomGenerator(); } static A listGet(List l, int idx) { return l != null && idx >= 0 && idx < l.size() ? l.get(idx) : null; } static Map vm_generalWeakSubMap(Object name) { synchronized(vm_generalMap()) { Map map = (Map) (vm_generalMap_get(name)); if (map == null) vm_generalMap_put(name, map = newWeakMap()); return map; } } static WeakReference weakRef(A a) { return newWeakReference(a); } static File imageSnippetCacheFile(String snippetID) { File dir = imageSnippetsCacheDir(); if (!loadBufferedImage_useImageCache) return null; return new File(dir, parseSnippetID(snippetID) + ".png"); } static String snippetImageURL_noHttps(String snippetID) { return snippetImageURL_noHttps(snippetID, "png"); } static String snippetImageURL_noHttps(String snippetID, String contentType) { return snippetImageURL(snippetID, contentType) .replace("https://www.botcompany.de:8443/", "http://www.botcompany.de:8080/") .replace("https://botcompany.de/", "http://botcompany.de/"); } static File getGlobalCache() { File file = new File(javaxCachesDir(), "Binary Snippets"); file.mkdirs(); return file; } static long psI(String snippetID) { return parseSnippetID(snippetID); } static String dataSnippetLink(String snippetID) { long id = parseSnippetID(snippetID); if (id >= 1100000 && id < 1200000) return imageServerURL() + id; if (id >= 1200000 && id < 1300000) { // Woody files, actually String pw = muricaPassword(); if (empty(pw)) throw fail("Please set 'murica password by running #1008829"); return "https://botcompany.de/files/" + id + "?_pass=" + pw; // XXX, although it typically gets hidden when printing } return fileServerURL() + "/" + id /*+ "?_pass=" + muricaPassword()*/; } static String tb_mainServer_default = "https://code.botcompany.de:9898"; static Object tb_mainServer_override; // func -> S static String tb_mainServer() { if (tb_mainServer_override != null) return (String) callF(tb_mainServer_override); return trim(loadTextFile(tb_mainServer_file(), tb_mainServer_default)); } static File tb_mainServer_file() { return getProgramFile("#1001638", "mainserver.txt"); } static boolean tb_mainServer_isDefault() { return eq(tb_mainServer(), tb_mainServer_default); } static A addAndReturn(Collection c, A a) { if (c != null) c.add(a); return a; } static A addAndReturn(List c, int idx, A a) { if (c != null) c.add(idx, a); return a; } static void loadBinaryPageToFile(String url, File file) { try { print("Loading " + url); loadBinaryPageToFile(openConnection(new URL(url)), file); } catch (Exception __e) { throw rethrow(__e); } } static void loadBinaryPageToFile(URLConnection con, File file) { try { setHeaders(con); loadBinaryPageToFile_noHeaders(con, file); } catch (Exception __e) { throw rethrow(__e); } } static void loadBinaryPageToFile_noHeaders(URLConnection con, File file) { try { File ftemp = new File(f2s(file) + "_temp"); FileOutputStream buf = newFileOutputStream(mkdirsFor(ftemp)); try { InputStream inputStream = con.getInputStream(); long len = 0; try { len = con.getContentLength/*Long*/(); } catch (Throwable e) { printStackTrace(e); } String pat = " {*}" + (len != 0 ? "/" + len : "") + " bytes loaded."; copyStreamWithPrints(inputStream, buf, pat); inputStream.close(); buf.close(); file.delete(); renameFile_assertTrue(ftemp, file); } finally { if (buf != null) buf.close(); } } catch (Exception __e) { throw rethrow(__e); } } static URLConnection openConnection(String url) { try { return openConnection(new URL(url)); } catch (Exception __e) { throw rethrow(__e); } } static URLConnection openConnection(URL url) { try { ping(); callOpt(javax(), "recordOpenURLConnection", str(url)); return url.openConnection(); } catch (Exception __e) { throw rethrow(__e); } } static Map compileRegexp_cache = syncMRUCache(10); static java.util.regex.Pattern compileRegexp(String pat) { java.util.regex.Pattern p = compileRegexp_cache.get(pat); if (p == null) { compileRegexp_cache.put(pat, p = java.util.regex.Pattern.compile(pat)); } return p; } static boolean networkAllowanceTest(String url) { return isAllowed("networkAllowanceTest", url); } static String exceptionToStringShort(Throwable e) { try { lastException(e); e = getInnerException(e); String msg = hideCredentials(unnull(e.getMessage())); if (msg.indexOf("Error") < 0 && msg.indexOf("Exception") < 0) return baseClassName(e) + prependIfNempty(": ", msg); else return msg; } catch (Throwable _e) { printStackTrace(_e); return "Error in exceptionToStringShort"; } } static void sleepSeconds(double s) { if (s > 0) sleep(round(s*1000)); } static A printWithTime(A a) { return printWithTime("", a); } static A printWithTime(String s, A a) { print(hmsWithColons() + ": " + s, a); return a; } static A optPar(ThreadLocal tl, A defaultValue) { A a = tl.get(); if (a != null) { tl.set(null); return a; } return defaultValue; } static A optPar(ThreadLocal tl) { return optPar(tl, null); } static Object optPar(Object[] params, String name) { return optParam(params, name); } static Object optPar(String name, Object[] params) { return optParam(params, name); } static Object optPar(String name, Map params) { return optParam(name, params); } static A optPar(Object[] params, String name, A defaultValue) { return optParam(params, name, defaultValue); } static A optPar(String name, Object[] params, A defaultValue) { return optParam(params, name, defaultValue); } static Map vm_generalSubMap(Object name) { synchronized(vm_generalMap()) { Map map = (Map) (vm_generalMap_get(name)); if (map == null) vm_generalMap_put(name, map = synchroMap()); return map; } } static InputStream urlConnection_getInputStream(URLConnection con) throws IOException { return con.getInputStream(); } static String toHex(byte[] bytes) { return bytesToHex(bytes); } static String toHex(byte[] bytes, int ofs, int len) { return bytesToHex(bytes, ofs, len); } static byte[] utf8(String s) { return toUtf8(s); } static URLConnection setURLConnectionTimeouts(URLConnection con, long timeout) { con.setConnectTimeout(toInt(timeout)); con.setReadTimeout(toInt(timeout)); if (con.getConnectTimeout() != timeout || con.getReadTimeout() != timeout) print("Warning: Timeouts not set by JDK."); return con; } static URLConnection setURLConnectionDefaultTimeouts(URLConnection con, long timeout) { if (con.getConnectTimeout() == 0) { con.setConnectTimeout(toInt(timeout)); if (con.getConnectTimeout() != timeout) print("Warning: URL connect timeout not set by JDK."); } if (con.getReadTimeout() == 0) { con.setReadTimeout(toInt(timeout)); if (con.getReadTimeout() != timeout) print("Warning: URL read timeout not set by JDK."); } return con; } static String getComputerID_quick() { return computerID(); } static Object[] mapToParams(Map map) { return mapToObjectArray(map); } static Object[] unrollParams(Object[] params) { if (l(params) == 1 && params[0] instanceof Map) return mapToParams((Map) params[0]); return params; } static Object html_valueLessParam_cache; static Object html_valueLessParam() { if (html_valueLessParam_cache == null) html_valueLessParam_cache = html_valueLessParam_load(); return html_valueLessParam_cache;} static Object html_valueLessParam_load() { return new Object(); } static String htmlQuote(String s) { return "\"" + htmlencode_forParams(s) + "\""; } static boolean neqic(String a, String b) { return !eqic(a, b); } static boolean neqic(char a, char b) { return !eqic(a, b); } static A getWeakRef(Reference ref) { return ref == null ? null : ref.get(); } static x30_pkg.x30_util.BetterThreadLocal dm_current_generic_tl; static x30_pkg.x30_util.BetterThreadLocal dm_current_generic_tl() { if (dm_current_generic_tl == null) dm_current_generic_tl = vm_generalMap_getOrCreate("currentModule", () -> new x30_pkg.x30_util.BetterThreadLocal()); return dm_current_generic_tl; } static String programID; static String getProgramID() { return nempty(programID) ? formatSnippetIDOpt(programID) : "?"; } // TODO: ask JavaX instead static String getProgramID(Class c) { String id = (String) getOpt(c, "programID"); if (nempty(id)) return formatSnippetID(id); return "?"; } static String getProgramID(Object o) { return getProgramID(getMainClass(o)); } static ThreadLocal customRandomizerForThisThread_tl = new ThreadLocal(); static ThreadLocal customRandomizerForThisThread_tl() { return customRandomizerForThisThread_tl; } static Map newWeakMap() { return newWeakHashMap(); } static WeakReference newWeakReference(A a) { return a == null ? null : new WeakReference(a); } static String imageServerURL() { return or2(trim(loadTextFile(javaxDataDir("image-server-url.txt"))), "http://botcompany.de/images/raw/"); } static volatile boolean muricaPassword_pretendNotAuthed = false; static String muricaPassword() { if (muricaPassword_pretendNotAuthed) return null; return trim(loadTextFile(muricaPasswordFile())); } static String fileServerURL() { return "https://botcompany.de/files"; } static String loadTextFile(String fileName) { return loadTextFile(fileName, null); } static String loadTextFile(File f, String defaultContents) { return loadTextFile(f, defaultContents, "UTF-8"); } static String loadTextFile(File f, String defaultContents, String encoding) { try { checkFileNotTooBigToRead(f); if (f == null || !f.exists()) return defaultContents; FileInputStream fileInputStream = new FileInputStream(f); InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, encoding); return loadTextFile(inputStreamReader); } catch (Exception __e) { throw rethrow(__e); } } public static String loadTextFile(File fileName) { return loadTextFile(fileName, null); } static String loadTextFile(String fileName, String defaultContents) { return fileName == null ? defaultContents : loadTextFile(newFile(fileName), defaultContents); } static String loadTextFile(Reader reader) throws IOException { StringBuilder builder = new StringBuilder(); try { char[] buffer = new char[1024]; int n; while (-1 != (n = reader.read(buffer))) builder.append(buffer, 0, n); } finally { reader.close(); } return str(builder); } static File getProgramFile(String progID, String fileName) { if (new File(fileName).isAbsolute()) return new File(fileName); return new File(getProgramDir(progID), fileName); } static File getProgramFile(String fileName) { return getProgramFile(getProgramID(), fileName); } static void copyStreamWithPrints(InputStream in, OutputStream out, String pat) { try { byte[] buf = new byte[65536]; int total = 0; while (true) { int n = in.read(buf); if (n <= 0) return; out.write(buf, 0, n); if ((total+n)/100000 > total/100000) print(pat.replace("{*}", str(roundDownTo(100000, total)))); total += n; } } catch (Exception __e) { throw rethrow(__e); } } static File renameFile_assertTrue(File a, File b) { try { if (a.equals(b)) return b; // no rename necessary if (!a.exists()) throw fail("Source file not found: " + f2s(a)); if (b.exists()) throw fail("Target file exists: " + f2s(b)); mkdirsForFile(b); if (!a.renameTo(b)) throw fail("Can't rename " + f2s(a) + " to " + f2s(b)); return b; } catch (Exception __e) { throw rethrow(__e); } } static Map syncMRUCache(int size) { return synchroMap(new MRUCache(size)); } static volatile Object isAllowed_function; // func(S, O[]) -> bool static volatile boolean isAllowed_all = true; static boolean isAllowed(String askingMethod, Object... args) { // check on VM level Object f = vm_generalMap_get("isAllowed_function"); if (f != null && !isTrue(callF(f, askingMethod, args))) return false; // check locally return isAllowed_all || isTrue(callF(isAllowed_function, askingMethod, args)); } static Throwable getInnerException(Throwable e) { if (e == null) return null; while (e.getCause() != null) e = e.getCause(); return e; } static Throwable getInnerException(Runnable r) { return getInnerException(getException(r)); } static String baseClassName(String className) { return substring(className, className.lastIndexOf('.')+1); } static String baseClassName(Object o) { return baseClassName(getClassName(o)); } static String prependIfNempty(String prefix, String s) { return empty(s) ? unnull(s) : prefix + s; } static volatile boolean sleep_noSleep = false; static void sleep(long ms) { ping(); if (ms < 0) return; // allow spin locks if (isAWTThread() && ms > 100) throw fail("Should not sleep on AWT thread"); try { Thread.sleep(ms); } catch (Exception e) { throw new RuntimeException(e); } } static void sleep() { try { if (sleep_noSleep) throw fail("nosleep"); print("Sleeping."); sleepQuietly(); } catch (Exception __e) { throw rethrow(__e); } } static long round(double d) { return Math.round(d); } static String round(String s) { return roundBracket(s); } static Complex round(Complex c) { return new Complex(round(c.re), round(c.im)); } static String hmsWithColons() { return hmsWithColons(now()); } static String hmsWithColons(long time) { return new SimpleDateFormat("HH:mm:ss").format(time); } static A optParam(ThreadLocal tl, A defaultValue) { return optPar(tl, defaultValue); } static A optParam(ThreadLocal tl) { return optPar(tl); } static Object optParam(String name, Map params) { return mapGet(params, name); } // now also takes a map as single array entry static A optParam(Object[] opt, String name, A defaultValue) { int n = l(opt); if (n == 1 && opt[0] instanceof Map) { Map map = (Map) (opt[0]); return map.containsKey(name) ? (A) map.get(name) : defaultValue; } if (!even(l(opt))) throw fail("Odd parameter length"); for (int i = 0; i < l(opt); i += 2) if (eq(opt[i], name)) return (A) opt[i+1]; return defaultValue; } static Object optParam(Object[] opt, String name) { return optParam(opt, name, null); } static Object optParam(String name, Object[] params) { return optParam(params, name); } public static String bytesToHex(byte[] bytes) { return bytesToHex(bytes, 0, bytes.length); } public static String bytesToHex(byte[] bytes, int ofs, int len) { StringBuilder stringBuilder = new StringBuilder(len*2); for (int i = 0; i < len; i++) { String s = "0" + Integer.toHexString(bytes[ofs+i]); stringBuilder.append(s.substring(s.length()-2, s.length())); } return stringBuilder.toString(); } static byte[] toUtf8(String s) { try { return s.getBytes(utf8charset()); } catch (Exception __e) { throw rethrow(__e); } } static String _computerID; static Lock computerID_lock = lock(); public static String computerID() { if (_computerID == null) { Lock __0 = computerID_lock; lock(__0); try { if (_computerID != null) return _computerID; File file = computerIDFile(); _computerID = loadTextFile(file.getPath()); if (_computerID == null) { // legacy load _computerID = loadTextFile(userDir(".tinybrain/computer-id")); if (_computerID == null) _computerID = makeRandomID(12, new SecureRandom()); saveTextFile(file, _computerID); } } finally { unlock(__0); } } return _computerID; } static Object[] mapToObjectArray(Map map) { List l = new ArrayList(); for (Object o : keys(map)) { l.add(o); l.add(map.get(o)); } return toObjectArray(l); } static Object[] mapToObjectArray(Object f, Collection l) { int n = l(l); Object[] array = new Object[n]; if (n != 0) { Iterator it = iterator(l); for (int i = 0; i < n; i++) array[i] = callF(f, it.next()); } return array; } static Object[] mapToObjectArray(Object f, Object[] l) { int n = l(l); Object[] array = new Object[n]; for (int i = 0; i < n; i++) array[i] = callF(f, l[i]); return array; } static Object[] mapToObjectArray(Collection l, IF1 f) { return mapToObjectArray(f, l); } static Object[] mapToObjectArray(A[] l, IF1 f) { return mapToObjectArray(f, l); } static Object[] mapToObjectArray(IF1 f, A[] l) { int n = l(l); Object[] array = new Object[n]; for (int i = 0; i < n; i++) array[i] = f.get(l[i]); return array; } static Object[] mapToObjectArray(IF1 f, Collection l) { int n = l(l); Object[] array = new Object[n]; if (n != 0) { Iterator it = iterator(l); for (int i = 0; i < n; i++) array[i] = callF(f, it.next()); } return array; } // this should be on by default now I think, but it may break // legacy code... static ThreadLocal htmlencode_forParams_useV2 = new ThreadLocal(); static String htmlencode_forParams(String s) { if (s == null) return ""; if (isTrue(htmlencode_forParams_useV2.get())) return htmlencode_forParams_v2(s); StringBuilder out = new StringBuilder(Math.max(16, s.length())); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c > 127 || c == '"' || c == '<' || c == '>') { out.append("&#"); out.append((int) c); out.append(';'); } else out.append(c); } return out.toString(); } static String formatSnippetIDOpt(String s) { return isSnippetID(s) ? formatSnippetID(s) : s; } static Class getMainClass() { return mc(); } static Class getMainClass(Object o) { try { if (o == null) return null; if (o instanceof Class && eq(((Class) o).getName(), "x30")) return (Class) o; ClassLoader cl = (o instanceof Class ? (Class) o : o.getClass()).getClassLoader(); if (cl == null) return null; String name = mainClassNameForClassLoader(cl); return loadClassFromClassLoader_orNull(cl, name); } catch (Exception __e) { throw rethrow(__e); } } static String or2(String a, String b) { return nempty(a) ? a : b; } static String or2(String a, String b, String c) { return or2(or2(a, b), c); } static File javaxDataDir_dir; // can be set to work on different base dir static File javaxDataDir() { return javaxDataDir_dir != null ? javaxDataDir_dir : new File(userHome(), "JavaX-Data"); } static File javaxDataDir(String... subs) { return newFile(javaxDataDir(), subs); } static File muricaPasswordFile() { return new File(javaxSecretDir(), "murica/muricaPasswordFile"); } static ThreadLocal> checkFileNotTooBigToRead_tl = new ThreadLocal(); static void checkFileNotTooBigToRead(File f) { callF(checkFileNotTooBigToRead_tl.get(), f); } static File getProgramDir() { return programDir(); } static File getProgramDir(String snippetID) { return programDir(snippetID); } // TODO: optimize to x-(x%n) in case that's the same thing // (or x-mod(x,n)?) static int roundDownTo(int n, int x) { return x/n*n; } static long roundDownTo(long n, long x) { return x/n*n; } static Throwable getException(Runnable r) { try { callF(r); return null; } catch (Throwable e) { return e; } } static Object sleepQuietly_monitor = new Object(); static void sleepQuietly() { try { assertFalse(isAWTThread()); synchronized(sleepQuietly_monitor) { sleepQuietly_monitor.wait(); } } catch (Exception __e) { throw rethrow(__e); } } static boolean even(int i) { return (i & 1) == 0; } static boolean even(long i) { return (i & 1) == 0; } static boolean even(BigInteger n) { return even(n.intValue()); } static Charset utf8charset_cache; static Charset utf8charset() { if (utf8charset_cache == null) utf8charset_cache = utf8charset_load(); return utf8charset_cache;} static Charset utf8charset_load() { return Charset.forName("UTF-8"); } static void lock(Lock lock) { try { ping(); if (lock == null) return; try { vmBus_send("locking", lock, "thread" , currentThread()); lock.lockInterruptibly(); vmBus_send("locked", lock, "thread" , currentThread()); } catch (InterruptedException e) { Object reason = vm_threadInterruptionReasonsMap().get(currentThread()); print("Locking interrupted! Reason: " + strOr(reason, "Unknown")); printStackTrace(e); rethrow(e); } // NO call to ping here! Make sure lock is always released. } catch (Exception __e) { throw rethrow(__e); } } static void lock(Lock lock, String msg) { print("Locking: " + msg); lock(lock); } static void lock(Lock lock, String msg, long timeout) { print("Locking: " + msg); lockOrFail(lock, timeout); } static ReentrantLock lock() { return fairLock(); } static File computerIDFile() { return javaxDataDir("Basic Info/computer-id.txt"); } static String makeRandomID(int length) { return makeRandomID(length, defaultRandomGenerator()); } static String makeRandomID(int length, Random random) { char[] id = new char[length]; for (int i = 0; i < id.length; i++) id[i] = (char) ((int) 'a' + random.nextInt(26)); return new String(id); } static String makeRandomID(Random r, int length) { return makeRandomID(length, r); } /** writes safely (to temp file, then rename) */ static File saveTextFile(String fileName, String contents) throws IOException { /*ifdef CriticalActions temp beginCriticalAction("Saving file " + fileName + " (" + l(contents) + " chars)"); endifdef*/ File file = new File(fileName); mkdirsForFile(file); String tempFileName = fileName + "_temp"; File tempFile = new File(tempFileName); if (contents != null) { if (tempFile.exists()) try { String saveName = tempFileName + ".saved." + now(); copyFile(tempFile, new File(saveName)); } catch (Throwable e) { printStackTrace(e); } FileOutputStream fileOutputStream = newFileOutputStream(tempFile.getPath()); try { OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8"); PrintWriter printWriter = new PrintWriter(outputStreamWriter); printWriter.print(contents); printWriter.close(); } finally { _close(fileOutputStream); }} if (file.exists() && !file.delete()) throw new IOException("Can't delete " + fileName); if (contents != null) if (!tempFile.renameTo(file)) throw new IOException("Can't rename " + tempFile + " to " + file); vmBus_send("wroteFile", file); return file; } static File saveTextFile(File fileName, String contents) { try { saveTextFile(fileName.getPath(), contents); return fileName; } catch (Exception __e) { throw rethrow(__e); } } static void unlock(Lock lock, String msg) { if (lock == null) return; lock.unlock(); vmBus_send("unlocked", lock, "thread" , currentThread()); print("Unlocked: " + msg); // print afterwards to make sure the lock is always unlocked } static void unlock(Lock lock) { if (lock == null) return; lock.unlock(); vmBus_send("unlocked", lock, "thread" , currentThread()); } static String htmlencode_forParams_v2(String s) { if (s == null) return ""; StringBuilder out = new StringBuilder(Math.max(16, s.length())); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c > 127 || c == '"' || c == '<' || c == '>' || c == '&') { out.append("&#"); out.append((int) c); out.append(';'); } else out.append(c); } return out.toString(); } static Class loadClassFromClassLoader_orNull(ClassLoader cl, String name) { try { return cl == null ? null : cl.loadClass(name); } catch (ClassNotFoundException e) { return null; } } static File javaxSecretDir_dir; // can be set to work on different base dir static File javaxSecretDir() { return javaxSecretDir_dir != null ? javaxSecretDir_dir : new File(userHome(), "JavaX-Secret"); } static File javaxSecretDir(String sub) { return newFile(javaxSecretDir(), sub); } static File programDir_mine; // set this to relocate program's data static File programDir() { return programDir(getProgramID()); } static File programDir(String snippetID) { boolean me = sameSnippetID(snippetID, programID()); if (programDir_mine != null && me) return programDir_mine; File dir = new File(javaxDataDir(), formatSnippetIDOpt(snippetID)); if (me) { String c = caseID(); if (nempty(c)) dir = newFile(dir, c); } return dir; } static File programDir(String snippetID, String subPath) { return new File(programDir(snippetID), subPath); } static Map vm_threadInterruptionReasonsMap() { return vm_generalWeakSubMap("Thread interruption reasons"); } static String strOr(Object o, String ifNull) { return o == null ? ifNull : str(o); } static void lockOrFail(Lock lock, long timeout) { try { ping(); vmBus_send("locking", lock, "thread" , currentThread()); if (!lock.tryLock(timeout, TimeUnit.MILLISECONDS)) { String s = "Couldn't acquire lock after " + timeout + " ms."; if (lock instanceof ReentrantLock) { ReentrantLock l = (ReentrantLock) lock; s += " Hold count: " + l.getHoldCount() + ", owner: " + call(l, "getOwner"); } throw fail(s); } vmBus_send("locked", lock, "thread" , currentThread()); ping(); } catch (Exception __e) { throw rethrow(__e); } } static ReentrantLock fairLock() { return new ReentrantLock(true); } static File copyFile(File src, File dest) { try { FileInputStream inputStream = new FileInputStream(src.getPath()); FileOutputStream outputStream = newFileOutputStream(dest.getPath()); try { copyStream(inputStream, outputStream); inputStream.close(); } finally { outputStream.close(); } return dest; } catch (Exception __e) { throw rethrow(__e); } } static boolean sameSnippetID(String a, String b) { if (!isSnippetID(a) || !isSnippetID(b)) return false; return parseSnippetID(a) == parseSnippetID(b); } static volatile String caseID_caseID; static String caseID() { return caseID_caseID; } static void caseID(String id) { caseID_caseID = id; } static void copyStream(InputStream in, OutputStream out) { try { byte[] buf = new byte[65536]; while (true) { int n = in.read(buf); if (n <= 0) return; out.write(buf, 0, n); } } catch (Exception __e) { throw rethrow(__e); } } // elements are put to front when added (not when accessed) static class MRUCache extends LinkedHashMap { int maxSize = 10; MRUCache() {} MRUCache(int maxSize) { this.maxSize = maxSize;} protected boolean removeEldestEntry(Map.Entry eldest) { return size() > maxSize; } Object _serialize() { return ll(maxSize, cloneLinkedHashMap(this)); } static MRUCache _deserialize(List l) { MRUCache m = new MRUCache(); m.maxSize = (int) first(l); m.putAll((LinkedHashMap) second(l)); return m; } } static class Chain implements Iterable { A element; Chain next; int size; Chain() {} Chain(A element) { this.element = element; size = 1; } Chain(A element, Chain next) { this.next = next; this.element = element; size = next != null ? next.size+1 : 1; } public String toString() { return str(toList()); } ArrayList toList() { ArrayList l = emptyList(size); Chain c = this; while (c != null) { l.add(c.element); c = c.next; } return l; } // TODO: optimize public Iterator iterator() { return toList().iterator(); } } static class ReverseChain implements Iterable { A element; ReverseChain prev; int size; ReverseChain() {} ReverseChain(ReverseChain prev, A element) { this.element = element; this.prev = prev; if (prev == null) size = 1; else { prev.check(); size = prev.size+1; } } void check() { if (size < 1) throw fail("You called the ReverseChain default constructor. Don't do that"); } public String toString() { return str(toList()); } ArrayList toList() { check(); ArrayList l = emptyList(size); for (int i = 0; i < size; i++) l.add(null); int i = size; ReverseChain c = this; while (c != null) { l.set(--i, c.element); c = c.prev; } return l; } public Iterator iterator() { return toList().iterator(); } } static class Value implements IF0 , IFieldsToList{ A value; Value() {} Value(A value) { this.value = value;} public boolean equals(Object o) { if (!(o instanceof Value)) return false; Value __1 = (Value) o; return eq(value, __1.value); } public int hashCode() { int h = 82420049; h = boostHashCombine(h, _hashCode(value)); return h; } public Object[] _fieldsToList() { return new Object[] {value}; } public A get() { return value; } public String toString() { return str(get()); } } // a variant of thread where you can get the Runnable target later. // Also notes its existence on the VM bus. // We should use this exclusively instead of Thread. static class BetterThread extends Thread { Runnable target; BetterThread(Runnable target) { this.target = target; _created(); } BetterThread(Runnable target, String name) { super(name); this.target = target; _created(); } void _created() { vmBus_send("threadCreated", this); } public void run() { try { try { vmBus_send("threadStarted", this); if (target != null) target.run(); } finally { vmBus_send("threadEnded", this); } } catch (Exception __e) { throw rethrow(__e); } } Runnable getTarget() { return target; } } static A second(List l) { return get(l, 1); } static A second(Iterable l) { if (l == null) return null; Iterator it = iterator(l); if (!it.hasNext()) return null; it.next(); return it.hasNext() ? it.next() : null; } static A second(A[] bla) { return bla == null || bla.length <= 1 ? null : bla[1]; } static B second(Pair p) { return p == null ? null : p.b; } static B second(T3 t) { return t == null ? null : t.b; } static char second(String s) { return charAt(s, 1); } static char charAt(String s, int i) { return s != null && i >= 0 && i < s.length() ? s.charAt(i) : '\0'; } }