persistable sclass GeometricPriceCells is PriceCells { // cell size in percent settable double cellSizeInPercent; // one of the cell limits settable double basePrice = 1000; *(double *cellSizeInPercent) {} *(double *basePrice, double *cellSizeInPercent) {} double ratio() { ret 1+cellSizeInPercent/100; } double toLogScale(double price) { ret log(price, ratio()); } double fromLogScale(double logPrice) { ret pow(ratio(), logPrice); } double logBasePrice() { ret toLogScale(basePrice); } double remainder(double price) { ret remainder(toLogScale(price)); } double remainderLog(double logPrice) { ret frac(logPrice-logBasePrice()); } // TODO: fix this logic's rounding problems public bool isCellLimit(double price) { ret remainder(price) == 0; } public double nextCellLimitLog(double logPrice) { double r = remainderLog(logPrice); ret logPrice + 1-r; } public double nextCellLimit(double price) { ret fromLogScale(nextCellLimitLog(toLogScale(price))); } public double previousCellLimitLog(double logPrice) { double r = remainderLog(logPrice); ret logPrice - (r == 0 ? 1 : r); } public double previousCellLimit(double price) { ret fromLogScale(previousCellLimitLog(toLogScale(price))); } public double nCellLimitsDown(double price, int n) { double logPrice = toLogScale(price); logPrice = previousCellLimitLog(logPrice)-(n-1); ret fromLogScale(logPrice); } public double nCellLimitsUp(double price, int n) { double logPrice = toLogScale(price); logPrice = nextCellLimitLog(logPrice)+n; ret fromLogScale(logPrice); } public double priceToCellNumber(double price) { ret toLogScale(price)-logBasePrice(); } public double cellNumberToPrice(double cellNumber) { ret fromLogScale(cellNumber+logBasePrice()); } toString { ret formatDouble2(cellSizeInPercent) + "% cells with C0=" + formatPrice(basePrice); } }