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 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 javax.imageio.*;
import java.math.*;
import java.text.SimpleDateFormat;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import jdk.incubator.vector.*;
class main {
// TODO: image w/h not divisible by 8
final static class BWIntegralImage_doubleVectorized implements MakesBufferedImage, IBWIntegralImage {
  // dual logarithm of block size & corresponding int vector species
  
  
  
  
  int w, h; // actual image size
  int blockW, blockH; // width and height of block array
  Block[] blocks;
  
  
  static class Block {
    int[] rowAndColSums = new int[(1 << 3)*2]; // length 16
    int sum;
    int[] data; // length 64 if calculated
  }
  
  BWIntegralImage_doubleVectorized() {}
  BWIntegralImage_doubleVectorized(File f) { this(loadImage2(f)); }
  
  BWIntegralImage_doubleVectorized(MakesBufferedImage img) { this(toBufferedImage(img)); }
  
  BWIntegralImage_doubleVectorized(BufferedImage image) { try {
    alloc(image.getWidth(), image.getHeight());
    
    // Grab image
    // TODO: could use grayscale color model here (faster?)
    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");
      
    // for brightness of pixels,
    // for now we cheat by just using one of the channels
    
    // calculate sums in each block
    
    int iBlock = 0;
    for (int by = 0; by < blockH; by++) {
      int iLine = (by << 3)*w;
      for (int bx = 0; bx < blockW; bx++) {
        Block block = blocks[iBlock++];
        int[] sums = block.rowAndColSums;
        
        IntVector vColSums = by == 0 ? IntVector.zero(IntVector.SPECIES_256) : IntVector.fromArray(IntVector.SPECIES_256, getBlock(bx, by-1).rowAndColSums, (1 << 3));
        int[] leftSums = bx == 0 ? null : getBlock(bx-1, by).rowAndColSums;
        
        for (int y = 0; y < (1 << 3); y++) {
          IntVector v = IntVector.fromArray(IntVector.SPECIES_256, data, y << 3);
          int leftSum = leftSums != null ? leftSums[y] : 0;
          sums[y] = v.reduceLanes(VectorOperators.ADD)+leftSum;
          v = v.add(leftSum);
          vColSums = vColSums.add(v);
        }
        vColSums.intoArray(sums, (1 << 3));
        block.sum = vColSums.reduceLanes(VectorOperators.ADD);
      }
    }
  } catch (Exception __e) { throw rethrow(__e); } }
  
  int blockSum(int bx, int by) {
    return bx < 0 || by < 0 ? 0 : getBlock(bx, by).sum;
  }
  
  Block getBlock(int bx, int by) { return blocks[by*blockW+bx]; }
  
  int[] getBlockData(int bx, int by) {
    Block block = getBlock(bx, by);
    if (block.data == null)
      calcData(bx, by, block);
    return block.data;
  }
  
  void calcData(int bx, int by, Block block) {
    throw todo();
  }
  
  private void alloc(int w, int h) {
    if ((w % (1 << 3)) != 0 || (h % (1 << 3)) != 0)
      throw fail("Need image dimensions divisible by " + (1 << 3) + ": " + w + "*" + h);
    this.w = w;
    this.h = h;
    blockW = ratioRoundUp(w, (1 << 3));
    blockH = ratioRoundUp(h, (1 << 3));
    //int dataLength = blockSize*blockSize;
    blocks = repF_array(Block.class,blockW*blockH, () -> new Block());
  }
  
  // get sum value at x, y
  // pixels outside of image are considered black
  public int getIIValue(int x, int y) {
    
    if (x < 0 || y < 0 || x >= w || y >= h) return 0;
    int idx = ((x & ((1 << 3)-1)) << (1 << 3)) | (y & ((1 << 3)-1));
    return idx == 0 ? getBlock(x >> 3, y >> 3).sum
      : getBlockData(x >> 3, y >> 3)[idx];
  }
  
  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);
  }
  
  int getPixel(int x, int y) {
    return bwIntegralImage_sumRect(this, x, y, x+1, y+1);
  }
  
  int getPixel(Pt p) { return getPixel(p.x, p.y); }
  
  public int getWidth() { return w; }
  public int getHeight() { return h; }
  
  // unoptimized
  public BufferedImage getBufferedImage() {
    return scaleDownUsingIntegralImageBW(this, w, h).getBufferedImage();
  }
}
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 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(String msg) { throw new RuntimeException(msg == null ? "" : msg); }
static RuntimeException fail(String msg, Throwable innerException) { throw new RuntimeException(msg, innerException); }
static RuntimeException rethrow(Throwable t) {
  
  if (t instanceof Error)
    _handleError((Error) t);
  
  throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
}
static RuntimeException rethrow(String msg, Throwable t) {
  throw new RuntimeException(msg, t);
}
  static RuntimeException todo() {
    throw new RuntimeException("TODO");
  }
  
  static RuntimeException todo(Object msg) {
    throw new RuntimeException("TODO: " + msg);
  }
static int ratioRoundUp(double a, double b) {
  return iceil(ratio(a, b));
}
static int ratioRoundUp(int a, int b) {
  return (a+b-1)/b;
}
static  A[] repF_array(int n, IF0 f) {
  A[] a =  (A[]) (new Object[n]);
  for (int i = 0; i < n; i++)
    a[i] = f.get();
  return a;
}
static  A[] repF_array(Class type, int n, IF0 f) {
  A[] a = newArrayOfType(type, n);
  for (int i = 0; i < n; i++)
    a[i] = f.get();
  return a;
}
static double doubleRatio(double x, double y) {
  return y == 0 ? 0 : x/y;
}
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 BWImage scaleDownUsingIntegralImageBW(int w, BWIntegralImage img) {
  return scaleDownUsingIntegralImageBW(img, w);
}
static BWImage scaleDownUsingIntegralImageBW(BWIntegralImage img, int w) {
  return scaleDownUsingIntegralImageBW(img, w, iround(w*img.h/(double) img.w));
}
static BWImage scaleDownUsingIntegralImageBW(BWIntegralImage img, int w, int h) {
  int w1 = img.w, h1 = img.h;
  BWImage out = new BWImage(w, h);
  for (int y = 0; y < h; y++)
    for (int x = 0; x < w; x++) {
      int x1 = x*w1/w, x2 = max(x1+1, (x+1)*w1/w);
      int y1 = y*h1/h, y2 = max(y1+1, (y+1)*h1/h);
      int area = (x2-x1)*(y2-y1);
      int pixel = bwIntegralImage_sumRect(img, x1, y1, x2, y2)/area;
      out.setByte(x, y, (byte) pixel);
    }
  return out;
}
static BWImage scaleDownUsingIntegralImageBW(IBWIntegralImage img, int w) { return scaleDownUsingIntegralImageBW(img, w, iround(w*img.getHeight()/(double) img.getWidth())); }
static BWImage scaleDownUsingIntegralImageBW(IBWIntegralImage img, int w, int h) {
  int w1 = img.getWidth(), h1 = img.getHeight();
  BWImage out = new BWImage(w, h);
  for (int y = 0; y < h; y++)
    for (int x = 0; x < w; x++) {
      int x1 = x*w1/w, x2 = max(x1+1, (x+1)*w1/w);
      int y1 = y*h1/h, y2 = max(y1+1, (y+1)*h1/h);
      int area = (x2-x1)*(y2-y1);
      int pixel = bwIntegralImage_sumRect(img, x1, y1, x2, y2)/area;
      out.setByte(x, y, (byte) pixel);
    }
  return out;
}
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 (isAbsolutePath(snippetIDOrURLOrFile)) 
    return loadBufferedImage(new File(snippetIDOrURLOrFile));
  
  if (!isSnippetID(snippetIDOrURLOrFile))
    throw fail("Not a URL or snippet ID or file: " + 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;
} catch (Exception __e) { throw rethrow(__e); } }
static BufferedImage loadBufferedImage(File file) { try {
  return file.isFile() ? ImageIO.read(file) : null;
} catch (Exception __e) { throw rethrow(__e); } }
static BufferedImage toBufferedImageOpt(Object o) {
  if (o instanceof BufferedImage) return (BufferedImage) o;
  if (o instanceof MakesBufferedImage)
    return ((MakesBufferedImage) o).getBufferedImage();
  if (o instanceof File)
    if (isImageFile(((File) o)))
      return loadImage2(((File) o));
  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");
    
  if (eq(c, "main$PNGFile"))
    return (BufferedImage) call(o, "getImage");
  return null;
}
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 int iceil(double d) {
  return (int) Math.ceil(d);
}
static long ratio(long x, long y) {
  return y == 0 ? 0 : x/y;
}
static double ratio(double x, double y) {
  return y == 0 ? 0 : x/y;
}
static  A[] newArrayOfType(Class type, int n) {
  return makeArray(type, n);
}
static int iround(double d) {
  return (int) Math.round(d);
}
static int iround(Number n) {
  return iround(toDouble(n));
}
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 int max(Collection c) {
  int x = Integer.MIN_VALUE;
  for (int i : c) x = max(x, i);
  return 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;
}
//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();
// always returns true
static boolean ping() {
  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 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); } }
static boolean isAbsolutePath(String s) {
  return s != null && new File(s).isAbsolute();
}
static boolean isAbsolutePath(File f) {
  return f != null && f.isAbsolute();
}
  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 volatile StringBuffer local_log = new StringBuffer(); // not redirected
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