/// <summary> /// Distributes the minimal amount to the specified result. /// </summary> /// <remarks>Immutable operation: creates other instance of allocation.</remarks> internal protected Allocation apply(Allocation allocation, int index) { Money[] results = allocation .Select((m, i) => i != index ? m : m + m.MinValue) .ToArray(); return(new Allocation(allocation.Allocatable, results)); }
private static Allocation allocateRemainderIfNeeded(this Money money, IRemainderAllocator allocator, Allocation allocatedSoFar) { Money remainder = money - allocatedSoFar.TotalAllocated; Allocation beingAllocated = allocatedSoFar; if (remainder >= remainder.MinValue) { beingAllocated = allocator.Allocate(allocatedSoFar); } return(beingAllocated); }
/// <summary> /// Allocates the sum of money 'fairly', discarding the remainder of an uneven allocation. /// </summary> /// <remarks> /// <para> /// A sum of money that can be allocated to each recipient exactly evenly is inherently 'fair'. For example, a US /// Dollar split four (4) ways leaves each recipient with 25 cents and everything is allocated.</para> /// <para> /// A US Dollar split three (3) ways cannot be distributed evenly and is therefore inherently 'unfair'. What will be done /// is allocate the maximum fair amount and leave the remainding amount for the caller to decide.</para> /// </remarks> /// <param name="numberOfRecipients">The number of times to split up the total.</param> /// <returns> /// The results of the even allocation with a length equal to <paramref name="numberOfRecipients"/>. /// <para>In the case of an even allocation, the allocation will be complete.<see cref="Allocation.IsComplete"/>, having a zero <see cref="Allocation.Remainder"/>.</para> /// <para>In the case of an uneven allocation, the allocation will not be complete <see cref="EvenAllocator(Money)"/>, having a non-zero <see cref="Allocation.Remainder"/>.</para> /// </returns> /// <seealso cref="EvenAllocator(Money)"/> public Allocation Allocate(int numberOfRecipients) { // if amount to allocate is too 'scarce' to allocate something to all // then effectively go into remainder allocation mode if (notEnoughToAllocateEvenly(numberOfRecipients)) { return(Allocation.Zero(_toAllocate, numberOfRecipients)); } decimal each = amountforEachRecipient(numberOfRecipients); Money[] results = Money.Some(each, _currency, numberOfRecipients); return(new Allocation(_toAllocate, results)); }
public static Allocation Allocate(this Money money, int numberOfRecipients, IRemainderAllocator allocator) { EvenAllocator.AssertNumberOfRecipients(nameof(numberOfRecipients), numberOfRecipients); if (money.notEnoughToAllocate()) { return(Allocation.Zero(money, numberOfRecipients)); } Allocation allocated = new EvenAllocator(money) .Allocate(numberOfRecipients); allocated = money.allocateRemainderIfNeeded(allocator, allocated); return(allocated); }
public static Allocation Allocate(this Money money, RatioCollection ratios, IRemainderAllocator allocator) { Guard.AgainstNullArgument(nameof(ratios), ratios); if (money.notEnoughToAllocate()) { return(Allocation.Zero(money, ratios.Count)); } Allocation allocated = new ProRataAllocator(money) .Allocate(ratios); allocated = money.allocateRemainderIfNeeded(allocator, allocated); return(allocated); }
public override Allocation Allocate(Allocation allocatedSoFar) { var rnd = new Random(); // the number of recipients that can get an incremental share Money remainder = allocatedSoFar.Remainder; var numberOfRecipients = (int)(remainder.Amount / remainder.MinValue.Amount); // make a list of all indexes, order it randomly // and take the number of indexes we need var indexes = Enumerable .Range(0, allocatedSoFar.Length) .OrderBy(x => rnd.Next()) .Take(numberOfRecipients); return(indexes.Aggregate(allocatedSoFar, apply)); }
public abstract Allocation Allocate(Allocation allocatedSoFar);