import java.util.*;
import java.util.zip.*;
import java.util.List;
import java.util.regex.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.function.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.table.*;
import java.io.*;
import java.net.*;
import java.lang.reflect.*;
import java.lang.ref.*;
import java.lang.management.*;
import java.security.*;
import java.security.spec.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import javax.imageio.*;
import java.math.*;
import java.time.Duration;
import java.lang.invoke.VarHandle;
import java.lang.invoke.MethodHandles;
import java.awt.geom.*;
import static x30_pkg.x30_util.DynamicObject;
import java.text.*;
import java.text.NumberFormat;
import java.util.TimeZone;
class main {
static class TradingCandle {
// min, max, start and end prices
final public TradingCandle setMin(double min){ return min(min); }
public TradingCandle min(double min) { this.min = min; return this; } final public double getMin(){ return min(); }
public double min() { return min; }
double min = infinity();
final public TradingCandle setMax(double max){ return max(max); }
public TradingCandle max(double max) { this.max = max; return this; } final public double getMax(){ return max(); }
public double max() { return max; }
double max;
final public TradingCandle setStart(double start){ return start(start); }
public TradingCandle start(double start) { this.start = start; return this; } final public double getStart(){ return start(); }
public double start() { return start; }
double start;
final public TradingCandle setEnd(double end){ return end(end); }
public TradingCandle end(double end) { this.end = end; return this; } final public double getEnd(){ return end(); }
public double end() { return end; }
double end;
final public TradingCandle setStartTime(Timestamp startTime){ return startTime(startTime); }
public TradingCandle startTime(Timestamp startTime) { this.startTime = startTime; return this; } final public Timestamp getStartTime(){ return startTime(); }
public Timestamp startTime() { return startTime; }
Timestamp startTime;
final public TradingCandle setEndTime(Timestamp endTime){ return endTime(endTime); }
public TradingCandle endTime(Timestamp endTime) { this.endTime = endTime; return this; } final public Timestamp getEndTime(){ return endTime(); }
public Timestamp endTime() { return endTime; }
Timestamp endTime;
final public TradingCandle setProjectedEndTime(long projectedEndTime){ return projectedEndTime(projectedEndTime); }
public TradingCandle projectedEndTime(long projectedEndTime) { this.projectedEndTime = projectedEndTime; return this; } final public long getProjectedEndTime(){ return projectedEndTime(); }
public long projectedEndTime() { return projectedEndTime; }
long projectedEndTime;
final public TradingCandle setOngoing(boolean ongoing){ return ongoing(ongoing); }
public TradingCandle ongoing(boolean ongoing) { this.ongoing = ongoing; return this; } final public boolean getOngoing(){ return ongoing(); }
public boolean ongoing() { return ongoing; }
boolean ongoing = false;
final public TradingCandle setCoin(String coin){ return coin(coin); }
public TradingCandle coin(String coin) { this.coin = coin; return this; } final public String getCoin(){ return coin(); }
public String coin() { return coin; }
String coin;
final double startingPrice(){ return openingPrice(); }
final double open(){ return openingPrice(); }
double openingPrice() { return start; }
final double endPrice(){ return closingPrice(); }
final double close(){ return closingPrice(); }
double closingPrice() { return end; }
TradingCandle open(double x) { return start(x); }
TradingCandle close(double x) { return end(x); }
TradingCandle high(double x) { return max(x); }
TradingCandle low(double x) { return min(x); }
boolean isGreen() {
return end > start;
}
boolean isRed() {
return end < start;
}
boolean isWhite() {
return end == start;
}
String colorText() {
return isGreen() ? "green"
: isRed() ? "red"
: "white";
}
double durationInSeconds() {
return toSeconds(endTime.minus(startTime));
}
void addValue(double price, Timestamp timestamp) {
if (price < min) min = price;
if (price > max) max = price;
end = price;
endTime = timestamp;
if (startTime == null) {
startTime = timestamp;
start = price;
}
}
String myType() { return "candle"; }
public String toString() {
String text = firstToUpper(colorText()) + " " + myType();
if (startTime != null) {
text += " starting " + startTime + ", duration " +
iround(toSeconds(endTime.minus(startTime))) + "s";
text += ", starting price " + formatPrice(start) + ", end price " + formatPrice(end);
text += ", min " + formatPrice(min) + ", max " + formatPrice(max);
}
return text;
}
double changeRatio() {
return doubleRatio(end, start);
}
public TradingCandle clone() {
return shallowClone(this);
}
Color color() {
return directionToCandleColor(end-start);
}
final double mid(){ return hl2(); }
double hl2() { return avg(min, max); }
final double move(){ return delta(); }
double delta() { return end-start; }
double high() { return max; }
double low() { return min; }
TradingCandle startTime(long startTime) { return startTime(toTimestamp(startTime)); }
TradingCandle endTime(long endTime) { return endTime(toTimestamp(endTime)); }
Timestamp openingTime() { return startTime; }
Timestamp closingTime() { return endTime; }
Timestamp middleTime() { return new Timestamp((startTime.toLong()+endTime.toLong())/2); }
// whichever one is later
long endOrProjectedEndTime() {
return main.max(endTime().unixDate(), projectedEndTime);
}
}
static int min(int a, int b) {
return Math.min(a, b);
}
static long min(long a, long b) {
return Math.min(a, b);
}
static float min(float a, float b) { return Math.min(a, b); }
static float min(float a, float b, float c) { return min(min(a, b), c); }
static double min(double a, double b) {
return Math.min(a, b);
}
static double min(double[] c) {
double x = Double.MAX_VALUE;
for (double d : c) x = Math.min(x, d);
return x;
}
static float min(float[] c) {
float x = Float.MAX_VALUE;
for (float d : c) x = Math.min(x, d);
return x;
}
static byte min(byte[] c) {
byte x = 127;
for (byte d : c) if (d < x) x = d;
return x;
}
static short min(short[] c) {
short x = 0x7FFF;
for (short d : c) if (d < x) x = d;
return x;
}
static int min(int[] c) {
int x = Integer.MAX_VALUE;
for (int d : c) if (d < x) x = d;
return x;
}
static > A min(A a, A b) {
return cmp(a, b) <= 0 ? a : b;
}
static double infinity() {
return positiveInfinity();
}
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 double toSeconds(long ms) {
return ms/1000.0;
}
static String toSeconds(long ms, int digits) {
return formatDouble(toSeconds(ms), digits);
}
static double toSeconds(double ms) {
return ms/1000.0;
}
static String toSeconds(double ms, int digits) {
return formatDouble(toSeconds(ms), digits);
}
static String firstToUpper(String s) {
if (empty(s)) return s;
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
}
static int iround(double d) {
return (int) Math.round(d);
}
static int iround(Number n) {
return iround(toDouble(n));
}
static String formatPrice(double price) {
//ret formatDouble2(price);
return formatDouble_significant(price, 6);
}
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 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 java.awt.Color color(String hex) {
return awtColor(hex);
}
static Color directionToCandleColor(double direction) { return directionToCandleColor(direction, Color.white); }
static Color directionToCandleColor(double direction, Color zeroColor) {
return direction < 0 ? colorFromHex("f1493f")
: direction > 0 ? colorFromHex("1da2b4")
: zeroColor;
}
static float avg(float a, float b) {
return (a+b)/2;
}
static double avg(double a, double b) {
return (a+b)/2;
}
static double avg(double[] l) {
return doubleAverage(l);
}
static Timestamp toTimestamp(long unixTime) {
return unixTime == 0 ? null : new Timestamp(unixTime);
}
static Timestamp toTimestamp(java.nio.file.attribute.FileTime fileTime) {
return fileTime == null ? null : toTimestamp(fileTime.toMillis());
}
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 positiveInfinity() {
return Double.POSITIVE_INFINITY;
}
static Iterator iterator(Iterable c) {
return c == null ? emptyIterator() : c.iterator();
}
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 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(Rect r) { return !(r != null && r.w != 0 && r.h != 0); }
static boolean empty(Chain c) { return c == null; }
static boolean empty(AppendableChain c) { return c == null; }
static boolean empty(IntSize l) { return l == null || l.size() == 0; }
static 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 String formatDouble_significant(double d, int digits) {
return formatDouble_significant2(d, digits);
}
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 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 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 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 java.awt.Color awtColor(String hex) {
byte[] b = bytesFromHex(dropPrefix("#", hex));
return new Color(ubyteToInt(b[0]), ubyteToInt(b[1]), ubyteToInt(b[2]));
}
static java.awt.Color colorFromHex(String hex) {
return awtColor(hex);
}
static double doubleAverage(int... a) {
double sum = 0;
for (int i = 0; i < l(a); i++)
sum += a[i];
return doubleRatio(sum, l(a));
}
static double doubleAverage(double... l) {
return doubleRatio(doubleSum(l), l(l));
}
static double doubleAverage(Iterable l) {
double sum = 0;
long n = 0;
for (Double i : unnullForIteration(l))
if (i != null) { sum += i; ++n; }
return doubleRatio(sum, n);
}
static Iterator emptyIterator() {
return Collections.emptyIterator();
}
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 c == null ? "null" : new String(c);
}
static String str(char[] c, int offset, int count) {
return new String(c, offset, count);
}
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 getType(Object o) {
return getClassName(o);
}
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 double parseDouble(String s) {
return empty(s) ? 0.0 : Double.parseDouble(s);
}
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); }
}
// 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;
if (o == null) return null;
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