/// <summary>
        /// Optimize transactions on the provided exchange. This will execute purchases via the exchange methods provided, after
        ///     finding an available trade which can increase utility.
        /// </summary>
        /// <returns>a summary of all exchanges made during the optimization</returns>
        public IList <(ExchangeResult <Resource>?, PurchaseOperationResult <Resource>)> Optimize()
        {
            var transactionLedger = new List <(ExchangeResult <Resource>?, PurchaseOperationResult <Resource>)>();
            var purchase          = PurchaseResult.Purchase(this, selfInventory, otherInventory);

            if (purchase?.ledger.exchages.Count > 0)
            {
                transactionLedger.Add((null, purchase.ledger));
            }

            var iterations       = 0;
            var executesPurchase = true;

            for (var minUtility = GetHighestSellableValuePerUtility(increment, selfInventory, otherInventory);
                 !EqualityComparer <Resource> .Default.Equals(minUtility, default) && executesPurchase;
                 minUtility = GetHighestSellableValuePerUtility(increment, selfInventory, otherInventory))
            {
                var nextTransaction = SellUntilPurchaseCanHappen(minUtility);

                if (!nextTransaction.HasValue)
                {
                    break;
                }
                transactionLedger.Add(nextTransaction.Value);
                iterations++;
                if (iterations > 1000)
                {
                    throw new Exception("Attempted to optimize over too many iterations, broke to safegaurd against infinite loop");
                }
            }

            return(transactionLedger);
        }
        /// <summary>
        /// Searches thought the inventories for the first possible purchase to execute, attempting to sell more of the resource of minimum utility
        ///     bit by bit until a purchase is possible
        ///
        /// There is a fundamental problem with this in terms of optimization: This only looks for the -first- purchase that can happen: not the -best-
        ///     purchase. finding the best purchase option would require evaluating all possible selling amounts, or
        ///     using a completely different search strategy
        /// To make this work well enough for resource sets with just a couple possible options, this function will never
        ///     return an exchange in which it purchases the same items as it has sold
        /// </summary>
        /// <param name="minUtility"></param>
        /// <returns></returns>
        private (ExchangeResult <Resource>, PurchaseResult)? GetFirstPossiblePurchase(Resource minUtility)
        {
            PurchaseResult purchaseOption = null;
            ActionOption <ExchangeResult <Resource> > sellOption = null;
            float purchaseAmount = 0;
            int   iterations     = 0;

            while (purchaseOption == null || purchaseOption.ledger.exchages.Count() <= 0)
            {
                var simSelfInventory  = CloneSelfInventory();
                var simOtherInventory = CloneOtherInventory();
                purchaseAmount += increment;

                // Execute all operations on a simulated inventory to make sure all prices, utilities,
                //  and any constraints on size are respected
                sellOption = exchange.Sell(minUtility, purchaseAmount, simSelfInventory, simOtherInventory);
                // If the sold amount is less than the requested amount by more than increment
                //  this means that we aren't going to gain anything at all
                if (sellOption.info.amount <= (purchaseAmount - increment))
                {
                    return(null);
                }
                sellOption.Execute();

                purchaseOption = PurchaseResult.Purchase(this, simSelfInventory, simOtherInventory, new[] { minUtility });

                iterations++;
                if (iterations > 1000)
                {
                    throw new Exception("Attempted to find purchase option over too many iterations, broke to safegaurd against infinite loop");
                }
            }
            return(sellOption.info, purchaseOption);
        }