public int getPartition(K key, V value, int numReduceTasks) {
    int h = SEED ^ key.hashCode();
    h ^= (h >>> 20) ^ (h >>> 12);
    h = h ^ (h >>> 7) ^ (h >>> 4);

    return (h & Integer.MAX_VALUE) % numReduceTasks;
  }
@Test
  public void testPatterns() {
    int results[] = new int[PARTITIONS];
    RehashPartitioner <IntWritable, NullWritable> p = new RehashPartitioner < IntWritable, NullWritable> ();
    /* test sequence 4, 8, 12, ... 128 */
    for(int i = 0; i < END; i+= STEP) {
      results[p.getPartition(new IntWritable(i), null, PARTITIONS)]++;
    }
    int badbuckets = 0;
    Integer min = Collections.min(Arrays.asList(ArrayUtils.toObject(results)));
    Integer max = Collections.max(Arrays.asList(ArrayUtils.toObject(results)));
    Integer avg = (int) Math.round((max+min)/2.0);
    System.out.println("Dumping buckets distribution: min="+min+" avg="+avg+" max="+max);
    for (int i = 0; i < PARTITIONS; i++) {
      double var = (results[i]-avg)/(double)(avg);
      System.out.println("bucket "+i+" "+results[i]+" items, variance "+var);
      if (Math.abs(var) > MAX_ERROR)
        badbuckets++;
    }
    System.out.println(badbuckets + " of "+PARTITIONS+" are too small or large buckets");
    assertTrue("too many overflow buckets", badbuckets < PARTITIONS * MAX_BADBUCKETS);
  }