diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2018-10-14 20:15:17 -0400 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2018-10-14 20:15:17 -0400 |
| commit | 004bc5de7618bc44079e6cdd21a50d6814c76c50 (patch) | |
| tree | 3cbcc77bf04c7dae4c8ad3d745d181d4bfca77fb /src/main/java/bjc/inflexion/NumberUtils.java | |
| parent | b09885e13d8829ee59e10ec0a957f6209c3e4aeb (diff) | |
General update
Testing, plus some reorganization
Diffstat (limited to 'src/main/java/bjc/inflexion/NumberUtils.java')
| -rw-r--r-- | src/main/java/bjc/inflexion/NumberUtils.java | 570 |
1 files changed, 570 insertions, 0 deletions
diff --git a/src/main/java/bjc/inflexion/NumberUtils.java b/src/main/java/bjc/inflexion/NumberUtils.java new file mode 100644 index 0000000..3a0200d --- /dev/null +++ b/src/main/java/bjc/inflexion/NumberUtils.java @@ -0,0 +1,570 @@ +package bjc.inflexion; + +/** + * A variety of functions for doing useful stuff with numbers. + * + * @author EVE + * + */ +public class NumberUtils { + /* + * @TODO 2/12/18 Ben Culkin :RomanExpansion + * + * Use U+305 for large roman numerals, as well as excels 'concise' + * numerals (as implemented by roman()). + */ + + /** + * Convert a number into a roman numeral. + * + * @param number + * The number to convert. + * @param classic + * Whether to use classic roman numerals (use IIII + * instead of IV, and such). + * @return The number as a roman numeral. + */ + public static String toRoman(long number, boolean classic) { + StringBuilder work = new StringBuilder(); + + long currNumber = number; + + if (currNumber == 0) { return "N"; } + + if (currNumber < 0) { + currNumber *= -1; + + work.append("-"); + } + + if (currNumber >= 1000) { + int numM = (int) (currNumber / 1000); + currNumber = currNumber % 1000; + + for (int i = 0; i < numM; i++) { + work.append("M"); + } + } + + if (currNumber >= 900 && !classic) { + currNumber = currNumber % 900; + + work.append("CM"); + } + + if (currNumber >= 500) { + currNumber = currNumber % 500; + + work.append("D"); + } + + if (currNumber >= 400 && !classic) { + currNumber = currNumber % 400; + + work.append("CD"); + } + + if (currNumber >= 100) { + int numC = (int) (currNumber / 100); + currNumber = currNumber % 100; + + for (int i = 0; i < numC; i++) { + work.append("C"); + } + } + + if (currNumber >= 90 && !classic) { + currNumber = currNumber % 90; + + work.append("XC"); + } + + if (currNumber >= 50) { + currNumber = currNumber % 50; + + work.append("L"); + } + + if (currNumber >= 40 && !classic) { + currNumber = currNumber % 40; + + work.append("XL"); + } + + if (currNumber >= 10) { + int numX = (int) (currNumber / 10); + currNumber = currNumber % 10; + + for (int i = 0; i < numX; i++) { + work.append("X"); + } + } + + if (currNumber >= 9 && !classic) { + currNumber = currNumber % 9; + + work.append("IX"); + } + + if (currNumber >= 5) { + currNumber = currNumber % 5; + + work.append("V"); + } + + if (currNumber >= 4 && !classic) { + currNumber = currNumber % 4; + + work.append("IV"); + } + + if (currNumber >= 1) { + int numI = (int) (currNumber / 1); + currNumber = currNumber % 1; + + for (int i = 0; i < numI; i++) { + work.append("I"); + } + } + + return work.toString(); + } + + private static String[] summaryNums = new String[] { "no", "one", "a couple of", "a few", "several" }; + + private static String[] summaryNumsEnd = new String[] { "none", "one", "a couple", "a few", "several" }; + + private static int[] summaryMap = new int[] { + /* no */ + 0, + /* one */ + 1, + /* a couple of */ + 2, + /* a few */ + 3, 3, 3, + /* several */ + 4, 4, 4, 4 }; + + /** + * Summarize an integer. + * + * @param num + * The number to summarize. + * + * @param atEnd + * Whether or not the integer is at the end of a string. + * + * @return A string summarizing the integer. + */ + public static String summarizeNumber(final long num, final boolean atEnd) { + if (num >= 0 && num < 10) { + if (atEnd) return summaryNumsEnd[summaryMap[(int) num]]; + + return summaryNums[summaryMap[(int) num]]; + } + + return "many"; + } + + /** + * Convert a number into a cardinal number, up to a threshold. + * + * @param number + * The number to convert + * @param thresh + * The threshold to stop at. + * @return The number as a cardinal. + */ + public static String toCardinal(long number, long thresh) { + if (number < thresh) return toCardinal(number, null); + + return Long.toString(number); + } + + /** + * Convert a number into a cardinal number. + * + * @param number + * The number to convert + * @return The number as a cardinal. + */ + public static String toCardinal(long number) { + return toCardinal(number, null); + } + + private static String[] cardinals = new String[] { "zero", "one", "two", "three", "four", "five", "six", + "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", + "sixteen", "seventeen", "eighteen", "nineteen", "twenty", }; + + /** + * Convert a number into a cardinal number. + * + * @param number + * The number to convert to a cardinal. + * @param custom + * The customizations to use. + * @return The number as a cardinal. + */ + public static String toCardinal(long number, CardinalState custom) { + if (custom != null) { + String res = custom.handleCustom(number); + + if (res != null) return res; + } + + if (number < 0) return "negative " + toCardinal(number * -1, custom); + + if (number <= 20) return cardinals[(int) number]; + + if (number < 100) { + if (number % 10 == 0) { + switch ((int) number) { + case 30: + return "thirty"; + case 40: + return "forty"; + case 50: + return "fifty"; + case 60: + return "sixty"; + case 70: + return "seventy"; + case 80: + return "eighty"; + case 90: + return "ninety"; + default: + /* + * Shouldn't happen. + */ + assert (false); + } + } + + long numTens = number / 10; + long numOnes = number % 10; + + return toCardinal(numTens, custom) + "-" + toCardinal(numOnes, custom); + } + + if (number < 1000) { + long numHundreds = number / 100; + long rest = number % 100; + + return toCardinal(numHundreds, custom) + " hundred and " + toCardinal(rest, custom); + } + + long MILLION = (long) (Math.pow(10, 6)); + if (number < MILLION) { + long numThousands = number / 1000; + long rest = number % 1000; + + return toCardinal(numThousands, custom) + " thousand, " + toCardinal(rest, custom); + } + + long BILLION = (long) (Math.pow(10, 9)); + if (number < BILLION) { + long numMillions = number / MILLION; + long rest = number % MILLION; + + return toCardinal(numMillions, custom) + " million, " + toCardinal(rest, custom); + } + + long TRILLION = (long) (Math.pow(10, 12)); + if (number < TRILLION) { + long numBillions = number / BILLION; + long rest = number % BILLION; + + return toCardinal(numBillions, custom) + " billion, " + toCardinal(rest, custom); + } + + throw new IllegalArgumentException( + "Numbers greater than or equal to 1 trillion are not supported yet."); + } + + /** + * Convert a number into an ordinal, up to a certain value. + * + * @param number + * The number to convert to an ordinal. + * @param thresh + * The threshold value to stop converting at. + * @return The number as an ordinal. + */ + public static String toOrdinal(long number, long thresh) { + return toOrdinal(number, thresh, true); + } + + /** + * Convert a number into an ordinal, up to a certain value. + * + * @param number + * The number to convert to an ordinal. + * @param thresh + * The threshold value to stop converting at. + * @param longForm + * Whether or not to use long-form ordinals (zeroth, + * first, etc.) instead of (0th, 1st, etc.) + * @return The number as an ordinal. + */ + public static String toOrdinal(long number, long thresh, boolean longForm) { + if (number < thresh) return toOrdinal(number, longForm); + + return Long.toString(number); + } + + /** + * Convert a number into an ordinal. + * + * @param number + * The number to convert to an ordinal. + * @return The number as an ordinal. + */ + public static String toOrdinal(long number) { + return toOrdinal(number, true); + } + + /** + * Convert a number into an ordinal. + * + * @param number + * The number to convert to an ordinal. + * @param longForm + * Whether or not to use long-form ordinals (zeroth, + * first, etc.) instead of (0th, 1st, etc.) + * @return The number as an ordinal. + */ + public static String toOrdinal(long number, boolean longForm) { + if (number < 0) { return "minus " + toOrdinal(number, longForm); } + + if (longForm) { + if (number < 20) { + switch ((int) number) { + case 0: + return "zeroth"; + case 1: + return "first"; + case 2: + return "second"; + case 3: + return "third"; + case 4: + return "fourth"; + case 5: + return "fifth"; + case 6: + return "sixth"; + case 7: + return "seventh"; + case 8: + return "eighth"; + case 9: + return "ninth"; + case 10: + return "tenth"; + case 11: + return "eleventh"; + case 12: + return "twelfth"; + case 13: + return "thirteenth"; + case 14: + return "fourteenth"; + case 15: + return "fifteenth"; + case 16: + return "sixteenth"; + case 17: + return "seventeenth"; + case 18: + return "eighteenth"; + case 19: + return "nineteenth"; + default: + /* + * Shouldn't happen. + */ + assert (false); + } + } + + if (number < 100) { + if (number % 10 == 0) { + switch ((int) number) { + case 20: + return "twentieth"; + case 30: + return "thirtieth"; + case 40: + return "fortieth"; + case 50: + return "fiftieth"; + case 60: + return "sixtieth"; + case 70: + return "seventieth"; + case 80: + return "eightieth"; + case 90: + return "ninetieth"; + default: + throw new IllegalArgumentException( + String.format("Illegal number %d", number)); + } + } + + long numPostfix = number % 10; + return toCardinal(number - numPostfix) + "-" + toOrdinal(numPostfix, longForm); + } + } + + long procNum = number % 100; + long tens = procNum / 10; + long ones = procNum % 10; + + if (tens == 1) { return Long.toString(number) + "th"; } + + switch ((int) ones) { + case 1: + return Long.toString(number) + "st"; + case 2: + return Long.toString(number) + "nd"; + case 3: + return Long.toString(number) + "rd"; + default: + return Long.toString(number) + "th"; + } + } + + private static char[] radixChars = new char[62]; + static { + int idx = 0; + + for (char i = 0; i < 10; i++) { + radixChars[idx] = (char) ('0' + i); + + idx += 1; + } + + for (char i = 0; i < 26; i++) { + radixChars[idx] = (char) ('A' + i); + + idx += 1; + } + + for (char i = 0; i < 26; i++) { + radixChars[idx] = (char) ('a' + i); + + idx += 1; + } + } + + /** + * Convert a number into a commafied string. + * + * @param val + * The number to convert. + * @param mincols + * The minimum number of columns to use. + * @param padchar + * The padding char to use. + * @param commaInterval + * The interval to place commas at. + * @param commaChar + * The character to use as a comma + * @param signed + * Whether or not to always display a sign + * @param radix + * The radix to use + * @return The number as a commafied string. + */ + public static String toCommaString(long val, int mincols, char padchar, int commaInterval, char commaChar, + boolean signed, int radix) { + if (radix > radixChars.length) { throw new IllegalArgumentException(String.format( + "Radix %d is larger than largest supported radix %d", radix, radixChars.length)); } + + StringBuilder work = new StringBuilder(); + + boolean isNeg = false; + long currVal = val; + if (currVal < 0) { + isNeg = true; + currVal *= -1; + } + + if (currVal == 0) { + work.append(radixChars[0]); + } else { + int valCounter = 0; + + while (currVal != 0) { + valCounter += 1; + + int radDigit = (int) (currVal % radix); + work.append(radixChars[radDigit]); + currVal = currVal / radix; + + if (commaInterval != 0 && valCounter % commaInterval == 0 && currVal != 0) + work.append(commaChar); + } + } + + if (isNeg) + work.append("-"); + else if (signed) work.append("+"); + + work.reverse(); + + /* + * @TODO + * + * Should we have some way to specify how to pad? + * + * By this, I mean specify padding direction (left, right, + * balanced...) + */ + StringBuilder pad = new StringBuilder(); + + if (work.length() < mincols) { + @SuppressWarnings("unused") + int padCount = 0; + for (int i = work.length(); i < mincols; i++) { + // @NOTE 9/6/18 :CommaPad + // + // I have no idea if this is the intended + // behavior, or if something is wrong with the + // example case in the menu + // if (commaInterval != 0 && padCount != 0) { + // if (Character.isDigit(padchar) && padCount % commaInterval == 0) + // pad.append(commaChar); + // else + pad.append(padchar); + // } + + padCount++; + } + } + + return pad.toString() + work.toString(); + } + + /** + * Convert a number to a normal commafied string. + * + * @param val + * The value to convert. + * @param mincols + * The minimum number of columns. + * @param padchar + * The padding char to use. + * @param signed + * Whether or not to display the sign. + * @param radix + * The radix to use. + * @return The number as a normal commafied string. + */ + public static String toNormalString(long val, int mincols, char padchar, boolean signed, int radix) { + return toCommaString(val, mincols, padchar, 0, ',', signed, radix); + } +} |
