/// <summary>
        /// Sets the status for the specified integer to false.
        /// </summary>
        /// <param name="inputInteger">The integer to set the status for.</param>
        public void SetStatusFalse(Int64 inputInteger)
        {
            LongIntegerRange rangeToModify = null;

            // Create an integer range based on the inputted integer
            LongIntegerRange inputIntegerRange = new LongIntegerRange(inputInteger, 1);

            // Attempt to find the range holding the inputted integer
            Tuple <Boolean, LongIntegerRange> nextLowerRange = rangeStatuses.GetNextLessThan(inputIntegerRange);

            // Check whether a range starting with the specified integer exists in the tree
            if (rangeStatuses.Contains(inputIntegerRange) == true)
            {
                // Need special handling if inputInteger is Int64.MaxValue
                if (inputInteger == Int64.MaxValue)
                {
                    rangeToModify = new LongIntegerRange(inputInteger, 1);
                }
                else
                {
                    rangeToModify = rangeStatuses.GetNextLessThan(new LongIntegerRange(inputInteger + 1, 1)).Item2;
                }
            }
            // Otherwise check whether the specified integer exists in the next lower range
            else if (nextLowerRange.Item1 == true && (inputInteger <= (nextLowerRange.Item2.StartValue + nextLowerRange.Item2.Length - 1)))
            {
                rangeToModify = nextLowerRange.Item2;
            }

            if (rangeToModify != null)
            {
                // Handle the case where the inputted integer is at the start of the range to modify
                if (inputInteger == rangeToModify.StartValue)
                {
                    if (rangeToModify.Length == 1)
                    {
                        rangeStatuses.Remove(rangeToModify);
                    }
                    else
                    {
                        rangeToModify.Length     = rangeToModify.Length - 1;
                        rangeToModify.StartValue = rangeToModify.StartValue + 1;
                    }
                }
                // Handle the case where the inputted integer is at the end of the range to modify
                else if (inputInteger == (rangeToModify.StartValue + rangeToModify.Length - 1))
                {
                    rangeToModify.Length = rangeToModify.Length - 1;
                }
                // Handle the case where the inputted integer is in the middle of the range to modify
                else
                {
                    LongIntegerRange newRange = new LongIntegerRange(inputInteger + 1, rangeToModify.Length - (inputInteger - rangeToModify.StartValue + 1));
                    rangeToModify.Length = inputInteger - rangeToModify.StartValue;
                    rangeStatuses.Add(newRange);
                }

                count--;
            }
        }
        /// <summary>
        /// Sets a contiguous range of statuses to true when all statuses are false.
        /// </summary>
        /// <param name="rangeStart">The inclusive start of the range of integers to set to true.</param>
        /// <param name="rangeEnd">The inclusive end of the range of integers to set to true.</param>
        /// <exception cref="System.InvalidOperationException">Statuses for integers have been set to true.</exception>
        /// <exception cref="System.ArgumentException">Parameter 'rangeEnd' is less than parameter 'rangeStart'.</exception>
        /// <exception cref="System.ArgumentException">The total inclusive range exceeds Int64.MaxValue.</exception>
        public void SetRangeTrue(Int64 rangeStart, Int64 rangeEnd)
        {
            if (rangeStatuses.Count != 0)
            {
                throw new InvalidOperationException("A range of statuses can only be set true when all existing statuses are false.");
            }
            if (rangeEnd < rangeStart)
            {
                throw new ArgumentException($"Parameter '{nameof(rangeEnd)}' must be greater than or equal to parameter '{nameof(rangeStart)}'.", nameof(rangeEnd));
            }
            if (rangeStart < 1)
            {
                if (rangeStart + Int64.MaxValue <= rangeEnd)
                {
                    throw new ArgumentException("The total inclusive range cannot exceed Int64.MaxValue.", nameof(rangeEnd));
                }
            }
            else if (rangeEnd > -2)
            {
                if (rangeEnd - Int64.MaxValue >= rangeStart)
                {
                    throw new ArgumentException("The total inclusive range cannot exceed Int64.MaxValue.", nameof(rangeEnd));
                }
            }

            var newRange = new LongIntegerRange(rangeStart, rangeEnd - rangeStart + 1);

            rangeStatuses.Add(newRange);
            count = newRange.Length;
        }
        /// <summary>
        /// Initialises a new instance of the MoreComplexDataStructures.UniqueRandomGenerator class.
        /// </summary>
        /// <param name="rangeStart">The inclusive start of the range to generate random numbers from.</param>
        /// <param name="rangeEnd">The inclusive end of the range to generate random numbers from.</param>
        /// <exception cref="System.ArgumentException">Parameter 'rangeEnd' is less than parameter 'rangeStart'.</exception>
        /// <exception cref="System.ArgumentException">The total inclusive range exceeds Int64.MaxValue.</exception>
        public UniqueRandomGenerator(Int64 rangeStart, Int64 rangeEnd)
        {
            if (rangeEnd < rangeStart)
            {
                throw new ArgumentException($"Parameter '{nameof(rangeEnd)}' must be greater than or equal to parameter '{nameof(rangeStart)}'.", nameof(rangeEnd));
            }
            if (rangeStart < 1)
            {
                if (rangeStart + Int64.MaxValue <= rangeEnd)
                {
                    throw new ArgumentException("The total inclusive range cannot exceed Int64.MaxValue.", nameof(rangeEnd));
                }
            }
            else if (rangeEnd > -2)
            {
                if (rangeEnd - Int64.MaxValue >= rangeStart)
                {
                    throw new ArgumentException("The total inclusive range cannot exceed Int64.MaxValue.", nameof(rangeEnd));
                }
            }
            rangeTree = new RangeTree();
            var baseRange          = new LongIntegerRange(rangeStart, rangeEnd - rangeStart + 1);
            var baseRangeAndCounts = new RangeAndSubtreeCounts(baseRange, 0, 0);

            rangeTree.Add(baseRangeAndCounts);
            this.rangeStart       = rangeStart;
            this.rangeEnd         = rangeEnd;
            numbersGeneratedCount = 0;
            randomGenerator       = new DefaultRandomGenerator();
        }
            /// <summary>
            /// Initialises a new instance of the MoreComplexDataStructures.UniqueRandomGenerator+RangeAndSubtreeCounts class.
            /// </summary>
            /// <param name="range">The range of integers.</param>
            /// <param name="leftSubtreeRangeCount">The total count of integers in the left subtree of a node holding an instance of this class.</param>
            /// <param name="rightSubtreeRangeCount">The total count of integers in the right subtree of a node holding an instance of this class.</param>
            public RangeAndSubtreeCounts(LongIntegerRange range, Int64 leftSubtreeRangeCount, Int64 rightSubtreeRangeCount)
            {
                ValidateSubtreeRangeCount(leftSubtreeRangeCount, "leftSubtreeRangeCount");
                ValidateSubtreeRangeCount(rightSubtreeRangeCount, "rightSubtreeRangeCount");

                this.range = range;
                this.leftSubtreeRangeCount  = leftSubtreeRangeCount;
                this.rightSubtreeRangeCount = rightSubtreeRangeCount;
            }
Ejemplo n.º 5
0
        /// <summary>
        /// Returns the lowest unflagged number in the range.
        /// </summary>
        /// <returns>A tuple containing 2 values: a boolean indicating whether any flagged numbers exist (false if no flagged numbers exist), and the lowest unflagged number in the range (or 0 if no unflagged numbers exist).</returns>
        public Tuple <Boolean, Int64> GetLowestUnflaggedNumber()
        {
            if (rangeStorer.Count == 0)
            {
                return(new Tuple <Boolean, Int64>(false, 0));
            }
            else
            {
                LongIntegerRange lowestRange = rangeStorer.MinimumRange;

                return(new Tuple <Boolean, Int64>(true, lowestRange.StartValue));
            }
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Returns the highest unflagged number in the range.
        /// </summary>
        /// <returns>A tuple containing 2 values: a boolean indicating whether any flagged numbers exist (false if no flagged numbers exist), and the highest unflagged number in the range (or 0 if no unflagged numbers exist).</returns>
        public Tuple <Boolean, Int64> GetHighestUnflaggedNumber()
        {
            if (rangeStorer.Count == 0)
            {
                return(new Tuple <Boolean, Int64>(false, 0));
            }
            else
            {
                LongIntegerRange highestRange = rangeStorer.MaximumRange;

                return(new Tuple <Boolean, Int64>(true, highestRange.StartValue + highestRange.Length - 1));
            }
        }
        /// <summary>
        /// Sets the status for the specified integer to true.
        /// </summary>
        /// <param name="inputInteger">The integer to set the status for.</param>
        /// <exception cref="System.InvalidOperationException">The class cannot support storing statuses for greater than Int64.MaxValue integers.</exception>
        public void SetStatusTrue(Int64 inputInteger)
        {
            if (count == Int64.MaxValue)
            {
                throw new InvalidOperationException("The class cannot support storing statuses for greater than Int64.MaxValue integers.");
            }

            // Create an integer range based on the inputted integer
            LongIntegerRange inputIntegerRange = new LongIntegerRange(inputInteger, 1);

            // Check whether a range starting with the specified integer already exists in the tree
            if (rangeStatuses.Contains(inputIntegerRange) == true)
            {
                return;
            }

            Tuple <Boolean, LongIntegerRange> nextLowerRange   = rangeStatuses.GetNextLessThan(inputIntegerRange);
            Tuple <Boolean, LongIntegerRange> nextGreaterRange = rangeStatuses.GetNextGreaterThan(inputIntegerRange);

            // Check whether the specified integer exists in the next lower range
            if (nextLowerRange.Item1 == true && (inputInteger <= (nextLowerRange.Item2.StartValue + nextLowerRange.Item2.Length - 1)))
            {
                return;
            }
            // Handle the case where the new integer 'merges' two existing ranges
            if (IntegerIsImmediatelyGreaterThanRange(inputInteger, nextLowerRange) && IntegerIsImmediatelyLessThanRange(inputInteger, nextGreaterRange))
            {
                rangeStatuses.Remove(nextGreaterRange.Item2);
                nextLowerRange.Item2.Length = nextLowerRange.Item2.Length + 1 + nextGreaterRange.Item2.Length;
            }
            // Handle the case where the new integer 'extends' the next lower range by 1
            else if (IntegerIsImmediatelyGreaterThanRange(inputInteger, nextLowerRange))
            {
                nextLowerRange.Item2.Length = nextLowerRange.Item2.Length + 1;
            }
            // Handle the case where the new integer reduces the start value of the next greater range by 1
            else if (IntegerIsImmediatelyLessThanRange(inputInteger, nextGreaterRange))
            {
                nextGreaterRange.Item2.StartValue = nextGreaterRange.Item2.StartValue - 1;
                nextGreaterRange.Item2.Length     = nextGreaterRange.Item2.Length + 1;
            }
            // Otherwise add a new range starting at 'inputInteger' with length = 1
            else
            {
                rangeStatuses.Add(inputIntegerRange);
            }

            count++;
        }
        /// <summary>
        /// Returns copies of all number ranges in the underlying tree in descending order.
        /// </summary>
        /// <returns>All number ranges in the underlying tree in descending order.</returns>
        /// <remarks>Note that copies/clones of the actual ranges are returned to prevent modification/invalidation of the underlying tree structure.</remarks>
        public IEnumerable <LongIntegerRange> GetAllRangesDescending()
        {
            if (rangeStatuses.Count == 0)
            {
                yield break;
            }

            LongIntegerRange maximumRange = rangeStatuses.Max;

            yield return(new LongIntegerRange(maximumRange.StartValue, maximumRange.Length));

            foreach (LongIntegerRange currentRange in rangeStatuses.GetAllLessThan(maximumRange))
            {
                yield return(new LongIntegerRange(currentRange.StartValue, currentRange.Length));
            }
        }
        /// <summary>
        /// Retrieves the current status for the specified integer.
        /// </summary>
        /// <param name="inputInteger">The integer to retrieve the status for.</param>
        /// <returns>The status of the integer.</returns>
        public Boolean GetStatus(Int64 inputInteger)
        {
            // Create an integer range based on the inputted integer
            LongIntegerRange inputIntegerRange = new LongIntegerRange(inputInteger, 1);

            // Check whether a range starting with the specified integer exists in the tree
            if (rangeStatuses.Contains(inputIntegerRange) == true)
            {
                return(true);
            }

            Tuple <Boolean, LongIntegerRange> nextLowerRange = rangeStatuses.GetNextLessThan(inputIntegerRange);

            // Check whether the specified integer exists in the next lower range
            if (nextLowerRange.Item1 == true && (inputInteger <= (nextLowerRange.Item2.StartValue + nextLowerRange.Item2.Length - 1)))
            {
                return(true);
            }

            return(false);
        }
        /// <summary>
        /// Sets the random weightings, and the item corresponding to each weighting.
        /// </summary>
        /// <param name="weightings">The set of weightings defined by list of tuples containing 2 values: the item to attach to the weighting, and a weighting allocated to random selection of the item (i.e. an item with weight 2 will be twice as likely to be selected as an item with weight 1).</param>
        /// <exception cref="System.ArgumentException">The specified set of weightings is empty.</exception>
        /// <exception cref="System.ArgumentException">The total of the specified weightings exceeds Int64.MaxValue.</exception>
        /// <exception cref="System.ArgumentException">The specified weightings contain a duplicate item.</exception>
        public void SetWeightings(IList <Tuple <T, Int64> > weightings)
        {
            if (weightings.Count == 0)
            {
                throw new ArgumentException("The specified set of weightings is empty.", nameof(weightings));
            }

            Clear();
            var   weightingsAsRanges  = new ItemAndWeighting <T> [weightings.Count];
            Int64 nextRangeStartValue = 0;
            Int32 currentIndex        = 0;

            foreach (Tuple <T, Int64> currentWeighting in weightings)
            {
                if ((Int64.MaxValue - 1) - nextRangeStartValue < (currentWeighting.Item2 - 1))
                {
                    throw new ArgumentException("The total of the specified weightings cannot exceed Int64.MaxValue.", nameof(weightings));
                }

                var currentRange = new LongIntegerRange(nextRangeStartValue, currentWeighting.Item2);
                weightingsAsRanges[currentIndex] = new ItemAndWeighting <T>(currentWeighting.Item1, currentRange);
                currentIndex++;
                weightingsTotal     += (currentWeighting.Item2);
                nextRangeStartValue += currentWeighting.Item2;
            }
            foreach (ItemAndWeighting <T> currentItemAndWeighting in weightingsAsRanges)
            {
                if (itemToWeightingMap.ContainsKey(currentItemAndWeighting.Item) == true)
                {
                    throw new ArgumentException($"The specified weightings contain duplicate item with value = '{currentItemAndWeighting.Item.ToString()}'.", nameof(weightings));
                }

                weightingRangesAndItems.Add(currentItemAndWeighting);
                itemToWeightingMap.Add(currentItemAndWeighting.Item, currentItemAndWeighting.Weighting);
            }
        }
 /// <summary>
 /// Initialises a new instance of the MoreComplexDataStructures.WeightedRandomGenerator+ItemAndWeighting class.
 /// </summary>
 /// <param name="item">The item attached to the weighting.</param>
 /// <param name="weighting">The weighting.</param>
 public ItemAndWeighting(T item, LongIntegerRange weighting)
 {
     this.item      = item;
     this.weighting = weighting;
 }
        /// <summary>
        /// Returns a unique random number from within the range.
        /// </summary>
        /// <returns>The random number.</returns>
        /// <exception cref="System.InvalidOperationException">No further unique numbers remain in the range.</exception>
        public Int64 Generate()
        {
            if (NumbersRemainingCount == 0)
            {
                throw new InvalidOperationException("Cannot generate a random number as no further unique numbers exist in the specified range.");
            }

            WeightBalancedTreeNode <RangeAndSubtreeCounts> currentNode = rangeTree.RootNode;
            Int64 returnNumber = 0;

            while (true)
            {
                // Decide whether to stop at this node, or to move left or right
                Int64 randomRange  = currentNode.Item.LeftSubtreeRangeCount + currentNode.Item.Range.Length + currentNode.Item.RightSubtreeRangeCount;
                Int64 randomNumber = randomGenerator.Next(randomRange);
                if (randomNumber < currentNode.Item.LeftSubtreeRangeCount)
                {
                    // Move to the left child node
                    currentNode.Item.LeftSubtreeRangeCount--;
                    currentNode = currentNode.LeftChildNode;
                }
                else if (randomNumber >= (currentNode.Item.LeftSubtreeRangeCount + currentNode.Item.Range.Length))
                {
                    // Move to the right child node
                    currentNode.Item.RightSubtreeRangeCount--;
                    currentNode = currentNode.RightChildNode;
                }
                else
                {
                    // Stop traversing at the current node
                    returnNumber = (randomNumber - currentNode.Item.LeftSubtreeRangeCount) + currentNode.Item.Range.StartValue;
                    break;
                }
            }
            if (returnNumber == currentNode.Item.Range.StartValue)
            {
                if (currentNode.Item.Range.Length == 1)
                {
                    if (currentNode.LeftChildNode != null && currentNode.RightChildNode != null)
                    {
                        // The current node will be swapped with the next less than or next greater than, so need to update the subtree range counts between the current and swapped nodes
                        WeightBalancedTreeNode <RangeAndSubtreeCounts> swapNode = null;
                        if (currentNode.LeftSubtreeSize > currentNode.RightSubtreeSize)
                        {
                            // The current node will be swapped with the next less than
                            swapNode = rangeTree.GetNextLessThan(currentNode);
                            swapNode.Item.LeftSubtreeRangeCount  = currentNode.Item.LeftSubtreeRangeCount - swapNode.Item.Range.Length;
                            swapNode.Item.RightSubtreeRangeCount = currentNode.Item.RightSubtreeRangeCount;
                        }
                        else
                        {
                            // The current node will be swapped with the next greater than
                            swapNode = rangeTree.GetNextGreaterThan(currentNode);
                            swapNode.Item.LeftSubtreeRangeCount  = currentNode.Item.LeftSubtreeRangeCount;
                            swapNode.Item.RightSubtreeRangeCount = currentNode.Item.RightSubtreeRangeCount - swapNode.Item.Range.Length;
                        }
                        // Update the subtree range counts
                        Func <WeightBalancedTreeNode <RangeAndSubtreeCounts>, Nullable <Boolean>, Boolean> updateSubtreeRangeCountFunc = (node, nodeTraversedToFromLeft) =>
                        {
                            if (node == currentNode)
                            {
                                return(false);
                            }
                            if (nodeTraversedToFromLeft.HasValue)
                            {
                                if (nodeTraversedToFromLeft == true)
                                {
                                    node.Item.LeftSubtreeRangeCount -= swapNode.Item.Range.Length;
                                }
                                else
                                {
                                    node.Item.RightSubtreeRangeCount -= swapNode.Item.Range.Length;
                                }
                            }
                            return(true);
                        };
                        rangeTree.TraverseUpToNode(swapNode, updateSubtreeRangeCountFunc);
                    }

                    rangeTree.Remove(currentNode.Item);
                }
                else
                {
                    // Shorten the range on the left side
                    currentNode.Item.Range.StartValue++;
                    currentNode.Item.Range.Length--;
                }
            }
            else if (returnNumber == (currentNode.Item.Range.StartValue + currentNode.Item.Range.Length - 1))
            {
                // Shorten the range on the right side
                currentNode.Item.Range.Length--;
            }
            else
            {
                // Split the range
                var leftSideRange = new LongIntegerRange(currentNode.Item.Range.StartValue, returnNumber - currentNode.Item.Range.StartValue);
                var newNodeItem   = new RangeAndSubtreeCounts(leftSideRange, currentNode.Item.LeftSubtreeRangeCount, currentNode.Item.RightSubtreeRangeCount);
                var newNode       = new WeightBalancedTreeNode <RangeAndSubtreeCounts>(newNodeItem, null);
                currentNode.Item.Range.Length    -= (returnNumber - currentNode.Item.Range.StartValue + 1);
                currentNode.Item.Range.StartValue = returnNumber + 1;
                WeightBalancedTreeNode <RangeAndSubtreeCounts> upperNode = null;
                if (currentNode.LeftSubtreeSize > currentNode.RightSubtreeSize)
                {
                    // 'Push' the current node down right
                    if (currentNode == rangeTree.RootNode)
                    {
                        rangeTree.RootNode = newNode;
                    }
                    else
                    {
                        if (currentNode.ParentNode.LeftChildNode == currentNode)
                        {
                            currentNode.ParentNode.LeftChildNode = newNode;
                        }
                        else
                        {
                            currentNode.ParentNode.RightChildNode = newNode;
                        }
                        newNode.ParentNode = currentNode.ParentNode;
                    }
                    newNode.LeftChildNode = currentNode.LeftChildNode;
                    currentNode.LeftChildNode.ParentNode = newNode;
                    newNode.RightChildNode                 = currentNode;
                    newNode.LeftSubtreeSize                = currentNode.LeftSubtreeSize;
                    newNode.RightSubtreeSize               = 1 + currentNode.RightSubtreeSize;
                    newNode.Item.LeftSubtreeRangeCount     = currentNode.Item.LeftSubtreeRangeCount;;
                    newNode.Item.RightSubtreeRangeCount    = currentNode.Item.Range.Length + currentNode.Item.RightSubtreeRangeCount;
                    currentNode.ParentNode                 = newNode;
                    currentNode.LeftChildNode              = null;
                    currentNode.LeftSubtreeSize            = 0;
                    currentNode.Item.LeftSubtreeRangeCount = 0;
                    upperNode = newNode;
                }
                else
                {
                    // 'Push' the new node down left
                    newNode.LeftChildNode               = currentNode.LeftChildNode;
                    newNode.RightChildNode              = null;
                    newNode.LeftSubtreeSize             = currentNode.LeftSubtreeSize;
                    newNode.RightSubtreeSize            = 0;
                    newNode.Item.LeftSubtreeRangeCount  = currentNode.Item.LeftSubtreeRangeCount;
                    newNode.Item.RightSubtreeRangeCount = 0;
                    newNode.ParentNode = currentNode;
                    if (newNode.LeftChildNode != null)
                    {
                        newNode.LeftChildNode.ParentNode = newNode;
                    }
                    currentNode.LeftChildNode = newNode;
                    currentNode.LeftSubtreeSize++;
                    currentNode.Item.LeftSubtreeRangeCount += newNode.Item.Range.Length;
                    upperNode = currentNode;
                }

                // Update the subtree sizes
                Action <WeightBalancedTreeNode <RangeAndSubtreeCounts>, Nullable <Boolean> > incrementSubtreeSizeAction = (node, nodeTraversedToFromLeft) =>
                {
                    if (nodeTraversedToFromLeft.HasValue)
                    {
                        if (nodeTraversedToFromLeft.Value == true)
                        {
                            node.LeftSubtreeSize++;
                        }
                        else
                        {
                            node.RightSubtreeSize++;
                        }
                    }
                };
                rangeTree.TraverseUpFromNode(upperNode, incrementSubtreeSizeAction);
                // Balance the tree
                rangeTree.BalanceTreeUpFromNode(upperNode);
            }

            numbersGeneratedCount++;

            return(returnNumber);
        }