import java.util.Random; /** This program illustrates how to work in the finite fields GF(2n), and can be used to find irreducible polynomials in GF(2n) for n less than 64.

The techniques used here are described in "Probabilistic Algorithms in Finite Fields" by Michael O. Rabin, SIAM Journal of Computing, May 1980. @author David Chase chase@naturalbridge.com */ public class TwoPolynomials { static long [][] primeFactorsOfTwoToTheNMinusOneCache = new long[64][]; static long[] primeFactorsOfTwoToTheNMinusOne(long x) { int ix = (int) x; if (primeFactorsOfTwoToTheNMinusOneCache[ix] == null) primeFactorsOfTwoToTheNMinusOneCache[ix] = PF2.factors2ttxm1(x); return primeFactorsOfTwoToTheNMinusOneCache[ix]; } /** Returns the number of set (1) bits in x. */ public static int populationCount(long x) { return populationCount((int) x) + populationCount((int) (x >>> 32)); } public static int populationCount(int x) { int x1 = x & 0x55555555; int x2 = (x & 0xaaaaaaaa) >>> 1; int x3 = x1 + x2; // Each pair of bits in x3 is 0-2 int x4 = x3 & 0x33333333; int x5 = (x3 & 0xcccccccc) >>> 2; int x6 = x4 + x5; // Each quad of bits in x6 is 0-4 int x7 = x6 & 0x0f0f0f0f; int x8 = (x6 & 0xf0f0f0f0) >>> 4; int x9 = x7 + x8; // Each octet of bits in x9 is 0-8; int x10 = x9 & 0x00ff00ff; int x11 = (x9 & 0xff00ff00) >>> 8; int x12 = x10 + x11; // Each half of x12 is 0-16; int x13 = x12 & 0x0000ffff; int x14 = x12 >>> 16; return x13 + x14; } /** countHighZeros(x) returns the number of contiguous zeros found at the high-order (unsigned) end of x.

Thus,

countHighZeros(-1) = 0; countHighZeros(0) = 32; countHighZeros(1) = 31; countHighZeros(65535) = 16;
*/ public static int countHighZeros(int x) { int count = 0; if ((x & 0xffff0000) == 0) { count += 16; x = x << 16; } if ((x & 0xff000000) == 0) { count += 8; x = x << 8; } if ((x & 0xf0000000) == 0) { count += 4; x = x << 4; } if ((x & 0xc0000000) == 0) { count += 2; x = x << 2; } if ((x & 0x80000000) == 0) { count += 1; x = x << 1; } return count + (((x & 0x80000000) == 0) ? 1 : 0); } public static int countHighZeros(long x) { int xl = (int) x; int xh = (int) (x >>> 32); if (xh == 0) return 32 + countHighZeros(xl); return countHighZeros(xh); } /** highBit(a) returns 0 if a is zero, otherwise it returns a rounded down to the largest power of two less than or equal to a. */ static long highBit(long a) { if (a == 0) return 0; return 1L << (63 - countHighZeros(a)); } static boolean isEven(long a) { return ((int) a & 1) == 0; } static boolean isOdd(long a) { return ((int) a & 1) == 1; } /** polyMulMod(long a, long b, long m) Multiply together two polynomials a and b, modulo a third polynomial m. A and b must have smaller degree than m does.

Here, the polynomials have coefficients in {0,1}, where addition is XOR and multiplication is AND. The set bits in a long integer correspond to the non-zero coefficients of the polynomial, where the coefficient N corresponds to the bit for 2n in the the integer. Thus, x3 + x + 1 has coefficients 3, 1, 0, and value 8 + 2 + 1 = 11. */ public static long polyMulMod(long a, long b, long m) { if (a > b) { long t = a; a = b; b = t; } assertSmallerDegree(b, m); long hm = highBit(m); if (a == 0) return 0; if (a == 1) return b; long r = 0; while (a != 0) { if (isOdd(a)) r = r ^ b; /* Add b to r */ b = b + b; /* Raise the degree of b */ if ((b & hm) != 0) b = b ^ m; /* Subtract out m if equal degree */ a = a >>> 1; } return r; } /** pToTheNMod(long p, long n, long m) returns the result of raising polynomial p to the n power, modulo polynomial m. P must have lower degree than m's degree.

Again, these are polynomials with coefficients in {0,1}, + = XOR, * = AND. */ public static long pToTheNMod(long p, long n, long m) { if (n == 0) return 1; if (n == 1) return p; assertSmallerDegree(p, m); return polyMulMod( isEven(n) ? 1 : p, pToTheNMod(polyMulMod(p, p, m), n / 2, m), m); } private static void assertSmallerDegree(long p, long m) { if (p >= m || (p & m) * 2 > m) throw new IllegalArgumentException( "p (0x" + Long.toHexString(p) + ") >= m(0x" + Long.toHexString(m) + ")"); } /** pToTheTwoToTheNMod(long p, int n, long m) returns the result of raising polynomial p to the 2n power, modulo polynomial m */ public static long pToTheTwoToTheNMod(long p, int n, long m) { long r = p; while (n-- > 0) r = polyMulMod(r, r, m); return r; } /** gcd(long p, long q) returns the gcd of the two polynomials p and q. */ public static long gcd(long p, long q) { long hp = highBit(p); long hq = highBit(q); // Without loss of generality, degree(p) <= degree(q) if (hp > hq) { long t = p; p = q; q = t; t = hp; hp = hq; hq = t; } // gcd(0,q) == q; if (p == 0) return q; // n = 1 + degree(q) - degree(p) int n = 0; while ((hp << n) < hq) n++; // q = q modulo p while (n >= 0) { if (((hp << n) & q) != 0) { q = q ^ (p << n); } n--; } // and recur. return gcd(q, p); } /** This is Lemma 1 in Rabin's article:

Let L1, ..., Lk be all the prime divisors of n and denote n/li = mi.
A polynomial g(x) in Zp[x] is irreducible in Zp[x] if and only if

  1. g(x) divides (x pn - x),
  2. For all i, 1 <= i <=k, gcd(g(x), xpmi - x) = 1.

For our purposes, recall that "p" is 2. Also recall that our representation of the polynomial "x" is 2, since x == x1 and 2 == 21. NOTE: I think I got this wrong in the transcription somehow. The prime factors need to be from 2 to the N, minus one. */ public static boolean isIrreducible(long gOfX) { int n = 63 - countHighZeros(gOfX); long tttnm1 = (1L << n) - 1; // Lemma 1, item 1, translated: // // if g(x) divides (x-to-the-2-to-the-n minus x), // that is equivalent to xtt2ttn modulo g(x) equals x. // Or, translated into the representations of the polynomials // we are using, it equals the number 2. long t1 = pToTheTwoToTheNMod(2, n, gOfX) ^ 2; if (t1 != 0) { return false; } // Item 2 translated: // // In this system, for polynomials m with degree larger // than 1, (p mod m) - x == (p - x) mod m. Therefore, // the calculation of the gcd operation and the calculation // of xtt2tt-m-sub-i minus x are interleaved to avoid // gigantic intermediate results. Rather than computing // xtt2ttmsi-x first, then feeding it to the gcd calculation, // this code calculates xtt2ttmsi modulo g(x) first, then // subtracts x, then proceeds with the gcd calculation. // for (int m = 1; m < n ; m++) { // long t = pToTheTwoToTheNMod(2, m, gOfX) ^ 2; // t = gcd(t, gOfX); // if (t != 1) // return false; // } long[] pf = primeFactorsOfTwoToTheNMinusOne(n); for (int i = 0; i < pf.length; i++) { long m = tttnm1 / pf[i]; long t = pToTheNMod(2, m, gOfX); if (t == 1) return false; } return true; } /** Treating p as a polynomial of x with {0,1} coefficients, this returns (in polynomial arithmetic) p*x modulo irred_poly.

This assumes that irred_poly has degree 32, and that the representation of irred_poly has an implicit bit 32 (since if it were explicit, the representation of irred_poly would require 33 bits). */ public static int step(int p, int irred_poly) { if (p < 0) { return (p + p) ^ irred_poly; } else { return p + p; } } /** This constructs a lookup table that can be used to accelerate calculation of p*x8 modulo irred_poly

This assumes that irred_poly has degree 32, and that the representation of irred_poly has an implicit bit 32 (since if it were explicit, the representation of irred_poly would require 33 bits).

This table can be used in byte-string hashing operations; to calculate the remainder of a given string of bytes modulo some poly, it is sufficient to use a table returned by this routine, and use the iteration:

      int remainder = 0;
      for (int i = 0; i < bytes.length; i++) {
          remainder =
            (table[remainder >>> 24] ^ (remainder << 8)) +
              ((int) bytes[i] & 0xFF); 
      }
      
This is not a spectacular hash function for short strings, but it apparently has interesting properties for manipulating long streams of bytes. If a hash function is desired, merely performing an additional four iterations:
      for (int i = 0; i < 4; i++) {
          remainder =
            (table[remainder >>> 24] ^ (remainder << 8));
      }
      
yields a very good one, though it is not the fastest possible. */ public static int[] makeLookupTable(int irred_poly) { int[] result = new int[256]; for (int i = 0; i < 256; i++) { int trial = i << 24; for (int j = 0; j < 8; j++) { trial = step(trial, irred_poly); } result[i] = trial; } return result; } public static void printLookupTable(int[] table) { System.out.println(" = {"); for (int i = 0; i < table.length; i++) { if (i == 255) { System.out.print("0x" + Integer.toHexString(table[i]) + "};"); } else { System.out.print("0x" + Integer.toHexString(table[i]) + ", "); } if (((i + 1) & 3) == 0) System.out.println(); } } // Sanity check static boolean test(long x) { System.err.print(Long.toHexString(x)); boolean b = isIrreducible(x); System.err.println((b ? " is irreducible" : " is reducible")); return b; } static int pcount = 0; public static long exercise(long l) { int pc = populationCount((int) l); if (pc == 16 && isIrreducible(l) // && 0 == divisor() ) { long i1 = Invert.invert(l & 0xffffffffL, 0x7fffffffL); long i2 = Invert.invert(l & 0xffffffffL, 0x80000000L); long i3 = Invert.invert(l & 0xffffffffL, 0x100000000L); int d0 = TwoPolynomials.divisor(l & 0xffffffffL); int d1 = TwoPolynomials.divisor(i1); int d2 = TwoPolynomials.divisor(i2); int d3 = TwoPolynomials.divisor(i3); int dcount = 0; if (d0 == 0) dcount++; if (d1 == 0) dcount++; if (d2 == 0) dcount++; if (d3 == 0) dcount++; if (dcount == 2) { System.err.println( "#define h" + (pcount++) + " 0x" + Long.toHexString(0xffffffffL & l)); // makeLookupTable((int)l); } return l; } return 0; } final static int h0 = 0x2099ebb3; final static int h1 = 0x28d7c663; final static int h2 = 0xbd52982d; final static int h3 = 0x153d88db; final static int h4 = 0xae88dd83; final static int h5 = 0xad657069; final static int h6 = 0x1c62cd6b; final static int h7 = 0x5c2225f7; final static int h8 = 0xe9525c8d; final static int h9 = 0x2aa9b2ab; final static int h10 = 0x90fa44ed; final static int h11 = 0x068b8fd9; final static int h12 = 0xa334536b; final static int h13 = 0x7de40e29; final static int h14 = 0x9394965b; final static int h15 = 0x71cc581f; final static int h16 = 0xa7429b27; final static int h17 = 0x9992627d; final static int h18 = 0x33e485cb; final static int h19 = 0xc448ccfd; final static int h20 = 0xb0d91f0b; final static int h21 = 0x73396433; final static int h22 = 0x46b0b747; final static int h23 = 0x20b4bee3; final static int h24 = 0xc0d562ed; final static int h25 = 0xcd2f28d1; final static int h26 = 0x46384f75; final static int h27 = 0xad88ae87; final static int h28 = 0xb23b8d43; // negative final static int h29 = 0xbca541d3; // negative final static int h30 = 0x05592ff1; final static int h31 = 0xed1a0ec9; final static int h32 = 0x6d1418ef; final static int h33 = 0xb4ce06b3; final static int h34 = 0x0fe4b171; final static int h35 = 0x89b6ccc9; final static int h36 = 0x8e4154df; final static int h37 = 0x04e0ef6b; final static int h38 = 0xd90ea917; final static int h39 = 0x5c0ead87; final static int h40 = 0x478a157d; final static int h41 = 0x1b4ad713; final static int h42 = 0xa256d569; final static int h43 = 0x5b94469b; final static int h44 = 0x52e90be5; final static int h45 = 0x7607db81; final static int h46 = 0xcb1a8753; final static int h47 = 0x1f62b945; // For convenient array access. final static int[] H = { h0, h1, h2, h3, h4, h5, h6, h7, h8, h9, h10, h11, h12, h13, h14, h15, h16, h17, h18, h19, h20, h21, h22, h23, h24, h25, h26, h27, h28, h29, h30, h31, h32, h33, h34, h35, h36, h37, h38, h39, h40, h41, h42, h43, h44, h45, h46, h47 }; public static int divisor(int x) { long l = (long) x & 0xffffffffL; return divisor(l); } public static int divisor(long l) { if (0 == (l & 1)) return 2; long s = (long) Math.sqrt((double) l); for (int i = 3; i <= (int) s; i += 2) { if (l % i == 0) return i; } return 0; } static public void checkHPrime() { for (int i = 0; i < H.length; i++) { int h = H[i]; int d = divisor(h); String s = "0x" + Long.toHexString((long) h & 0xffffffffL); if (d == 0) System.out.println(s + " is prime"); else System.out.println(s + " is divided by " + d); } } public static void main(String[] args) { // checkHPrime(); test(0x118000003L); // Known irred poly. test(0x101800003L); // Known reducible poly. test(0x19L); test(0x13L); test(0x11dL); test(0x1c3L); test(0x1100bL); test(0x100400007L); if (true) return; Random r = new Random(0x123456789L); int n_found = 0; // Find a few irreducible polynomial with 16 set bits in // their implicit-leading-term representation, // and print them, and finally print the table for the last // one. for (int j = 0; j < 25; j++) { long start = System.currentTimeMillis(); if (pcount > 50) break; for (int i = 0; i < 500000; i++) { long l = 0x100000001L | ((long) r.nextInt() & 0xffffffffL); if (pcount > 50) break; exercise(l); } long stop = System.currentTimeMillis(); try { Thread.sleep(1000); } catch (InterruptedException e) { } System.err.println( "Timing run " + j + " took " + (stop - start) + " milliseconds"); } } }