On Wed, 12 May 2021 12:20:09 GMT, iaroslavski 
<github.com+43264149+iaroslav...@openjdk.org> wrote:

>> src/java.base/share/classes/java/util/DualPivotQuicksort.java line 47:
>> 
>>> 45:  * @author Doug Lea
>>> 46:  *
>>> 47:  * @version 2020.06.14
>> 
>> Vladimir, I would update to 2021.05.06 (+your hash)
>
> Laurent, the date in this class is not the date of our last commit,
> this date is the date when I have final idea regarding to Radix sort,
> therefore, I prefer to keep 2020.06.14

Hi @iaroslavski I'm unconvinced that this work was from 14/06/2020 - I believe 
this work derives from an unsigned radix sort I implemented on 10/04/2021 
https://github.com/richardstartin/radix-sort-benchmark/commit/ab4da230e1d0ac68e5ee2cee38d71c7e7d50f49b#diff-6c13d3fb74f38906677dbfa1a70a123c8e5baf4a39219c81ef121e078d0013bcR226
 which has numerous structural similarities to this work:
* Producing all four histograms in one pass
* Skipping passes based on detecting the total in the histogram
* Bailing out of the skip detection if a nonzero value not equal to the total 
is encountered
* Manually unrolling the LSD radix sort loop in order to avoid array copies

My implementation from 10th April is below for reference:

  public static void unrollOnePassHistogramsSkipLevels(int[] data) {
    int[] histogram1 = new int[257];
    int[] histogram2 = new int[257];
    int[] histogram3 = new int[257];
    int[] histogram4 = new int[257];

    for (int value : data) {
      ++histogram1[(value & 0xFF) + 1];
      ++histogram2[((value >>> 8) & 0xFF) + 1];
      ++histogram3[((value >>> 16) & 0xFF) + 1];
      ++histogram4[(value >>> 24) + 1];
    }
    boolean skipLevel1 = canSkipLevel(histogram1, data.length);
    boolean skipLevel2 = canSkipLevel(histogram2, data.length);
    boolean skipLevel3 = canSkipLevel(histogram3, data.length);
    boolean skipLevel4 = canSkipLevel(histogram4, data.length);

    if (skipLevel1 && skipLevel2 && skipLevel3 && skipLevel4) {
      return;
    }
    int[] copy = new int[data.length];

    int[] source = data;
    int[] dest = copy;

    if (!skipLevel1) {
      for (int i = 1; i < histogram1.length; ++i) {
        histogram1[i] += histogram1[i - 1];
      }
      for (int value : source) {
        dest[histogram1[value & 0xFF]++] = value;
      }
      if (!skipLevel2 || !skipLevel3 || !skipLevel4) {
        int[] tmp = dest;
        dest = source;
        source = tmp;
      }
    }

    if (!skipLevel2) {
      for (int i = 1; i < histogram2.length; ++i) {
        histogram2[i] += histogram2[i - 1];
      }
      for (int value : source) {
        dest[histogram2[(value >>> 8) & 0xFF]++] = value;
      }
      if (!skipLevel3 || !skipLevel4) {
        int[] tmp = dest;
        dest = source;
        source = tmp;
      }
    }

    if (!skipLevel3) {
      for (int i = 1; i < histogram3.length; ++i) {
        histogram3[i] += histogram3[i - 1];
      }
      for (int value : data) {
        dest[histogram3[(value >>> 16) & 0xFF]++] = value;
      }
      if (!skipLevel4) {
        int[] tmp = dest;
        dest = source;
        source = tmp;
      }
    }

    if (!skipLevel4) {
      for (int i = 1; i < histogram4.length; ++i) {
        histogram4[i] += histogram4[i - 1];
      }
      for (int value : source) {
        dest[histogram4[value >>> 24]++] = value;
      }
    }
    if (dest != data) {
      System.arraycopy(dest, 0, data, 0, data.length);
    }
  }

  private static boolean canSkipLevel(int[] histogram, int dataSize) {
    for (int count : histogram) {
      if (count == dataSize) {
        return true;
      } else if (count > 0) {
        return false;
      }
    }
    return true;
  }


Moreover, @bourgesl forked my repository on 11/04/2021 and communicated with me 
about doing so. On 25/04/2021 there was a new implementation of 
`DualPivotQuicksort` with a signed radix sort but the same structural 
similarities, and with the same method and variable names in places 
https://github.com/bourgesl/radix-sort-benchmark/commit/90ff7e427da0fa49f374bff0241fb2487bd87bde#diff-397ce8fd791e2ce508cf9127201bc9ab46264cd2a79fd0487a63569f2e4b59b2R607-R609


    // TODO add javadoc
    private static void radixSort(Sorter sorter, int[] a, int low, int high) {
        int[] b;
        // LBO: prealloc (high - low) +1 element:
        if (sorter == null || (b = sorter.b) == null || b.length < (high - 
low)) {
            // System.out.println("alloc b: " + (high - low));
            b = new int[high - low];
        }

        int[] count1, count2, count3, count4;
        if (sorter != null) {
            sorter.resetRadixBuffers();
            count1 = sorter.count1;
            count2 = sorter.count2;
            count3 = sorter.count3;
            count4 = sorter.count4;
        } else {
            // System.out.println("alloc radix buffers(4x256)");
            count1 = new int[256];
            count2 = new int[256];
            count3 = new int[256];
            count4 = new int[256];
        }

        for (int i = low; i < high; ++i) {
            --count1[ a[i]         & 0xFF ];
            --count2[(a[i] >>>  8) & 0xFF ];
            --count3[(a[i] >>> 16) & 0xFF ];
            --count4[(a[i] >>> 24) ^ 0x80 ];
        }

        boolean skipLevel4 = canSkipLevel(count4, low - high);
        boolean skipLevel3 = skipLevel4 && canSkipLevel(count3, low - high);
        boolean skipLevel2 = skipLevel3 && canSkipLevel(count2, low - high);

        count1[255] += high;
        count2[255] += high;
        count3[255] += high;
        count4[255] += high;

        // 1 todo process LSD
        for (int i = 255; i > 0; --i) {
            count1[i - 1] += count1[i];
        }

        for (int i = low; i < high; ++i) {
            b[count1[a[i] & 0xFF]++ - low] = a[i];
        }

        if (skipLevel2) {
            System.arraycopy(b, 0, a, low, high - low);
            return;
        }

        // 2
        for (int i = 255; i > 0; --i) {
            count2[i - 1] += count2[i];
        }

        //for (int value : b) {
        //    a[count2[(value >> 8) & 0xFF]++] = value;
        for (int i = low; i < high; ++i) {
            a[count2[(b[i] >> 8) & 0xFF]++] = b[i];
        }

        if (skipLevel3) {
            return;
        }

        // 3
        for (int i = 255; i > 0; --i) {
            count3[i - 1] += count3[i];
        }

        for (int i = low; i < high; ++i) {
            b[count3[(a[i] >> 16) & 0xFF]++ - low] = a[i];
        }

        if (skipLevel4) {
            System.arraycopy(b, 0, a, low, high - low);
            return;
        }

        // 4
        for (int i = 255; i > 0; --i) {
            count4[i - 1] += count4[i];
        }

        // for (int value : b) {
        //    a[count4[ (value >>> 24) ^ 0x80]++] = value;
        for (int i = low; i < high; ++i) {
            a[count4[ (b[i] >>> 24) ^ 0x80]++] = b[i];
        }
    }

    // TODO: add javadoc
    private static boolean canSkipLevel(int[] count, int total) {
        for (int c : count) {
            if (c == 0) {
                continue;
            }
            return c == total;
        }
        return true;
    }


Later, the name of the method `canSkipLevel` changed to `skipByte`: 
https://github.com/bourgesl/radix-sort-benchmark/commit/a693b26b2e2c14cfeedf9c753c9d643096b0e38d#diff-397ce8fd791e2ce508cf9127201bc9ab46264cd2a79fd0487a63569f2e4b59b2R719
 - this is the name of the method in the version of this sort you committed on 
05/01/2021 
https://github.com/richardstartin/sorting/commit/f076073b8b819a9687613903a164e3ed71821769#diff-4b4d68fc834c2ad12a9fb9d316a812221af7c398338ed2ee907d0a795e7aadafR601-R604


    // TODO add javadoc
//  private 
    static void radixSort(Sorter sorter, int[] a, int low, int high) {
//System.out.println("                        Radix !!!");
        int[] count1 = new int[256];
        int[] count2 = new int[256];
        int[] count3 = new int[256];
        int[] count4 = new int[256];

        for (int i = low; i < high; ++i) {
            count1[ a[i]         & 0xFF ]--;
            count2[(a[i] >>>  8) & 0xFF ]--;
            count3[(a[i] >>> 16) & 0xFF ]--;
            count4[(a[i] >>> 24) ^ 0x80 ]--;
        }
        boolean skipByte4 = skipByte(count4, low - high, high, true);
        boolean skipByte3 = skipByte(count3, low - high, high, skipByte4);
        boolean skipByte2 = skipByte(count2, low - high, high, skipByte3);
        boolean skipByte1 = skipByte(count1, low - high, high, skipByte2);

        if (skipByte1) {
//Main.check(a, low, high - 1); // todo
            return;
        }
//        int xorA = Main.getXor(a, low, high);

        int[] b; int offset = low;

        if (sorter == null || (b = (int[]) sorter.b) == null) {
            b = new int[high - low];
        } else {
            offset = sorter.offset;
//System.out.println("      !!!! offset: " + offset);
        }
        int start = low - offset;
        int last = high - offset;


        // 1 todo process LSD
        for (int i = low; i < high; ++i) {
            b[count1[a[i] & 0xFF]++ - offset] = a[i];
        }

//        if (xorA != Main.getXor(a, low, high)) System.out.println("6K_4 1 xor 
changed");

        if (skipByte2) {
            System.arraycopy(b, start, a, low, high - low);
//Main.check(a, low, high - 1); // todo
            return;
        }

        // 2
        for (int i = start; i < last; ++i) {
            a[count2[(b[i] >> 8) & 0xFF]++] = b[i];
        }

//        if (xorA != Main.getXor(a, low, high)) System.out.println("6K_4 2 xor 
changed");

        if (skipByte3) {
//Main.check(a, low, high - 1); // todo
            return;
        }

        // 3
        for (int i = low; i < high; ++i) {
            b[count3[(a[i] >> 16) & 0xFF]++ - offset] = a[i];
        }

//        if (xorA != Main.getXor(a, low, high)) System.out.println("6K_4 3 xor 
changed");

        if (skipByte4) {
            System.arraycopy(b, start, a, low, high - low);
//Main.check(a, low, high - 1); // todo
            return;
        }

//        if (xorA != Main.getXor(a, low, high)) System.out.println("6K_4 4 xor 
changed");
        // 4
        for (int i = start; i < last; ++i) {
            a[count4[(b[i] >>> 24) ^ 0x80]++] = b[i];
        }
//        if (xorA != Main.getXor(a, low, high)) System.out.println("6K_4 5 xor 
changed");
//Main.check(a, low, high - 1); // todo
    }

    // TODO: add javadoc
    private static boolean skipByte(int[] count, int total, int high, boolean 
prevSkip) {
        if (prevSkip) {
            for (int c : count) {
                if (c == 0) {
                    continue;
                }
                if (c == total) {
                    return true;
                }
                break;
            }
        }
        // todo create historgam
        count[255] += high;

        for (int i = 255; i > 0; --i) {
            count[i - 1] += count[i];
        }
        return false;
    }


`skipByte` was later renamed to `passLevel` here, which is the name used in 
this PR: 
https://github.com/richardstartin/sorting/commit/22357e407d3ae7a1e159e06fe4afbb2c57f7d34c#diff-4b4d68fc834c2ad12a9fb9d316a812221af7c398338ed2ee907d0a795e7aadaf

Given the structural similarities mentioned, the chronological order of these 
commits, and the demonstrable provenance of the method name `passLevel` from 
`canSkipLevel` and since you have patented sort algorithms in the past, I want 
to make sure that it is recognised that this work is derived from my own.

-------------

PR: https://git.openjdk.java.net/jdk/pull/3938

Reply via email to