//--------------------------------------------------------------------------- // Copyright © 2009-2009 Eamonn Duffy. All Rights Reserved. //--------------------------------------------------------------------------- // // $RCSfile: $ // // $Revision: $ // // Created: Eamonn A. Duffy, 11-Jan-2009 (C#). // Eamonn A. Duffy, 10-Nov-2009 (Java). // // Purpose: Special Base Conversion routines. // // TODO: Why does the NetBeans IDE highlight String ==/!= String comparisons? // TODO: Find out why BigInteger == against .ZERO does not work on all versions of Java. // //--------------------------------------------------------------------------- package EamonnDuffy.Converter; import java.math.BigInteger; import javax.xml.ws.Holder; public class Special { public Special() { // // TODO: Add constructor logic here // } // Return the specified Input string in reversed form. public static String Reverse(String Input) { StringBuilder Working = new StringBuilder(); for (int Index = (Input.length() - 1); Index >= 0; Index--) Working.append(Input.charAt(Index)); return Working.toString(); } // Get the value of the specific Input character, using the Special Converter Base rules. private static boolean GetDigit(char Input, Holder Number) { boolean bValid = false; Number.value = 0; try { if ((Input >= '0') && (Input <= '9')) { Number.value = (Input - '0'); bValid = true; } else if ((Input >= 'A') && (Input <= 'Z')) { Number.value = ((Input - 'A') + 10); bValid = true; } else if ((Input >= 'a') && (Input <= 'z')) { Number.value = ((Input - 'a') + 36); bValid = true; } else bValid = false; } catch (Exception Exception) { System.err.printf(String.format("GetDigit() Exception = %s\n", Exception.getMessage())); bValid = false; } return bValid; } // Return a string representing the specified Number, using the Special Converter Base rules. private static String GetString(BigInteger Number, int Base) { String Result = ""; try { StringBuilder WorkingResult = new StringBuilder(); BigInteger FastBase = BigInteger.valueOf(Base); BigInteger WorkingNumber = Number; int Digit = 0; boolean bFinished = false; BigInteger[] DivMod = null; while (! bFinished) { DivMod = WorkingNumber.divideAndRemainder(FastBase); Digit = DivMod[1].intValue(); // Modulus result. if ((Digit >= 0) && (Digit <= 9)) WorkingResult.append((char)('0' + Digit)); else if ((Digit >= 10) && (Digit <= 35)) WorkingResult.append((char)('A' + (Digit - 10))); else if ((Digit >= 36) && (Digit <= 61)) WorkingResult.append((char)('a' + (Digit - 36))); else WorkingResult.append('*'); WorkingNumber = DivMod[0]; // Division result. if (WorkingNumber.compareTo(BigInteger.ZERO) == 0) bFinished = true; } Result = Reverse(WorkingResult.toString()); } catch (Exception Exception) { System.err.printf(String.format("GetString() Exception = %s\n", Exception.getMessage())); } return Result; } // Return a Number for the specified Input string, using the Special Converter Base rules. // NOTE: This method DOES NOT limit the Input string to being of characters within the specified Base. This has been done on purpose. private static boolean GetNumber(String Input, int Base, Holder Number) { boolean bValid = false; Number.value = BigInteger.ZERO; try { if (Input.length() < 1) bValid = false; else { BigInteger FastBase = BigInteger.valueOf(Base); BigInteger Working = BigInteger.ZERO; BigInteger NextWorking = BigInteger.ZERO; BigInteger Multiplier = BigInteger.ONE; BigInteger NextMultiplier = BigInteger.ONE; Holder DigitValue = new Holder(0); bValid = true; // Keep going until we encounter a problem for (int Index = (Input.length() - 1); bValid && (Index >= 0); Index--) { if (NextMultiplier.compareTo(Multiplier) < 0) // Wrap-around. bValid = false; else { Multiplier = NextMultiplier; if (! GetDigit(Input.charAt(Index), DigitValue)) bValid = false; else { BigInteger Interim = BigInteger.valueOf(DigitValue.value); Interim = Interim.multiply(Multiplier); NextWorking = Working.add(Interim); if (NextWorking.compareTo(Working) < 0) // Wrap-around. bValid = false; else { Working = NextWorking; NextMultiplier = Multiplier.multiply(FastBase); } } } } if (bValid) Number.value = Working; } } catch (Exception Exception) { System.err.printf("GetNumber() Exception = %s\n", Exception.getMessage()); bValid = false; } return bValid; } // Return a string in the specified ToBase from the specified string in the FromBase, // using the Special Converter Base rules. public static String FromBaseToBase(String Input, int FromBase, int ToBase) { String Result = ""; try { if ((FromBase >= 2) && (ToBase >= 2)) { Holder Number = new Holder(BigInteger.ZERO); if (GetNumber(Input, FromBase, Number)) { Result = GetString(Number.value, ToBase); } } } catch (Exception Exception) { } return Result; } // Return a string representing the Basic Sigma for the specified Input string. public static String CalculateBasicSigma(String Input) { String Result = ""; String Working = Input; boolean bContinue = true; while (bContinue) { if (Working.length() < 1) // NOTE: *NOT* <= 1 because must calculate value even if len == 1. { bContinue = false; } else { long Sum = 0; Holder ValueAtIndex = new Holder(0); for (int Index = 0; (Index < Working.length()) && bContinue; Index++) { if (! GetDigit(Working.charAt(Index), ValueAtIndex)) bContinue = false; else { Sum += ValueAtIndex.value; } } if (bContinue) { Working = Long.toString(Sum); if (Working.length() == 1) { bContinue = false; Result = Working; } } } } return Result; } // Unit Test Attributes. private int m_NumTestsPassed = 0; private int m_NumTestsFailed = 0; // Unit Test helper: Assert that the FromString and ExpectedToString items match, using the appropriate Bases. private void Assert(String FromString, int FromBase, String ExpectedToString, int ToBase) { String ToString = FromBaseToBase(FromString, FromBase, ToBase); if (ToString.compareTo(ExpectedToString) != 0) { m_NumTestsFailed++; System.err.printf("From = [\"%s\", %d] Does Not Match Expected To = [\"%s\", %d] : Actual To = \"%s\"\n", FromString, FromBase, ExpectedToString, ToBase, ToString); } else m_NumTestsPassed++; } // Unit Test helper: Assert that the Basic Sigma for the Input matches the specified ExpectedSigma. private void Assert(String Input, String ExpectedSigma) { String Sigma = CalculateBasicSigma(Input); if (Sigma.compareTo(ExpectedSigma) != 0) { m_NumTestsFailed++; System.err.printf("Input = \"%s\", Sigma Does Not Match Expected = \"%s\" : Actual Sigma = \"%s\"\n", Input, ExpectedSigma, Sigma); } else m_NumTestsPassed++; } // Run some very basic Unit Tests: Instance version. private void RunInstanceUnitTests() { System.out.printf("Special Unit Tests: Begin.\n"); m_NumTestsPassed = 0; m_NumTestsFailed = 0; // TODO: Run lots more tests. Assert("", 10, "", 16); Assert("10", 10, "A", 16); Assert("", 10, "", 8); Assert("10", 10, "12", 8); Assert("", 16, "", 10); Assert("1F", 16, "31", 10); Assert("", 16, "", 8); Assert("1F", 16, "37", 8); Assert("", 8, "", 10); Assert("12", 8, "10", 10); Assert("", 8, "", 16); Assert("12", 8, "A", 16); Assert("", ""); Assert(" ", ""); Assert("H", "8"); Assert("0", "0"); Assert("1", "1"); Assert("123", "6"); Assert("9876", "3"); Assert("6789", "3"); Assert("336", "3"); Assert("EAD", "1"); Assert("ABCDEF", "3"); Assert("0123456789ABCDEF", "3"); System.out.printf("Special Unit Tests: End.\n"); System.out.printf(" Num Tests Passed = %d.\n", m_NumTestsPassed); System.out.printf(" Num Tests Failed = %d.\n", m_NumTestsFailed); } // Run some very basic Unit Tests: Metaclass version. public static void RunUnitTests() { Special Instance = new Special(); Instance.RunInstanceUnitTests(); } } //--------------------------------------------------------------------------- // End Of $RCSfile: $ //---------------------------------------------------------------------------