package math;

/*************************************************************************
 *  Compilation:  javac Euler.java
 *  Execution:    java Euler N
 *  
 *  Tests whether there are any four positive integers that satisfy
 *  a^4 + b^4 + c^4 = d^4. Euler conjectured that no such solutions
 *  exists, but his conjecture was later disproved.
 *
 *  Mathematical shortcut: can assume
 *     - a is a multiple of 8
 *     - b is a multiple of 40
 *     - d-1 is a multiple of 8
 *     - either c+d is a multiple of 1024 or d-c is a multiple of 1024
 *     - d is not a multiple of 5
 *
 *  % java -Xmx900MB -Xms900MB Euler 500000
 *  95800^4 + 414560^4 + 217519^4 = 422481^4
 *  109208^4 + 367120^4 + 259225^4 = 481433^4
 *  137992^4 + 167600^4 + 32639^4 = 246913^4
 *  138688^4 + 332520^4 + 330537^4 = 421673^4
 *  414560^4 + 95800^4 + 217519^4 = 422481^4
 * 
 *  This takes approximately 75 minutes and used 736MB of memory.
 *
 *  Known bugs:
 *  -----------
 *  -  Only the first solution actually is valid, the others
 *     satisfy a^4 + b^4 = d^4 - c^4 (mod 2^64). These numbers should
 *     be checked with extended precision arithmetic.
 *  -  It is possible, but extremely unlikely, that we miss a solution.
 *     This could happen if two values of d^4 - c^4 are equal (mod 2^64)
 *     AND d^4 - c^4 is equal to a^4 + b^4 (mod 2^64)  AND the first
 *     (d, c) pair does not form a valid solution with (a, b) AND
 *     the second (d, c) pair does. We could modify the hash search
 *     function to return all items with a given sum, but this seems
 *     like overkill.
 *
 *
 *************************************************************************/

public class Euler {
    static long[] four;       // table of fourth powers
    static Item[] st;         // symbol table - hash table

    static int hash(long sum) { return (int) (sum) >>> 7; }

   /***********************************************************************
    *  Insert item into ST.
    ***********************************************************************/
    static void insert(Item x) {
        int i = hash(x.sum);
        while (st[i] != null) {
            i++;
            if (i >= st.length) i-= st.length;
        }
        st[i] = x;
    }

   /***********************************************************************
    *  Search ST for given key.
    ***********************************************************************/
    static Item search(long sum) {
        int i = hash(sum);
        while (st[i] != null) {
            if (st[i].sum == sum) return st[i];
            i++;
            if (i >= st.length) i-= st.length;
        }
        return null;
    }


   /***********************************************************************
    *  Item to store in hash table.
    ***********************************************************************/
    private static class Item {
        int d, c;
        long sum;    // d^4 - c^4  (mod 2^64)
        
        Item(int d, int c, long sum) { this.d = d; this.c = c; this.sum = sum; }
    }


    public static void main(String[] args) { 
        int N = Integer.parseInt(args[0]);
        long a4, b4, c4, d4;
        st = new Item[33554432];   // 2^25

       /**************************************************************
        * Precompute i^4 (mod 2^64) by relying on fact that overflow
        * of long in Java is automatically done this way.
        **************************************************************/
        long[] four = new long[N+1];
        for (int i = 0; i <= N; i++) {
            four[i] = i;
            four[i] *= four[i];
            four[i] *= four[i];
        }
        System.out.println("Done precomputing table of fourth powers");

       /**************************************************************
        * Enumerate of all pairs (c, d) and put d^4 - c^4 (mod 2^64)
        * in a hash table along with c and d.
        **************************************************************/
        for (int d = 1; d < N; d += 8) {
            if (d % 5 == 0) continue;
            d4 = four[d];
            for (int c = d % 1024; c < d; c += 1024) {
                c4 = four[c];
                insert(new Item(d, c, d4 - c4));
            }
            for (int c = 1024 - d % 1024; c < d; c += 1024) {
                c4 = four[c];
                insert(new Item(d, c, d4 - c4));
            }
        }

        System.out.println("Done building hash table");

       /**************************************************************
        * Enumerate of all pairs (a, b) and search in a hash table
        * for a^4 + b^4 (mod 2^64). If match, perform full precision
        * check to see if a^4 + b^4 = d^4 - c^4
        **************************************************************/
        for (int a = 8; a < N; a += 8) {
            a4 = four[a];
            if (a % 10000 == 0) System.out.println(a);
            for (int b = 40; b < N; b += 40) {
                b4 = four[b];
                if (search(a4 + b4) != null) {
                    Item item = search(a4 + b4);
                    System.out.print(a + "^4 + " + b + "^4 + ");
                    System.out.println(item.c + "^4 = " + item.d + "^4");
                }
            }
        }

    }
}
