diff --git a/src/main/java/com/thealgorithms/others/LowestBasePalindrome.java b/src/main/java/com/thealgorithms/others/LowestBasePalindrome.java index 3d50b4840..addf82554 100644 --- a/src/main/java/com/thealgorithms/others/LowestBasePalindrome.java +++ b/src/main/java/com/thealgorithms/others/LowestBasePalindrome.java @@ -1,153 +1,93 @@ package com.thealgorithms.others; -import java.util.InputMismatchException; -import java.util.Scanner; +import java.util.ArrayList; /** - * Class for finding the lowest base in which a given integer is a palindrome. - * Includes auxiliary methods for converting between bases and reversing - * strings. - * - *

- * NOTE: There is potential for error, see note at line 63. - * - * @author RollandMichael - * @version 2017.09.28 + * @brief Class for finding the lowest base in which a given integer is a palindrome. + cf. https://oeis.org/A016026 */ -public class LowestBasePalindrome { +final public class LowestBasePalindrome { + private LowestBasePalindrome() { + } - public static void main(String[] args) { - Scanner in = new Scanner(System.in); - int n = 0; - while (true) { - try { - System.out.print("Enter number: "); - n = in.nextInt(); - break; - } catch (InputMismatchException e) { - System.out.println("Invalid input!"); - in.next(); - } + private static void checkBase(int base) { + if (base <= 1) { + throw new IllegalArgumentException("base must be greater than 1."); + } + } + + private static void checkNumber(int number) { + if (number < 0) { + throw new IllegalArgumentException("number must be nonnegative."); } - System.out.println( - n + " is a palindrome in base " + lowestBasePalindrome(n) - ); - System.out.println( - base2base(Integer.toString(n), 10, lowestBasePalindrome(n)) - ); - in.close(); } /** - * Given a number in base 10, returns the lowest base in which the number is - * represented by a palindrome (read the same left-to-right and - * right-to-left). - * - * @param num A number in base 10. - * @return The lowest base in which num is a palindrome. + * @brief computes the representation of the input number in given base + * @param number the input number + * @param base the given base + * @exception IllegalArgumentException number is negative or base is less than 2 + * @return the list containing the digits of the input number in the given base, the most significant digit is at the end of the array */ - public static int lowestBasePalindrome(int num) { - int base, num2 = num; - int digit; - char digitC; - boolean foundBase = false; - String newNum = ""; - String digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - - while (!foundBase) { - // Try from bases 2 to num-1 - for (base = 2; base < num2; base++) { - newNum = ""; - while (num > 0) { - // Obtain the first digit of n in the current base, - // which is equivalent to the integer remainder of (n/base). - // The next digit is obtained by dividing n by the base and - // continuing the process of getting the remainder. This is done - // until n is <=0 and the number in the new base is obtained. - digit = (num % base); - num /= base; - // If the digit isn't in the set of [0-9][A-Z] (beyond base 36), its character - // form is just its value in ASCII. - - // NOTE: This may cause problems, as the capital letters are ASCII values - // 65-90. It may cause false positives when one digit is, for instance 10 and assigned - // 'A' from the character array and the other is 65 and also assigned 'A'. - // Regardless, the character is added to the representation of n - // in the current base. - if (digit >= digits.length()) { - digitC = (char) (digit); - newNum += digitC; - continue; - } - newNum += digits.charAt(digit); - } - // Num is assigned back its original value for the next iteration. - num = num2; - // Auxiliary method reverses the number. - String reverse = reverse(newNum); - // If the number is read the same as its reverse, then it is a palindrome. - // The current base is returned. - if (reverse.equals(newNum)) { - foundBase = true; - return base; - } - } + public static ArrayList computeDigitsInBase(int number, int base) { + checkNumber(number); + checkBase(base); + var result = new ArrayList(); + while (number > 0) { + result.add(number % base); + number /= base; } - // If all else fails, n is always a palindrome in base n-1. ("11") - return num - 1; + return result; } - private static String reverse(String str) { - String reverse = ""; - for (int i = str.length() - 1; i >= 0; i--) { - reverse += str.charAt(i); + /** + * @brief checks if the input array is a palindrome + * @brief list the input array + * @return true, if the input array is a palindrome, false otherwise + */ + public static boolean isPalindromic(ArrayList list) { + for (int pos = 0; pos < list.size()/2; ++pos) { + if(list.get(pos) != list.get(list.size()-1-pos)) { + return false; + } } - return reverse; + return true; } - private static String base2base(String n, int b1, int b2) { - // Declare variables: decimal value of n, - // character of base b1, character of base b2, - // and the string that will be returned. - int decimalValue = 0, charB2; - char charB1; - String output = ""; - // Go through every character of n - for (int i = 0; i < n.length(); i++) { - // store the character in charB1 - charB1 = n.charAt(i); - // if it is a non-number, convert it to a decimal value >9 and store it in charB2 - if (charB1 >= 'A' && charB1 <= 'Z') { - charB2 = 10 + (charB1 - 'A'); - } // Else, store the integer value in charB2 - else { - charB2 = charB1 - '0'; - } - // Convert the digit to decimal and add it to the - // decimalValue of n - decimalValue = decimalValue * b1 + charB2; + /** + * @brief checks if representation of the input number in given base is a palindrome + * @param number the input number + * @param base the given base + * @exception IllegalArgumentException number is negative or base is less than 2 + * @return true, if the input number represented in the given base is a palindrome, false otherwise + */ + public static boolean isPalindromicInBase(int number, int base) { + checkNumber(number); + checkBase(base); + + if (number <= 1) { + return true; } - // Converting the decimal value to base b2: - // A number is converted from decimal to another base - // by continuously dividing by the base and recording - // the remainder until the quotient is zero. The number in the - // new base is the remainders, with the last remainder - // being the left-most digit. - // While the quotient is NOT zero: - while (decimalValue != 0) { - // If the remainder is a digit < 10, simply add it to - // the left side of the new number. - if (decimalValue % b2 < 10) { - output = decimalValue % b2 + output; - } // If the remainder is >= 10, add a character with the - // corresponding value to the new number. (A = 10, B = 11, C = 12, ...) - else { - output = (char) ((decimalValue % b2) + 55) + output; - } - // Divide by the new base again - decimalValue /= b2; + if (number % base == 0) { + // the last digit of number written in base is 0 + return false; } - return output; + + return isPalindromic(computeDigitsInBase(number, base)); + } + + /** + * @brief finds the smallest base for which the representation of the input number is a palindrome + * @param number the input number + * @exception IllegalArgumentException number is negative + * @return the smallest base for which the representation of the input number is a palindrome + */ + public static int lowestBasePalindrome(int number) { + int base = 2; + while(!isPalindromicInBase(number, base)) { + ++base; + } + return base; } } diff --git a/src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java b/src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java new file mode 100644 index 000000000..3124d7b02 --- /dev/null +++ b/src/test/java/com/thealgorithms/others/LowestBasePalindromeTest.java @@ -0,0 +1,87 @@ +package com.thealgorithms.others; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +public class LowestBasePalindromeTest { + @Test + public void testIsPalindromicPositive() { + assertTrue(LowestBasePalindrome.isPalindromic(new ArrayList())); + assertTrue(LowestBasePalindrome.isPalindromic(new ArrayList(Arrays.asList(1)))); + assertTrue(LowestBasePalindrome.isPalindromic(new ArrayList(Arrays.asList(1, 1)))); + assertTrue(LowestBasePalindrome.isPalindromic(new ArrayList(Arrays.asList(1, 2, 1)))); + assertTrue(LowestBasePalindrome.isPalindromic(new ArrayList(Arrays.asList(1, 2, 2, 1)))); + } + + @Test + public void testIsPalindromicNegative() { + assertFalse(LowestBasePalindrome.isPalindromic(new ArrayList(Arrays.asList(1, 2)))); + assertFalse(LowestBasePalindrome.isPalindromic(new ArrayList(Arrays.asList(1, 2, 1, 1)))); + } + + @Test + public void testIsPalindromicInBasePositive() { + assertTrue(LowestBasePalindrome.isPalindromicInBase(101, 10)); + assertTrue(LowestBasePalindrome.isPalindromicInBase(1, 190)); + assertTrue(LowestBasePalindrome.isPalindromicInBase(0, 11)); + assertTrue(LowestBasePalindrome.isPalindromicInBase(10101, 10)); + assertTrue(LowestBasePalindrome.isPalindromicInBase(23, 22)); + } + + @Test + public void testIsPalindromicInBaseNegative() { + assertFalse(LowestBasePalindrome.isPalindromicInBase(1010, 10)); + assertFalse(LowestBasePalindrome.isPalindromicInBase(123, 10)); + } + + @Test + public void testIsPalindromicInBaseThrowsExceptionForNegativeNumbers() { + assertThrows( + IllegalArgumentException.class, + () -> LowestBasePalindrome.isPalindromicInBase(-1, 5) + ); + } + + @Test + public void testIsPalindromicInBaseThrowsExceptionForWrongBases() { + assertThrows( + IllegalArgumentException.class, + () -> LowestBasePalindrome.isPalindromicInBase(10, 1) + ); + } + + @Test + public void testLowestBasePalindrome() { + HashMap testCases = new HashMap<>(); + testCases.put(0, 2); + testCases.put(1, 2); + testCases.put(2, 3); + testCases.put(3, 2); + testCases.put(10, 3); + testCases.put(11, 10); + testCases.put(15, 2); + testCases.put(39, 12); + testCases.put(44, 10); + testCases.put(58, 28); + testCases.put(69, 22); + testCases.put(79, 78); + testCases.put(87, 28); + testCases.put(90, 14); + testCases.put(5591, 37); + testCases.put(5895, 130); + testCases.put(9950, 198); + testCases.put(9974, 4986); + + for (final var tc : testCases.entrySet()) { + assertEquals(LowestBasePalindrome.lowestBasePalindrome(tc.getKey()), tc.getValue()); + } + } +}