package Others;

import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * Class for finding the lowest base in which a given integer is a palindrome.
 * Includes auxiliary methods for converting between bases and reversing strings.
 * <p>
 * NOTE: There is potential for error, see note at line 63.
 *
 * @author RollandMichael
 * @version 2017.09.28
 */
public class 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();
            }
        }
        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.
     */
    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;
                }
            }
        }
        // If all else fails, n is always a palindrome in base n-1. ("11")
        return num - 1;
    }

    private static String reverse(String str) {
        String reverse = "";
        for (int i = str.length() - 1; i >= 0; i--) {
            reverse += str.charAt(i);
        }
        return reverse;
    }

    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;
        }

        // 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 = Integer.toString(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;
        }
        return output;
    }
}