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 static x30_pkg.x30_util.DynamicObject;
import java.text.*;
import java.text.NumberFormat;
import java.util.TimeZone;
import java.awt.geom.*;
class main {
static class GeometricPriceCells implements PriceCells {
GeometricPriceCells() {}
// cell size in percent
final public GeometricPriceCells setCellSizeInPercent(double cellSizeInPercent){ return cellSizeInPercent(cellSizeInPercent); }
public GeometricPriceCells cellSizeInPercent(double cellSizeInPercent) { this.cellSizeInPercent = cellSizeInPercent; return this; } final public double getCellSizeInPercent(){ return cellSizeInPercent(); }
public double cellSizeInPercent() { return cellSizeInPercent; }
double cellSizeInPercent;
// one of the cell limits
final public GeometricPriceCells setBasePrice(double basePrice){ return basePrice(basePrice); }
public GeometricPriceCells basePrice(double basePrice) { this.basePrice = basePrice; return this; } final public double getBasePrice(){ return basePrice(); }
public double basePrice() { return basePrice; }
double basePrice;
GeometricPriceCells(double cellSizeInPercent) {
this.cellSizeInPercent = cellSizeInPercent;}
GeometricPriceCells(double basePrice, double cellSizeInPercent) {
this.cellSizeInPercent = cellSizeInPercent;
this.basePrice = basePrice;}
double ratio() {
return 1+cellSizeInPercent/100;
}
double toLogScale(double price) {
return log(price, ratio());
}
double fromLogScale(double logPrice) {
return pow(ratio(), logPrice);
}
double logBasePrice() {
return toLogScale(basePrice);
}
double remainder(double price) {
return remainder(toLogScale(price));
}
double remainderLog(double logPrice) {
return frac(logPrice-logBasePrice());
}
// TODO: fix this logic's rounding problems
public boolean isCellLimit(double price) {
return remainder(price) == 0;
}
public double nextCellLimitLog(double logPrice) {
double r = remainderLog(logPrice);
return logPrice + 1-r;
}
public double nextCellLimit(double price) {
return fromLogScale(nextCellLimitLog(toLogScale(price)));
}
public double previousCellLimitLog(double logPrice) {
double r = remainderLog(logPrice);
return logPrice - (r == 0 ? 1 : r);
}
public double previousCellLimit(double price) {
return fromLogScale(previousCellLimitLog(toLogScale(price)));
}
public double nCellLimitsDown(double price, int n) {
double logPrice = toLogScale(price);
logPrice = previousCellLimitLog(logPrice)-(n-1);
return fromLogScale(logPrice);
}
public double nCellLimitsUp(double price, int n) {
double logPrice = toLogScale(price);
logPrice = nextCellLimitLog(logPrice)+n;
return fromLogScale(logPrice);
}
public double priceToCellNumber(double price) {
return toLogScale(price)-logBasePrice();
}
public double cellNumberToPrice(double cellNumber) {
return fromLogScale(cellNumber+logBasePrice());
}
public String toString() {
return formatDouble2(cellSizeInPercent) + "% cells with C0=" + formatPrice(basePrice);
}
}
static double log(double d) {
return Math.log(d);
}
static double log(double d, double base) {
return Math.log(d)/Math.log(base);
}
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 double pow(double a, double b) {
return Math.pow(a, b);
}
static float pow(float a, float b) {
return (float) Math.pow(a, b);
}
static BigInteger pow(BigInteger a, int b) {
return a.pow(b);
}
static double frac(double d) {
return fraction(d);
}
static String formatDouble2(double d) {
return formatDouble(d, 2);
}
static String formatPrice(double price) {
//ret formatDouble2(price);
return formatDouble_significant(price, 6);
}
static double fraction(double d) {
return d % 1;
}
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_significant(double d, int digits) {
return formatDouble_significant2(d, digits);
}
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 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 String formatDouble_significant2(double d, int digits) {
try {
digits -= Math.floor(max(-10, Math.log10(abs(d))+1));
return formatDouble(d, digits);
} catch (Throwable _e) {
print("Had number: " + d + ", digits: " + digits);
throw rethrow(_e); }
}
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 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 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 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