package com.numix.calculator; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.regex.Pattern; import org.javia.arity.SyntaxException; public class BaseModule { public static final char SELECTION_HANDLE = '\u2620'; public final String REGEX_NUMBER; public final String REGEX_NOT_NUMBER; Logic mLogic; private Mode mMode = Mode.DECIMAL; Map<Mode, List<Integer>> mBannedResources; BaseModule(Logic logic) { this.mLogic = logic; REGEX_NUMBER = "[A-F0-9" + Pattern.quote(mLogic.mDecimalPoint) + SELECTION_HANDLE + "]"; REGEX_NOT_NUMBER = "[^A-F0-9" + Pattern.quote(mLogic.mDecimalPoint) + SELECTION_HANDLE + "]"; mBannedResources = new HashMap<Mode, List<Integer>>(3); mBannedResources.put(Mode.DECIMAL, Arrays.asList(R.id.A, R.id.B, R.id.C, R.id.D, R.id.E, R.id.F)); mBannedResources.put(Mode.BINARY, Arrays.asList(R.id.A, R.id.B, R.id.C, R.id.D, R.id.E, R.id.F, R.id.digit2, R.id.digit3, R.id.digit4, R.id.digit5, R.id.digit6, R.id.digit7, R.id.digit8, R.id.digit9)); mBannedResources.put(Mode.HEXADECIMAL, new ArrayList<Integer>()); } public enum Mode { BINARY(0), DECIMAL(1), HEXADECIMAL(2); int quickSerializable; Mode(int num) { this.quickSerializable = num; } public int getQuickSerializable() { return quickSerializable; } } public Mode getMode() { return mMode; } public String setMode(Mode mode) { String text = updateTextToNewMode(mLogic.getText(), this.mMode, mode); this.mMode = mode; return text; } String updateTextToNewMode(final String originalText, final Mode mode1, final Mode mode2) { if(mode1.equals(mode2) || originalText.equals(mLogic.mErrorString) || originalText.isEmpty()) return originalText; String[] operations = originalText.split(REGEX_NUMBER); String[] numbers = originalText.split(REGEX_NOT_NUMBER); String[] translatedNumbers = new String[numbers.length]; for(int i = 0; i < numbers.length; i++) { if(!numbers[i].isEmpty()) { switch(mode1) { case BINARY: switch(mode2) { case BINARY: break; case DECIMAL: try { translatedNumbers[i] = newBase(numbers[i], 2, 10); } catch(NumberFormatException e) { return mLogic.mErrorString; } catch(SyntaxException e) { return mLogic.mErrorString; } break; case HEXADECIMAL: try { translatedNumbers[i] = newBase(numbers[i], 2, 16); } catch(NumberFormatException e) { return mLogic.mErrorString; } catch(SyntaxException e) { return mLogic.mErrorString; } break; } break; case DECIMAL: switch(mode2) { case BINARY: try { translatedNumbers[i] = newBase(numbers[i], 10, 2); } catch(NumberFormatException e) { return mLogic.mErrorString; } catch(SyntaxException e) { return mLogic.mErrorString; } break; case DECIMAL: break; case HEXADECIMAL: try { translatedNumbers[i] = newBase(numbers[i], 10, 16); } catch(NumberFormatException e) { return mLogic.mErrorString; } catch(SyntaxException e) { return mLogic.mErrorString; } break; } break; case HEXADECIMAL: switch(mode2) { case BINARY: try { translatedNumbers[i] = newBase(numbers[i], 16, 2); } catch(NumberFormatException e) { return mLogic.mErrorString; } catch(SyntaxException e) { return mLogic.mErrorString; } break; case DECIMAL: try { translatedNumbers[i] = newBase(numbers[i], 16, 10); } catch(NumberFormatException e) { e.printStackTrace(); return mLogic.mErrorString; } catch(SyntaxException e) { e.printStackTrace(); return mLogic.mErrorString; } break; case HEXADECIMAL: break; } break; } } } String text = ""; Object[] o = removeWhitespace(operations); Object[] n = removeWhitespace(translatedNumbers); if(originalText.substring(0, 1).matches(REGEX_NUMBER)) { for(int i = 0; i < o.length && i < n.length; i++) { text += n[i]; text += o[i]; } } else { for(int i = 0; i < o.length && i < n.length; i++) { text += o[i]; text += n[i]; } } if(o.length > n.length) { text += o[o.length - 1]; } else if(n.length > o.length) { text += n[n.length - 1]; } return text; } private Object[] removeWhitespace(String[] strings) { ArrayList<String> formatted = new ArrayList<String>(strings.length); for(String s : strings) { if(s != null && !s.isEmpty()) formatted.add(s); } return formatted.toArray(); } private final static int PRECISION = 8; private String newBase(String originalNumber, int originalBase, int base) throws SyntaxException { String[] split = originalNumber.split(Pattern.quote(mLogic.mDecimalPoint)); if(split.length == 0) { split = new String[1]; split[0] = "0"; } if(split[0].isEmpty()) { split[0] = "0"; } if(originalBase != 10) { split[0] = Long.toString(Long.parseLong(split[0], originalBase)); } String wholeNumber = ""; switch(base) { case 2: wholeNumber = Long.toBinaryString(Long.parseLong(split[0])); break; case 10: wholeNumber = split[0]; break; case 16: wholeNumber = Long.toHexString(Long.parseLong(split[0])); break; } if(split.length == 1) return wholeNumber.toUpperCase(Locale.US); // Catch overflow (it's a decimal, it can be (slightly) rounded if(split[1].length() > 13) { split[1] = split[1].substring(0, 13); } double decimal = 0; if(originalBase != 10) { String decimalFraction = Long.toString(Long.parseLong(split[1], originalBase)) + "/" + originalBase + "^" + split[1].length(); decimal = mLogic.mSymbols.eval(decimalFraction); } else { decimal = Double.parseDouble("0." + split[1]); } if(decimal == 0) return wholeNumber.toUpperCase(Locale.US); String decimalNumber = ""; for(int i = 0, id = 0; decimal != 0 && i <= PRECISION; i++) { decimal *= base; id = (int) Math.floor(decimal); decimal -= id; decimalNumber += Integer.toHexString(id); } return (wholeNumber + mLogic.mDecimalPoint + decimalNumber).toUpperCase(Locale.US); } public String groupSentence(String originalText, int selectionHandle) { if(originalText.equals(mLogic.mErrorString) || originalText.isEmpty()) return originalText; originalText = originalText.substring(0, selectionHandle) + SELECTION_HANDLE + originalText.substring(selectionHandle); String[] operations = originalText.split(REGEX_NUMBER); String[] numbers = originalText.split(REGEX_NOT_NUMBER); String[] translatedNumbers = new String[numbers.length]; for(int i = 0; i < numbers.length; i++) { if(!numbers[i].isEmpty()) { translatedNumbers[i] = groupDigits(numbers[i], mMode); } } String text = ""; Object[] o = removeWhitespace(operations); Object[] n = removeWhitespace(translatedNumbers); if(originalText.substring(0, 1).matches(REGEX_NUMBER)) { for(int i = 0; i < o.length && i < n.length; i++) { text += n[i]; text += o[i]; } } else { for(int i = 0; i < o.length && i < n.length; i++) { text += o[i]; text += n[i]; } } if(o.length > n.length) { text += o[o.length - 1]; } else if(n.length > o.length) { text += n[n.length - 1]; } return text; } public String groupDigits(String number, Mode mode) { return groupDigits(number, mode, -1); } public String groupDigits(String number, Mode mode, int selectionHandle) { String sign = ""; if(number.startsWith(String.valueOf(Logic.MINUS)) || number.startsWith("-")) { sign = String.valueOf(Logic.MINUS); number = number.substring(1); } String wholeNumber = number; String remainder = ""; // We only group the whole number if(number.contains(mLogic.mDecimalPoint)) { if(!number.startsWith(mLogic.mDecimalPoint)) { String[] temp = number.split(Pattern.quote(mLogic.mDecimalPoint)); wholeNumber = temp[0]; remainder = mLogic.mDecimalPoint + ((temp.length == 1) ? "" : temp[1]); } else { wholeNumber = ""; remainder = number; } } String modifiedNumber = wholeNumber; switch(mode) { case DECIMAL: modifiedNumber = group(wholeNumber, mLogic.mDecSeparatorDistance, mLogic.mDecSeparator); break; case BINARY: modifiedNumber = group(wholeNumber, mLogic.mBinSeparatorDistance, mLogic.mBinSeparator); break; case HEXADECIMAL: modifiedNumber = group(wholeNumber, mLogic.mHexSeparatorDistance, mLogic.mHexSeparator); break; } return sign + modifiedNumber + remainder; } private String group(String wholeNumber, int spacing, String separator) { String modifiedNumber = ""; int offset = 0; for(int i = 1; i <= wholeNumber.length(); i++) { char charFromEnd = wholeNumber.charAt(wholeNumber.length() - i); modifiedNumber = charFromEnd + modifiedNumber; if(charFromEnd == SELECTION_HANDLE) { offset++; if(i == wholeNumber.length()) { // Remove separator if we accidentally caused an extra one if(modifiedNumber.startsWith(SELECTION_HANDLE + separator)) { modifiedNumber = SELECTION_HANDLE + modifiedNumber.substring(2); } } } else { if((i - offset) % spacing == 0 && i != wholeNumber.length() && (i - offset) != 0) { modifiedNumber = separator + modifiedNumber; } } } return modifiedNumber; } }