diff options
| author | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2017-10-08 22:39:59 -0300 |
|---|---|---|
| committer | Benjamin J. Culkin <bjculkin@mix.wvu.edu> | 2017-10-08 22:39:59 -0300 |
| commit | c82e3b3b2de0633317ec8fc85925e91422820597 (patch) | |
| tree | 96567416ce23c5ce85601f9cedc3a94bb1c55cba /base/src/main/java/bjc/utils/ioutils/NumberUtils.java | |
| parent | b3ac1c8690c3e14c879913e5dcc03a5f5e14876e (diff) | |
Start splitting into maven modules
Diffstat (limited to 'base/src/main/java/bjc/utils/ioutils/NumberUtils.java')
| -rw-r--r-- | base/src/main/java/bjc/utils/ioutils/NumberUtils.java | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/base/src/main/java/bjc/utils/ioutils/NumberUtils.java b/base/src/main/java/bjc/utils/ioutils/NumberUtils.java new file mode 100644 index 0000000..1b754e2 --- /dev/null +++ b/base/src/main/java/bjc/utils/ioutils/NumberUtils.java @@ -0,0 +1,405 @@ +package bjc.utils.ioutils; + +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.LongPredicate; + +import static java.util.Map.Entry; + +public class NumberUtils { + /* + * @TODO Use U+305 for large roman numerals, as well as excels 'concise' + * numerals (as implemented by roman()). + */ + 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(); + } + + 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", + }; + + public static class CardinalState { + public final Map<Long, String> customNumbers; + public final Map<LongPredicate, BiFunction<Long, CardinalState, String>> customScales; + + public CardinalState(Map<Long, String> customNumbers, Map<LongPredicate, BiFunction<Long, CardinalState, String>> customScales) { + this.customNumbers = customNumbers; + this.customScales = customScales; + } + + public String handleCustom(long number) { + if(customNumbers.containsKey(number)) { + return customNumbers.get(number); + } + + for(Entry<LongPredicate, BiFunction<Long, CardinalState, String>> ent : customScales.entrySet()) { + if(ent.getKey().test(number)) { + return ent.getValue().apply(number, this); + } + } + + return null; + } + } + + 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 = (long)(number / 10); + long numOnes = number % 10; + + return toCardinal(numTens, custom) + "-" + toCardinal(numOnes, custom); + } + + if(number < 1000) { + long numHundreds = (long)(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 = (long)(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 = (long)(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 = (long)(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."); + } + + public static String toOrdinal(long number) { + if(number < 0) { + return "minus " + toOrdinal(number); + } + + 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"; + } + } + + long numPostfix = number % 10; + return toCardinal(number - numPostfix) + "-" + toOrdinal(numPostfix); + } + + long procNum = number % 100; + long tens = (long)(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; + } + } + + 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 = (long)(currVal / radix); + + if(commaInterval != 0 && valCounter % commaInterval == 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? */ + if(work.length() < mincols) { + for(int i = work.length(); i < mincols; i++) { + work.append(padchar); + } + } + + return work.toString(); + } + + public static String toNormalString(long val, int mincols, char padchar, boolean signed, int radix) { + return toCommaString(val, mincols, padchar, 0, ',', signed, radix); + } +} |
