Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- [28/03/2019]
- Roman
- this._decimalValue = [ 1000000, 900000, 500000, 400000, 100000, 90000, 50000, 40000, 10000, 9000, 5000, 4000, 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ];
- this._romanNumeral = [ "M̄", "C̄M̄", "D̄", "C̄D̄", "C̄", "X̄C̄", "L̄", "X̄L̄", "X̄", "ⅯX̄", "V̄", "ⅯV̄", "Ⅿ", "ⅭⅯ", "Ⅾ", "ⅭⅮ", "Ⅽ", "ⅩⅭ", "Ⅼ", "ⅩⅬ", "Ⅹ", "ⅠⅩ","Ⅴ", "ⅠⅤ", "Ⅰ" ];
- this._romanFractions = ["", "·", ":", "∴", "∷", "⁙"];
- this.maximum = 4000000;
- this._maxLog10 = Math.log10(this.maximum);
- formatDecimal(value, places) {
- if (value.lt(this.maximum)) {
- return this.romanize(value.toNumber());
- }
- const log10 = value.log10();
- const maximums = log10 / this._maxLog10;
- const current = Math.pow(this.maximum, maximums - Math.floor(maximums));
- return `${this.romanize(current)}↑${this.formatDecimal(maximums.toDecimal())}`;
- romanize(value) {
- const decimalValue = this._decimalValue;
- const romanNumeral = this._romanNumeral;
- const romanFractions = this._romanFractions;
- let roman = String.empty;
- for (let i = 0; i < decimalValue.length; i++) {
- while (decimalValue[i] <= value) {
- roman += romanNumeral[i];
- value -= decimalValue[i];
- }
- }
- let duodecimal = Math.round(Math.floor(value * 10) * 1.2);
- if (duodecimal === 0) {
- return roman === String.empty ? "nulla" : roman;
- }
- if (duodecimal > 5) {
- duodecimal -= 6;
- roman += "S";
- }
- roman += romanFractions[duodecimal];
- return roman;
- }
- formatDecimal (places does nothing)
- if (value<4e6) romanize(n)
- else romanize(4e6 ^ log4000000(n)%1) + ↑ + formatDecimal(log4000000(n))
- this._decimalValue = [ 1000000, 900000, 500000, 400000, 100000, 90000, 50000, 40000, 10000, 9000, 5000, 4000, 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ];
- this._romanNumeral = [ "M̄", "C̄M̄", "D̄", "C̄D̄", "C̄", "X̄C̄", "L̄", "X̄L̄", "X̄", "ⅯX̄", "V̄", "ⅯV̄", "Ⅿ", "ⅭⅯ", "Ⅾ", "ⅭⅮ", "Ⅽ", "ⅩⅭ", "Ⅼ", "ⅩⅬ", "Ⅹ", "ⅠⅩ","Ⅴ", "ⅠⅤ", "Ⅰ" ];
- this._romanFractions = ["", "·", ":", "∴", "∷", "⁙"];
- romanize(value)
- for each value in decimalValue in order: while value >= decimalValue[i], append the roman value to string and subtract decimalValue[i] from value.
- duodecimal = round(floor(value*10)*1.2) //that *10 should be %10 obviously
- if(duodecimal==0) end; if roman is empty (value = 0) return "nulla", otherwise return the string.
- if(duodecimal>5) add "S", subtract 6
- append the fraction value (romanFractions[duodecimal]) to end
- dots:
- formatUnder1000(value, places) {
- return this.dotify(value * 254);
- }
- formatInfinite() {
- return "⣿⠀⣿";
- }
- formatDecimal(value, places) {
- if (value.lt(16387063.9980315)) {
- return this.dotify(value.toNumber() * 254);
- }
- const log = value.log(254);
- const exponent = Math.floor(log - 2);
- const mantissa = Math.pow(254, log - exponent);
- return this.dotify(exponent) + "⣿" + this.dotify(mantissa * 254);
- }
- dotify(value, pad) {
- const DOT_DIGITS =
- "⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿" +
- "⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿" +
- "⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿" +
- "⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿";
- value = Math.round(value);
- if (!pad && value < 254) return DOT_DIGITS[value + 1];
- if (value < 64516) return DOT_DIGITS[Math.floor(value / 254) + 1] + DOT_DIGITS[value % 254 + 1];
- return this.dotify(Math.floor(value / 64516)) + this.dotify(value % 64516, true);
- }
- formatDecimal
- if (value < 16387063.9980315) dotify(value*254)
- log = log254(value)
- exponent = floor(log-2)
- dotify(exponent) + ⣿ + dotify(254^(log-exponent+1))
- dotify(value,pad) {pad skips the <254 step}
- "⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿" +
- "⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿" +
- "⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿" +
- "⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿";
- value = round(value)
- if (value < 254) return DOT_DIGITS[value + 1]; (one digit base254 number)
- if (value < 64516) return DOT_DIGITS[floor(value/254)+1] + DOT_DIGITS[value%254+1]; (two digit base254 number)
- return dotify(floor(value/64516)) + dotify(value%64516,true); (four digit base254 number)
- zalgo:
- constructor() {
- super("Zalgo");
- this._zalgoChars = ['\u030D', '\u0336', '\u0353', '\u033F', '\u0489', '\u0330', '\u031A', '\u0338', '\u035A', '\u0337'];
- this._heComes = ["H", "E" , " ", "C" , "O" , "M" , "E", "S"];
- }
- get isPainful() {
- return true;
- }
- formatInfinite() {
- return this._heComes
- .map(char => char + this._zalgoChars.randomElement())
- .join("");
- }
- formatUnder1000(value, places) {
- return this.heComes(value.toDecimal());
- }
- formatDecimal(value, places) {
- return this.heComes(value);
- }
- /**
- * @param {Decimal} value
- * @return {string}
- */
- heComes(value) {
- // Eternity seems to happen around e66666 antimatter, who would've thought? Scaled down to 1000.
- let scaled = value.clampMin(1).log10() / 66666 * 1000;
- let displayPart = scaled.toFixed(2);
- let zalgoPart = Math.floor(Math.abs(Math.pow(2, 30) * (scaled - displayPart)));
- let displayChars = Array.from(formatWithCommas(displayPart));
- let zalgoIndices = Array.from(zalgoPart.toString() + scaled.toFixed(0));
- for (let i = 0; i < zalgoIndices.length; i++) {
- let zalgoIndex = parseInt(zalgoIndices[i]);
- let displayIndex = 7 * i % displayChars.length;
- displayChars[displayIndex] += this._zalgoChars[zalgoIndex];
- }
- return displayChars.join("");
- }
- formatInfinite: randomly zalgo each character in HE COMES.
- heComes(value)
- scaled ~= log10(value)/66.666
- displayPart = scaled, to 2 places
- zalgoPart = floor(abs(1073741824 * scaled-displayPart))
- hard to parse will do later
- hex:
- formatInfinite() {
- return "FFFFFFFF";
- }
- formatDecimal(value) {
- // The `this.rawValue(x, 32, 8)` returns an integer between 0 and 2^32,
- // the .toString(16).toUpperCase().padStart(8, '0') formats it as
- // 8 hexadecimal digits.
- return this.rawValue(value, 32, 8).toString(16).toUpperCase().padStart(8, '0');
- }
- modifiedLogarithm(x) {
- // This function implements a tweak to the usual logarithm.
- // It has the same value at powers of 2 but is linear between
- // powers of 2 (so for example, f(3) = 1.5).
- const floorOfLog = Math.floor(Decimal.log2(x));
- const previousPowerOfTwo = Decimal.pow(2, floorOfLog);
- const fractionToNextPowerOfTwo = Decimal.div(x, previousPowerOfTwo).toNumber() - 1;
- return floorOfLog + fractionToNextPowerOfTwo;
- }
- rawValue(value, numberOfBits, extraPrecisionForRounding) {
- return this.getValueFromSigns(this.getSigns(value, numberOfBits, extraPrecisionForRounding), numberOfBits);
- }
- isFinite(x) {
- if (typeof x === "number") {
- return isFinite(x);
- }
- return isFinite(x.e) && isFinite(x.mantissa);
- }
- getSigns(value, numberOfBits, extraPrecisionForRounding) {
- // Extra precision is an arbitrary number, it only controls rounding of
- // the last digit, if it's 0 the last digit will almost always be odd
- // for non-dyadic x, if it's too high the code might be slower.
- // 8 (the value used) should be high enough for good results.
- let signs = [];
- for (let i = 0; i < numberOfBits + extraPrecisionForRounding; i++) {
- if (!this.isFinite(value)) {
- break;
- }
- if (Decimal.lt(value, 0)) {
- signs.push(this.signs.NEGATIVE);
- value = Decimal.times(value, -1);
- } else {
- signs.push(this.signs.POSITIVE);
- }
- value = this.modifiedLogarithm(value);
- }
- return signs;
- }
- getValueFromSigns(signs, numberOfBits) {
- // We need to use 0 as the initial value for result,
- // rather than, for example, 0.5. This is so that the sign list of 0
- // (passed in here as signs), which is just [Notation.hex.signs.POSITIVE],
- // becomes 1 / 2 + 0 = 0.5.
- // Another way of thinking about this:
- // The general way getSigns terminates, when it starts with a finite number,
- // is by taking the log of 0, which is -Infinity, which is the lowest number,
- // and which is also not finite, leading to the getSigns loop breaking. If you pass
- // -Infinity into getSigns, the result will be an empty list, so we want this function
- // to turn an empty list into 0. So the initial value has to be 0.
- // If you pass Infinity into getSigns, the result will also be an empty list,
- // but modifiedLogarithm never returns Infinity so the only way value can be Infinity
- // in getSigns is if Infinity was initially passed in, which should never happen.
- let result = 0;
- for (let i = signs.length - 1; i >= 0; i--) {
- if (signs[i] === this.signs.NEGATIVE) {
- result = 1 / 2 - result / 2;
- } else {
- result = 1 / 2 + result / 2;
- }
- }
- return Math.round(result * Math.pow(2, numberOfBits));
- }
- formatDecimal:
- signs = []
- for (i<40) {
- if (value isn't finite) break //log(0) = -infinity
- if(value<0) value *= -1; sign += negative //this is evil... it can only affect the last bit
- else signs += positive
- floor = floor(log2(x))
- value = floor+ x/(2^floor) -1
- }
- result = 0 //feels arbitrary but ok
- for (i:signs in reverse order) {
- if (i = negative) result = 0.5 - result/2
- else result = 0.5 + result/2
- }
- return round(result*2^32).toString(16).toUpperCase().padStart(8, '0');
- imperial:
- // The first column is the size in pL
- // the second is the name
- // the third is an index offset (going backwards) to the smallest unit that
- // is larger than "roudning error" for the unit in question (so, for a tun,
- // this is 7, which means that if we're within one pin of a tun, we'll say we're
- // "almost a tun" rather than "a pin short of a tun"
- this.VOLUME_UNITS = [
- [0, "pL", 0],
- [61611520, "minim", 0],
- [61611520*60, "dram", 1],
- [61611520*60*8, "ounce", 2],
- [61611520*60*8*4, "gill", 2],
- [61611520*60*8*4*2, "cup", 3],
- [61611520*60*8*4*2*2, "pint", 4],
- [61611520*60*8*4*2*2*2, "quart", 4],
- [61611520*60*8*4*2*2*2*4, "gallon", 4],
- [61611520*60*8*4*2*2*2*4*4.5, "pin", 3],
- [61611520*60*8*4*2*2*2*4*9, "firkin", 3],
- [61611520*60*8*4*2*2*2*4*18, "kilderkin", 4],
- [61611520*60*8*4*2*2*2*4*36, "barrel", 4],
- [61611520*60*8*4*2*2*2*4*54, "hogshead", 5],
- [61611520*60*8*4*2*2*2*4*72, "puncheon", 6],
- [61611520*60*8*4*2*2*2*4*108, "butt", 7],
- [61611520*60*8*4*2*2*2*4*216, "tun", 7],
- ];
- this.MINIMS = this.VOLUME_UNITS[1];
- this.VOLUME_ADJECTIVES = ["minute ", "tiny ", "petite ", "small ", "modest ", "medium ", "generous ",
- "large ", "great ", "huge ", "gigantic ", "colossal ", "vast ", "cosmic "];
- this.VOWELS = new Set("aeiouAEIOU");
- this.maxVolume = 10 * this.VOLUME_UNITS[this.VOLUME_UNITS.length-1][0];
- this.logMaxVolume = Math.log10(this.maxVolume);
- this.reduceRatio = Math.log10(this.maxVolume/this.MINIMS[0]);
- }
- get isPainful() {
- return true;
- }
- formatUnder1000(value) {
- return this.formatDecimal(new Decimal(value));
- }
- formatDecimal(value) {
- if (value.lt(this.maxVolume)) {
- return this.convertToVolume(value.toNumber(), this.VOLUME_ADJECTIVES[0]);
- }
- let logValue = value.log10() - this.logMaxVolume;
- let adjectiveIndex = 1;
- while (logValue > this.reduceRatio) {
- adjectiveIndex++;
- logValue /= this.reduceRatio;
- }
- return this.convertToVolume(Math.pow(10, logValue) * this.MINIMS[0], this.VOLUME_ADJECTIVES[adjectiveIndex]);
- }
- convertToVolume(x, adjective) {
- const volIdx = this.findVolumeUnit(x);
- if (volIdx === 0) return this.formatMetric(x);
- const smallStr = this.checkSmallUnits(adjective, x, volIdx);
- if (smallStr) return smallStr;
- const big = this.VOLUME_UNITS[volIdx]
- const numBig = Math.floor(x / big[0]);
- const remainder = x - numBig * big[0];
- // When we are within a specified rounding error, unit break:
- if (volIdx < this.VOLUME_UNITS.length - 1) {
- const ret = this.checkAlmost(adjective, x, 0, volIdx + 1);
- if (ret) return ret;
- }
- const nearMultiple = this.checkAlmost(adjective, remainder, numBig, volIdx);
- if (nearMultiple) return nearMultiple;
- // just over a multiple, in units that are too small:
- if (remainder < this.VOLUME_UNITS[volIdx - big[2]][0]) {
- return this.pluralOrArticle(numBig, adjective + big[1]);
- }
- // Search for the best unit to pair with:
- let numBest = Math.floor(remainder / this.VOLUME_UNITS[volIdx - 1][0]);
- let bestUnitIndex = volIdx - 1;
- let bestUnitError = remainder - numBest * this.VOLUME_UNITS[volIdx - 1][0];
- for (let thirdUnitIndex = volIdx - 2; thirdUnitIndex > 0 && thirdUnitIndex > volIdx - big[2]; --thirdUnitIndex) {
- const third = this.VOLUME_UNITS[thirdUnitIndex];
- const numThird = Math.floor(remainder / third[0]);
- // If we have a lot of the unit under consideration -- then stop. The exception is in
- // case of minims, where it may be normal to have a bunch of them; in that case, we print
- // drams if possible.
- if (numThird > 9 && (thirdUnitIndex != 1 || bestUnits)) break;
- // we are using floor, so we can compare error diretly, without abs
- const thirdUnitError = remainder - numThird * third[0];
- if (thirdUnitError < 0.99 * bestUnitError) {
- numBest = numThird;
- bestUnitIndex = thirdUnitIndex;
- bestUnitError = thirdUnitError;
- }
- }
- return this.bigAndSmall(adjective, numBig, big, numBest, this.VOLUME_UNITS[bestUnitIndex]);
- }
- /**
- * Format a small quantity that is less than the smallest minim; this is done without adjective
- * @param {number} x
- **/
- formatMetric(x) {
- // The jump from metric to minim is sudden. Small values (< 10) get more decimal places
- // because that's usually something like sacrifice multiplier
- if (x < 1000) {
- return ((x < 10 || x === Math.round(x)) ? x.toFixed(2) : x.toFixed(0)) + "pL"
- }
- if (x < 1e6) return (x / 1000).toPrecision(4) + "nL";
- return (x / 1e6).toPrecision(4) + "μL";
- }
- /**
- * handles cases involving everything up to ounces; in the case of ounces it may
- * return nothing, in which case, the normal code path should be used.
- * @param {string} adjective will be attached to unit
- * @param {number} x value to be formatted
- * @param {number} volIdx index into VOLUME_UNITS for x (largest unit smaller than x)
- * @returns {string?} the formatted output, if within the capabilities of this function
- */
- checkSmallUnits(adjective, x, volIdx) {
- const big = this.VOLUME_UNITS[volIdx];
- // Check for some minims short of a small unit break:
- if (volIdx <= 3 && x + 9.5 * this.MINIMS[0] > this.VOLUME_UNITS[volIdx + 1][0]) {
- return this.almostOrShortOf(x, adjective, 1, this.VOLUME_UNITS[volIdx + 1], this.MINIMS);
- }
- // minims to drams. This goes:
- // a minim
- // 1.5 minims <-- we don't do this with larger units
- // 10 minims ... 50 minims
- // 9 minims short of a dram
- // a minim short of a dram
- // almost a dram <-- handled above
- if (volIdx === 1) {
- const deciMinims = Math.round(x * 10 / big[0]);
- if (deciMinims === 10) return this.addArticle(adjective + big[1]);
- const places = deciMinims < 100 ? 1 : 0;
- return `${(deciMinims / 10).toFixed(places)} ${adjective}${big[1]}s`
- }
- if (volIdx === 2) {
- const numBig = Math.floor(x / big[0]);
- const remainder = x - numBig * big[0];
- if (remainder > 50.5 * this.MINIMS[0]) { // 9 minims short of a dram
- return this.almostOrShortOf(x, adjective, numBig + 1, big, this.MINIMS);
- }
- // for example, a dram and 15 minims
- const numSmall = Math.round(remainder / this.MINIMS[0]);
- return this.bigAndSmall(adjective, numBig, big, numSmall, this.MINIMS);
- }
- return null;
- }
- /**
- * Search for the largest unit smaller than x
- * @param {number} x
- * @returns {number} index into VOLUME_UNITS
- **/
- findVolumeUnit(x) {
- let low = 0;
- let high = this.VOLUME_UNITS.length;
- let guess;
- while (high - low > 1) {
- guess = Math.floor((low + high) / 2);
- if (this.VOLUME_UNITS[guess][0] > x) high = guess;
- else low = guess;
- }
- return low;
- }
- // Try to do "almost a big thing" or "a thing short of a big thing", based on the setting
- // we have for rounding error units; may return nothing if we are not actually near something
- checkAlmost(adjective, x, numBig, bigIndex) {
- const big = this.VOLUME_UNITS[bigIndex];
- if (x + this.VOLUME_UNITS[bigIndex - big[2]][0] >= big[0]) {
- return this.almost(adjective, numBig + 1, big);
- }
- const small = this.VOLUME_UNITS[bigIndex + 1 - big[2]];
- if (x + small[0] >= big[0]) {
- return this.shortOf(adjective, numBig + 1, big, 1, small);
- }
- return null;
- }
- bigAndSmall(adjective, numBig, big, numSmall, small) {
- const bigStr = this.pluralOrArticle(numBig, adjective + big[1]);
- return numSmall === 0 ? bigStr : bigStr + " and " + this.pluralOrArticle(numSmall, small[1]);
- }
- almost(adjective, numBig, big) {
- return "almost " + this.pluralOrArticle(numBig, adjective + big[1]);
- }
- almostOrShortOf(x, adjective, numBig, big, small) {
- const short = Math.round((numBig * big[0] - x) / small[0]);
- return short
- ? this.shortOf(adjective, numBig, big, short, small)
- : this.almost(adjective, numBig, big);
- }
- shortOf(adjective, numBig, big, numSmall, small) {
- return this.pluralOrArticle(numSmall, small[1]) + " short of " +
- this.pluralOrArticle(numBig, adjective + big[1]);
- }
- pluralOrArticle(num, str) {
- return num === 1 ? this.addArticle(str) : num + " " + str + "s";
- }
- addArticle(x) {
- return (this.VOWELS.has(x[0]) ? "an " : "a ") + x;
- }
- MAX = 10*tun = 8176489463808000
- REDUCE = log10(MAX/minim) ~= 7.12290495817
- formatDecimal:
- if (value < MAX) convertToVolume(value,minute)
- logValue = log10(value) - log10(MAX)
- while (logValue > REDUCE) logValue /= REDUCE; adjective++
- convertToVolume(10^logValue)*minim, adjectiveIndex)
- convertToVolume(x, adjective):
- volIdx = the highest adjective that is below x
- if (volIdx = 0) { //very small numbers below a minim
- if (x < 1000) {
- return ((x < 10 || x === Math.round(x)) ? x.toFixed(2) : x.toFixed(0)) + "pL" //tiny numbers have more precision because why not
- }
- if (x < 1e6) return (x / 1000).toPrecision(4) + "nL";
- return (x / 1e6).toPrecision(4) + "μL";
- }
- am tired will continue later
Add Comment
Please, Sign In to add comment