public static void Set(NumberSystem numberSystem) { var existing = _value; if (existing == null) { switch (numberSystem) { case NumberSystem.Unsigned: case NumberSystem.OnesComplement: case NumberSystem.TwosComplement: case NumberSystem.SignBit: _value = numberSystem; break; default: throw new ArgumentOutOfRangeException(nameof(numberSystem)); } } else if (numberSystem != existing.Value) { throw new InvalidOperationException($"The number-system for '{typeof(T).Name}' has already been set and cannot be changed"); } }
private static void Sort32(uint *keys, uint *workspace, int len, int r, uint keyMask, bool ascending, NumberSystem numberSystem) { if (len <= 1 || keyMask == 0) { return; } r = Util.ChooseBitCount <uint>(r, DefaultR); int countLength = 1 << r; int groups = GroupCount <uint>(r); uint *countsOffsets = stackalloc uint[countLength]; uint mask = (uint)(countLength - 1); bool reversed = false; if (SortCore32(keys, workspace, r, keyMask, countLength, len, countsOffsets, groups, mask, ascending, numberSystem != NumberSystem.Unsigned)) { Swap(ref keys, ref workspace, ref reversed); } if (reversed) { Unsafe.CopyBlock(workspace, keys, (uint)len << 2); } }
private static unsafe int ParallelSort32 <T>(Memory <T> keys, Memory <T> workspace, int r, bool descending, uint keyMask, NumberSystem numberSystem) where T : struct { r = Util.ChooseBitCount <uint>(r, DefaultR); int bucketCount = 1 << r, len = keys.Length; if (len <= 1 || keyMask == 0) { return(0); } workspace = workspace.Slice(0, len); int groups = Util.GroupCount <uint>(r); uint mask = (uint)(bucketCount - 1); int workerCount = Util.WorkerCount(keys.Length, MaxWorkerCount); // a shame that we nned to use "unsafe" for this, but we can't put a Span<uint> as // a field on the worker; however: the stack *won't* move, so this is in fact // perfectly safe, despite what it looks like uint *workerCountsOffsets = stackalloc uint[workerCount * bucketCount]; var worker = new Worker <T>(workerCount, workerCountsOffsets); if ((keyMask & Util.MSB32U) == 0) { numberSystem = NumberSystem.Unsigned; // without the MSB, sign doesn't matter } bool reversed = false; if (numberSystem == NumberSystem.SignBit) { // sort *just* on the MSB var split = ParallelSortCore32(worker, keys, workspace, 1, !descending, Util.MSB32U, true, 2, 32, mask, 31); if (split.Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } keyMask &= ~Util.MSB32U; // now sort the two chunks separately, respecting the corresponding data/workspace areas // note: regardless of asc/desc, we will always want the first chunk to be decreasing magnitude and the second chunk to be increasing magnitude - hence false/true var lower = split.Split <= 1 ? default : ParallelSortCore32(worker, keys.Slice(0, split.Split), workspace.Slice(0, split.Split), r, false, keyMask, false, bucketCount, groups, mask); var upper = split.Split >= keys.Length - 1 ? default : ParallelSortCore32(worker, keys.Slice(split.Split), workspace.Slice(split.Split), r, true, keyMask, false, bucketCount, groups, mask); if (lower.Reversed == upper.Reversed) { // both or neither reversed if (lower.Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } } else if (split.Split < (keys.Length / 2)) // lower group is smaller { if (split.Split != 0) { keys.Slice(0, split.Split).CopyTo(workspace.Slice(0, split.Split)); } // the lower-half is now in both spaces; respect the opinion of the upper-half if (upper.Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } } else // upper group is smaller { if (split.Split != keys.Length) { keys.Slice(split.Split).CopyTo(workspace.Slice(split.Split)); } // the upper-half is now in both spaces; respect the opinion of the lower-half if (lower.Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } } } else if (ParallelSortCore32(worker, keys, workspace, r, !descending, keyMask, numberSystem != NumberSystem.Unsigned, bucketCount, groups, mask).Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } if (reversed) { worker.Prepare(bucketCount, keys, workspace); worker.Execute(WorkerStep.Copy); } return(workerCount); }
private static void Sort8(Span <byte> keys, Span <byte> workspace, int r, byte keyMask, bool ascending, NumberSystem numberSystem) { r = Util.ChooseBitCount <byte>(r, DefaultR); if (keys.Length <= 1 || keyMask == 0) { return; } int countLength = 1 << r; Span <uint> countsOffsets = stackalloc uint[countLength]; workspace = workspace.Slice(0, keys.Length); int groups = Util.GroupCount <byte>(r); byte mask = (byte)(countLength - 1); bool reversed = false; if ((keyMask & Util.MSB8U) == 0) { numberSystem = NumberSystem.Unsigned; // without the MSB, sign doesn't matter } if (numberSystem == NumberSystem.SignBit) { // sort *just* on the MSB var split = SortCore8(keys, workspace, 1, Util.MSB8U, 2, countsOffsets.Slice(0, 2), 8, 1, ascending, true, 7); if (split.Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } keyMask = (byte)(keyMask & ~Util.MSB8U); // now sort the two chunks separately, respecting the corresponding data/workspace areas // note: regardless of asc/desc, we will always want the first chunk to be decreasing magnitude and the second chunk to be increasing magnitude - hence false/true var lower = split.Split <= 1 ? default : SortCore8(keys.Slice(0, split.Split), workspace.Slice(0, split.Split), r, keyMask, countLength, countsOffsets, groups, mask, false, false); var upper = split.Split >= keys.Length - 1 ? default : SortCore8(keys.Slice(split.Split), workspace.Slice(split.Split), r, keyMask, countLength, countsOffsets, groups, mask, true, false); if (lower.Reversed == upper.Reversed) { // both or neither reversed if (lower.Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } } else if (split.Split < (keys.Length / 2)) // lower group is smaller { if (split.Split != 0) { keys.Slice(0, split.Split).CopyTo(workspace.Slice(0, split.Split)); } // the lower-half is now in both spaces; respect the opinion of the upper-half if (upper.Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } } else // upper group is smaller { if (split.Split != keys.Length) { keys.Slice(split.Split).CopyTo(workspace.Slice(split.Split)); } // the upper-half is now in both spaces; respect the opinion of the lower-half if (lower.Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } } } else if (SortCore8(keys, workspace, r, keyMask, countLength, countsOffsets, groups, mask, ascending, numberSystem != NumberSystem.Unsigned).Reversed) { Util.Swap(ref keys, ref workspace, ref reversed); } if (reversed) { keys.CopyTo(workspace); } }
private static void Sort32(Span <uint> keys, Span <uint> workspace, int r, uint keyMask, bool ascending, NumberSystem numberSystem) { if ((keyMask & Util.MSB32U) == 0) { numberSystem = NumberSystem.Unsigned; } if (!ascending || numberSystem != NumberSystem.Unsigned) { throw new NotImplementedException("Need to do that!"); } if (ascending ? Util.ShortSortAscending(keys, 0, (uint)keys.Length) : Util.ShortSortDescending(keys, 0, (uint)keys.Length)) { r = Util.ChooseBitCount <uint>(r, DefaultR); workspace = workspace.Slice(0, keys.Length); if (keyMask == 0) { return; } Span <uint> offsets = stackalloc uint[1 << r]; int bucketCount = 1 << r, groups = ((32 - 1) / r) + 1; uint mask = (uint)(bucketCount - 1), groupMask; var shift = 32; do { shift -= r; groupMask = (keyMask >> shift) & mask; keyMask &= ~(mask << shift); } while (groupMask == 0); // no need to check groupMask is non-zero - we already checked keyMask, so we definitely expect *something* Sort32(keys, workspace, offsets, keyMask, groupMask, mask, r, shift, ascending, 0, keys.Length); } }
private static int ParallelSort32 <T>(Memory <T> keys, Memory <T> workspace, int r, bool descending, uint keyMask, NumberSystem numberSystem) where T : struct { if (keys.Length <= 1 || keyMask == 0) { return(0); } if (workspace.Length < ParallelWorkspaceSize <uint>(keys.Length, r)) { throw new ArgumentException($"The workspace provided is insufficient ({workspace.Length} vs {ParallelWorkspaceSize<uint>(keys.Length, r)} needed); the {nameof(ParallelWorkspaceSize)} method can be used to determine the minimum size required", nameof(workspace)); } int bucketCount = 1 << r, len = keys.Length; int countsOffsetsAsT = (((bucketCount << 2) - 1) / Unsafe.SizeOf <T>()) + 1; int workerCount = WorkerCount(len); var workerCountsOffsets = workspace.Slice(0, countsOffsetsAsT * workerCount); workspace = workspace.Slice(countsOffsetsAsT * workerCount, len); int groups = GroupCount <uint>(r); uint mask = (uint)(bucketCount - 1); var worker = new Worker <T>(workerCount, bucketCount, keys, workspace, workerCountsOffsets); bool reversed = false; int invertC = numberSystem != NumberSystem.Unsigned ? groups - 1 : -1; for (int c = 0, shift = 0; c < groups; c++, shift += r) { uint groupMask = (keyMask >> shift) & mask; keyMask &= ~(mask << shift); // remove those bits from the keyMask to allow fast exit if (groupMask == 0) { if (keyMask == 0) { break; } else { continue; } } // counting elements of the c-th group worker.SetGroup(groupMask, shift); worker.Execute(descending ? WorkerStep.BucketCountDescending : WorkerStep.BucketCountAscending); if (!worker.ComputeOffsets(c == invertC ? GetInvertStartIndex(32, r) : 0)) { continue; // all in one group } worker.Execute(descending ? WorkerStep.ApplyDescending : WorkerStep.ApplyAscending); worker.Swap(); reversed = !reversed; } if (reversed) { worker.Execute(WorkerStep.Copy); } return(workerCount); }
private static void Sort32(uint *keys, uint *workspace, int len, int r, uint keyMask, bool ascending, NumberSystem numberSystem) { if (len <= 1 || keyMask == 0) { return; } if (r < 1 || r > MAX_R) { throw new ArgumentOutOfRangeException(nameof(r)); } int countLength = 1 << r; int groups = GroupCount <uint>(r); uint *countsOffsets = stackalloc uint[countLength]; uint mask = (uint)(countLength - 1); bool reversed = false; if (SortCore32(keys, workspace, r, keyMask, countLength, len, countsOffsets, groups, mask, ascending, numberSystem != NumberSystem.Unsigned)) { Swap(ref keys, ref workspace, ref reversed); } if (reversed) Unsafe.CopyBlock(workspace, keys, (uint)len << 2); }
public static void SetNumberSystem <T>(NumberSystem numberSystem) => NumberSystem <T> .Set(numberSystem);
private static void Sort32(Span <uint> keys, Span <uint> workspace, int r, uint keyMask, bool ascending, NumberSystem numberSystem) { if (keys.Length <= 1 || keyMask == 0) { return; } if (workspace.Length < WorkspaceSize <uint>(keys.Length, r)) { throw new ArgumentException($"The workspace provided is insufficient ({workspace.Length} vs {WorkspaceSize<uint>(keys.Length, r)} needed); the {nameof(WorkspaceSize)} method can be used to determine the minimum size required", nameof(workspace)); } int countLength = 1 << r; var countsOffsets = workspace.Slice(0, countLength); workspace = workspace.Slice(countLength, keys.Length); int groups = GroupCount <uint>(r); uint mask = (uint)(countLength - 1); bool reversed = false; if ((keyMask & MSB32U) == 0) { numberSystem = NumberSystem.Unsigned; // without the MSB, sign doesn't matter } if (numberSystem == NumberSystem.SignBit) { // sort *just* on the MSB var split = SortCore32(keys, workspace, 1, MSB32U, 2, countsOffsets.Slice(0, 2), 32, 1, ascending, true, 31); if (split.Reversed) { Swap(ref keys, ref workspace, ref reversed); } keyMask &= ~MSB32U; // now sort the two chunks separately, respecting the corresponding data/workspace areas // note: regardless of asc/desc, we will always want the first chunk to be decreasing magnitude and the second chunk to be increasing magnitude - hence false/true var lower = split.Split == 0 ? default : SortCore32(keys.Slice(0, split.Split), workspace.Slice(0, split.Split), r, keyMask, countLength, countsOffsets, groups, mask, false, false); var upper = split.Split == keys.Length ? default : SortCore32(keys.Slice(split.Split), workspace.Slice(split.Split), r, keyMask, countLength, countsOffsets, groups, mask, true, false); if (lower.Reversed == upper.Reversed) { // both or neither reversed if (lower.Reversed) { Swap(ref keys, ref workspace, ref reversed); } } else if (split.Split < (keys.Length / 2)) // lower group is smaller { if (split.Split != 0) { keys.Slice(0, split.Split).CopyTo(workspace.Slice(0, split.Split)); } // the lower-half is now in both spaces; respect the opinion of the upper-half if (upper.Reversed) { Swap(ref keys, ref workspace, ref reversed); } } else // upper group is smaller { if (split.Split != keys.Length) { keys.Slice(split.Split).CopyTo(workspace.Slice(split.Split)); } // the upper-half is now in both spaces; respect the opinion of the lower-half if (lower.Reversed) { Swap(ref keys, ref workspace, ref reversed); } } } else if (SortCore32(keys, workspace, r, keyMask, countLength, countsOffsets, groups, mask, ascending, numberSystem != NumberSystem.Unsigned).Reversed) { Swap(ref keys, ref workspace, ref reversed); } if (reversed) { keys.CopyTo(workspace); } }