Beispiel #1
0
        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);
        }
Beispiel #4
0
        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);
            }
        }
Beispiel #5
0
        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);
            }
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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); }
Beispiel #8
0
 public static void SetNumberSystem <T>(NumberSystem numberSystem) => NumberSystem <T> .Set(numberSystem);
Beispiel #9
0
        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);
            }
        }