//------------------------------------------------------------------------------
/*
    Copyright (c) 2011-2013, OpenCoin, Inc.
*/
//==============================================================================

// TODO:
// - Do automatic bridging via XRP.
//
// OPTIMIZE: When calculating path increment, note if increment consumes all liquidity. No need to revisit path in the future if
// all liquidity is used.
//

SETUP_LOG (RippleCalc)

// If needed, advance to next funded offer.
// - Automatically advances to first offer.
// --> bEntryAdvance: true, to advance to next entry. false, recalculate.
// <-- uOfferIndex : 0=end of list.
TER RippleCalc::calcNodeAdvance (
    const unsigned int          uNode,              // 0 < uNode < uLast
    PathState&                  psCur,
    const bool                  bMultiQuality,
    const bool                  bReverse)
{
    PathState::Node&    pnPrv       = psCur.vpnNodes[uNode - 1];
    PathState::Node&    pnCur       = psCur.vpnNodes[uNode];

    const uint160&  uPrvCurrencyID  = pnPrv.uCurrencyID;
    const uint160&  uPrvIssuerID    = pnPrv.uIssuerID;
    const uint160&  uCurCurrencyID  = pnCur.uCurrencyID;
    const uint160&  uCurIssuerID    = pnCur.uIssuerID;

    uint256&        uDirectTip      = pnCur.uDirectTip;
    uint256&        uDirectEnd      = pnCur.uDirectEnd;
    bool&           bDirectAdvance  = pnCur.bDirectAdvance;
    SLE::pointer&   sleDirectDir    = pnCur.sleDirectDir;
    STAmount&       saOfrRate       = pnCur.saOfrRate;

    bool&           bEntryAdvance   = pnCur.bEntryAdvance;
    unsigned int&   uEntry          = pnCur.uEntry;
    uint256&        uOfferIndex     = pnCur.uOfferIndex;
    SLE::pointer&   sleOffer        = pnCur.sleOffer;
    uint160&        uOfrOwnerID     = pnCur.uOfrOwnerID;
    STAmount&       saOfferFunds    = pnCur.saOfferFunds;
    STAmount&       saTakerPays     = pnCur.saTakerPays;
    STAmount&       saTakerGets     = pnCur.saTakerGets;
    bool&           bFundsDirty     = pnCur.bFundsDirty;

    TER             terResult       = tesSUCCESS;

    WriteLog (lsDEBUG, RippleCalc) << "calcNodeAdvance: TakerPays:" << saTakerPays << " TakerGets:" << saTakerGets;

    int loopCount = 0;

    do
    {
        if (++loopCount > 20)
        {
            WriteLog (lsWARNING, RippleCalc) << "Loop count exceeded";
            return tefEXCEPTION;
        }

        bool    bDirectDirDirty = false;

        if (!uDirectTip)
        {
            // Need to initialize current node.

            uDirectTip      = Ledger::getBookBase (uPrvCurrencyID, uPrvIssuerID, uCurCurrencyID, uCurIssuerID);
            uDirectEnd      = Ledger::getQualityNext (uDirectTip);

            sleDirectDir    = lesActive.entryCache (ltDIR_NODE, uDirectTip);
            bDirectDirDirty = !!sleDirectDir;   // Associated vars are dirty, if found it.
            bDirectAdvance  = !sleDirectDir;    // Advance, if didn't find it. Normal not to be unable to lookup firstdirectory. Maybe even skip this lookup.

            WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: Initialize node: uDirectTip=%s uDirectEnd=%s bDirectAdvance=%d") % uDirectTip % uDirectEnd % bDirectAdvance);
        }

        if (bDirectAdvance)
        {
            // Get next quality.
            uDirectTip      = lesActive.getNextLedgerIndex (uDirectTip, uDirectEnd);

            bDirectDirDirty = true;
            bDirectAdvance  = false;

            if (!!uDirectTip)
            {
                // Have another quality directory.
                WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: Quality advance: uDirectTip=%s") % uDirectTip);

                sleDirectDir    = lesActive.entryCache (ltDIR_NODE, uDirectTip);
            }
            else if (bReverse)
            {
                WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: No more offers.";

                uOfferIndex = 0;
                break;
            }
            else
            {
                // No more offers. Should be done rather than fall off end of book.
                WriteLog (lsWARNING, RippleCalc) << "calcNodeAdvance: Unreachable: Fell off end of order book.";
                return mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING; // FIXME
                assert (false);

                terResult   = tefEXCEPTION;
            }
        }

        if (bDirectDirDirty)
        {
            saOfrRate       = STAmount::setRate (Ledger::getQuality (uDirectTip));  // For correct ratio
            uEntry          = 0;
            bEntryAdvance   = true;

            WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: directory dirty: saOfrRate=%s") % saOfrRate);
        }

        if (!bEntryAdvance)
        {
            if (bFundsDirty)
            {
                // We were called again probably merely to update structure variables.
                saTakerPays     = sleOffer->getFieldAmount (sfTakerPays);
                saTakerGets     = sleOffer->getFieldAmount (sfTakerGets);

                saOfferFunds    = lesActive.accountFunds (uOfrOwnerID, saTakerGets); // Funds left.
                bFundsDirty     = false;

                WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: funds dirty: saOfrRate=%s") % saOfrRate);
            }
            else
            {
                WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: as is"));
                nothing ();
            }
        }
        else if (!lesActive.dirNext (uDirectTip, sleDirectDir, uEntry, uOfferIndex))
        {
            // Failed to find an entry in directory.

            // Do another cur directory iff bMultiQuality
            if (bMultiQuality)
            {
                // We are allowed to process multiple qualities if this is the only path.
                WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: next quality"));

                bDirectAdvance  = true;         // Process next quality.
            }
            else if (!bReverse)
            {
                WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: unreachable: ran out of offers"));
                return mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING; // TEMPORARY
                assert (false);     // Can't run out of offers in forward direction.
                terResult       = tefEXCEPTION;
            }
            else
            {
                // Ran off end of offers.

                bEntryAdvance   = false;        // Done.
                uOfferIndex     = 0;            // Report nore more entries.
            }
        }
        else
        {
            // Got a new offer.
            sleOffer    = lesActive.entryCache (ltOFFER, uOfferIndex);
            uOfrOwnerID = sleOffer->getFieldAccount160 (sfAccount);
            saTakerPays = sleOffer->getFieldAmount (sfTakerPays);
            saTakerGets = sleOffer->getFieldAmount (sfTakerGets);

            const aciSource         asLine              = boost::make_tuple (uOfrOwnerID, uCurCurrencyID, uCurIssuerID);

            WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: uOfrOwnerID=%s saTakerPays=%s saTakerGets=%s uOfferIndex=%s")
                                           % RippleAddress::createHumanAccountID (uOfrOwnerID)
                                           % saTakerPays
                                           % saTakerGets
                                           % uOfferIndex);

            if (sleOffer->isFieldPresent (sfExpiration) && sleOffer->getFieldU32 (sfExpiration) <= lesActive.getLedger ()->getParentCloseTimeNC ())
            {
                // Offer is expired.
                WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: expired offer";

                if (bReverse)
                    musUnfundedFound.insert(uOfferIndex);
                else // it will already be deleted
                    assert (musUnfundedFound.find (uOfferIndex) != musUnfundedFound.end ()); // Verify reverse found it too.
                continue;
            }
            else if (!saTakerPays.isPositive () || !saTakerGets.isPositive ())
            {
                // Offer has bad amounts. Offers should never have a bad amounts.

                if (bReverse)
                {
                    // Past internal error, offer had bad amounts.
                    WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: PAST INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s")
                                                     % saTakerPays % saTakerGets);

                    musUnfundedFound.insert (uOfferIndex);              // Mark offer for always deletion.
                    continue;
                }
                else if (musUnfundedFound.find (uOfferIndex) != musUnfundedFound.end ())
                {
                    // Past internal error, offer was found failed to place this in musUnfundedFound.
                    WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: PAST INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s")
                                                   % saTakerPays % saTakerGets);

                    // Just skip it. It will be deleted.
                    continue;
                }
                else
                {
                    // Reverse should have previously put bad offer in list.
                    // An internal error previously left a bad offer.
                    WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: INTERNAL ERROR: OFFER NON-POSITIVE: saTakerPays=%s saTakerGets=%s")
                                                     % saTakerPays % saTakerGets);

                    // Don't process at all, things are in an unexpected state for this transactions.
                    terResult       = tefEXCEPTION;
                }

                continue;
            }

            // Allowed to access source from this node?
            // XXX This can get called multiple times for same source in a row, caching result would be nice.
            // XXX Going forward could we fund something with a worse quality which was previously skipped? Might need to check
            //     quality.
            curIssuerNodeConstIterator  itForward       = psCur.umForward.find (asLine);
            const bool                  bFoundForward   = itForward != psCur.umForward.end ();

            // Only a allow a source to be used once, in the first node encountered from initial path scan.
            // This prevents conflicting uses of the same balance when going reverse vs forward.
            if (bFoundForward && (itForward->second != uNode) && (uOfrOwnerID != uCurIssuerID))
            {
                // Temporarily unfunded. Another node uses this source, ignore in this offer.
                WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: temporarily unfunded offer (forward)";
                continue;
            }

            // This is overly strict. For contributions to past. We should only count source if actually used.
            curIssuerNodeConstIterator  itReverse       = psCur.umReverse.find (asLine);
            bool                        bFoundReverse   = itReverse != psCur.umReverse.end ();

            // For this quality increment, only allow a source to be used from a single node, in the first node encountered from applying offers
            // in reverse.
            if (bFoundReverse && (itReverse->second != uNode) && (uOfrOwnerID != uCurIssuerID))
            {
                // Temporarily unfunded. Another node uses this source, ignore in this offer.
                WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: temporarily unfunded offer (reverse)";
                continue;
            }

            // Determine if used in past.
            // We only need to know if it might need to be marked unfunded.
            curIssuerNodeConstIterator  itPast          = mumSource.find (asLine);
            bool                        bFoundPast      = itPast != mumSource.end ();

            // Only the current node is allowed to use the source.

            saOfferFunds    = lesActive.accountFunds (uOfrOwnerID, saTakerGets); // Funds held.

            if (!saOfferFunds.isPositive ())
            {
                // Offer is unfunded.
                WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: unfunded offer";

                if (bReverse && !bFoundReverse && !bFoundPast)
                {
                    // Never mentioned before, clearly just: found unfunded.
                    // That is, even if this offer fails due to fill or kill still do deletions.
                    musUnfundedFound.insert (uOfferIndex);              // Mark offer for always deletion.
                }
                else
                {
                    // Moving forward, don't need to insert again
                    // Or, already found it.
                }

                // YYY Could verify offer is correct place for unfundeds.
                continue;
            }

            if (bReverse            // Need to remember reverse mention.
                    && !bFoundPast      // Not mentioned in previous passes.
                    && !bFoundReverse)  // New to pass.
            {
                // Consider source mentioned by current path state.
                WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: remember=%s/%s/%s")
                                               % RippleAddress::createHumanAccountID (uOfrOwnerID)
                                               % STAmount::createHumanCurrency (uCurCurrencyID)
                                               % RippleAddress::createHumanAccountID (uCurIssuerID));

                psCur.umReverse.insert (std::make_pair (asLine, uNode));
            }

            bFundsDirty     = false;
            bEntryAdvance   = false;
        }
    }
    while (tesSUCCESS == terResult && (bEntryAdvance || bDirectAdvance));

    if (tesSUCCESS == terResult)
    {
        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: uOfferIndex=%s") % uOfferIndex);
    }
    else
    {
        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAdvance: terResult=%s") % transToken (terResult));
    }

    return terResult;
}

// At the right most node of a list of consecutive offer nodes, given the amount requested to be delivered, push toward node 0 the
// amount requested for previous nodes to know how much to deliver.
//
// Between offer nodes, the fee charged may vary.  Therefore, process one inbound offer at a time.  Propagate the inbound offer's
// requirements to the previous node.  The previous node adjusts the amount output and the amount spent on fees.  Continue
// processing until the request is satisified as long as the rate does not increase past the initial rate.
TER RippleCalc::calcNodeDeliverRev (
    const unsigned int          uNode,          // 0 < uNode < uLast
    PathState&                  psCur,
    const bool                  bMultiQuality,  // True, if not constrained to do the same or better quality.
    const uint160&              uOutAccountID,  // --> Output owner's account.
    const STAmount&             saOutReq,       // --> Funds requested to be delivered for an increment.
    STAmount&                   saOutAct)       // <-- Funds actually delivered for an increment.
{
    TER terResult   = tesSUCCESS;

    PathState::Node&    pnPrv       = psCur.vpnNodes[uNode - 1];
    PathState::Node&    pnCur       = psCur.vpnNodes[uNode];

    const uint160&  uCurIssuerID    = pnCur.uIssuerID;
    const uint160&  uPrvAccountID   = pnPrv.uAccountID;
    const STAmount& saTransferRate  = pnCur.saTransferRate; // Transfer rate of the TakerGets issuer.

    STAmount&       saPrvDlvReq     = pnPrv.saRevDeliver;   // Accumulation of what the previous node must deliver.

    uint256&        uDirectTip      = pnCur.uDirectTip;

    uDirectTip      = 0;                                    // Restart book searching.

    // YYY Note this gets zeroed on each increment, ideally only on first increment, then it could be a limit on the forward pass.
    saOutAct.zero (saOutReq);

    WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev> saOutAct=%s saOutReq=%s saPrvDlvReq=%s")
                                  % saOutAct
                                  % saOutReq
                                  % saPrvDlvReq);

    assert (!!saOutReq);

    int loopCount = 0;

    while (saOutAct < saOutReq)                             // Did not deliver as much as requested.
    {
        // VFALCO TODO Why 40? Give this magic constant a name and document it
        //        NOTE is the number 40 part of protocol?
        //
        if (++loopCount > 40)
        {
            WriteLog (lsFATAL, RippleCalc) << "loop count exceeded";
            return mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
        }

        bool&           bEntryAdvance   = pnCur.bEntryAdvance;
        STAmount&       saOfrRate       = pnCur.saOfrRate;
        uint256&        uOfferIndex     = pnCur.uOfferIndex;
        SLE::pointer&   sleOffer        = pnCur.sleOffer;
        const uint160&  uOfrOwnerID     = pnCur.uOfrOwnerID;
        bool&           bFundsDirty     = pnCur.bFundsDirty;
        STAmount&       saOfferFunds    = pnCur.saOfferFunds;
        STAmount&       saTakerPays     = pnCur.saTakerPays;
        STAmount&       saTakerGets     = pnCur.saTakerGets;
        STAmount&       saRateMax       = pnCur.saRateMax;

        terResult   = calcNodeAdvance (uNode, psCur, bMultiQuality || saOutAct.isZero(), true);      // If needed, advance to next funded offer.

        if (tesSUCCESS != terResult || !uOfferIndex)
        {
            // Error or out of offers.
            break;
        }

        const STAmount  saOutFeeRate    = uOfrOwnerID == uCurIssuerID || uOutAccountID == uCurIssuerID // Issuer sending or receiving.
                                          ? saOne             // No fee.
                                          : saTransferRate;   // Transfer rate of issuer.
        WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: uOfrOwnerID=%s uOutAccountID=%s uCurIssuerID=%s saTransferRate=%s saOutFeeRate=%s")
                                      % RippleAddress::createHumanAccountID (uOfrOwnerID)
                                      % RippleAddress::createHumanAccountID (uOutAccountID)
                                      % RippleAddress::createHumanAccountID (uCurIssuerID)
                                      % saTransferRate
                                      % saOutFeeRate);

        if (bMultiQuality)
        {
            // In multi-quality mode, ignore rate.

            nothing ();
        }
        else if (!saRateMax)
        {
            // Set initial rate.
            saRateMax   = saOutFeeRate;

            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: Set initial rate: saRateMax=%s saOutFeeRate=%s")
                                          % saRateMax
                                          % saOutFeeRate);
        }
        else if (saOutFeeRate > saRateMax)
        {
            // Offer exceeds initial rate.
            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: Offer exceeds initial rate: saRateMax=%s saOutFeeRate=%s")
                                          % saRateMax
                                          % saOutFeeRate);

            break;  // Done. Don't bother looking for smaller saTransferRates.
        }
        else if (saOutFeeRate < saRateMax)
        {
            // Reducing rate. Additional offers will only considered for this increment if they are at least this good.
            // At this point, the overall rate is reducing, while the overall rate is not saOutFeeRate, it would be wrong to add
            // anthing with a rate above saOutFeeRate.
            // The rate would be reduced if the current offer was from the issuer and the previous offer wasn't.

            saRateMax   = saOutFeeRate;

            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: Reducing rate: saRateMax=%s")
                                          % saRateMax);
        }

        // Amount that goes to the taker.
        STAmount    saOutPassReq    = std::min (std::min (saOfferFunds, saTakerGets), saOutReq - saOutAct); // Maximum out - assuming no out fees.
        STAmount    saOutPassAct    = saOutPassReq;

        // Amount charged to the offer owner.
        // The fee goes to issuer. The fee is paid by offer owner and not passed as a cost to taker.
        // Round down: prefer liquidity rather than microscopic fees.
        STAmount    saOutPlusFees   = STAmount::mulRound (saOutPassAct, saOutFeeRate, false);                       // Offer out with fees.

        WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: saOutReq=%s saOutAct=%s saTakerGets=%s saOutPassAct=%s saOutPlusFees=%s saOfferFunds=%s")
                                      % saOutReq
                                      % saOutAct
                                      % saTakerGets
                                      % saOutPassAct
                                      % saOutPlusFees
                                      % saOfferFunds);

        if (saOutPlusFees > saOfferFunds)
        {
            // Offer owner can not cover all fees, compute saOutPassAct based on saOfferFunds.

            saOutPlusFees   = saOfferFunds;
            // Round up: prefer liquidity rather than microscopic fees. But, limit by requested.
            saOutPassAct    = std::min (saOutPassReq, STAmount::divRound (saOutPlusFees, saOutFeeRate, true));

            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: Total exceeds fees: saOutPassAct=%s saOutPlusFees=%s saOfferFunds=%s")
                                          % saOutPassAct
                                          % saOutPlusFees
                                          % saOfferFunds);
        }

        // Compute portion of input needed to cover actual output.

        STAmount    saInPassReq = STAmount::mulRound (saOutPassAct, saOfrRate, saTakerPays, true);
        STAmount    saInPassAct;

        WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: saInPassReq=%s saOfrRate=%s saOutPassAct=%s saOutPlusFees=%s")
                                      % saInPassReq
                                      % saOfrRate
                                      % saOutPassAct
                                      % saOutPlusFees);

        if (saInPassReq > saTakerPays)
            saInPassReq = saTakerPays;

        if (!saInPassReq) // FIXME: This is bogus
        {
            // After rounding did not want anything.
            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: micro offer is unfunded."));

            bEntryAdvance   = true;
            continue;
        }
        // Find out input amount actually available at current rate.
        else if (!!uPrvAccountID)
        {
            // account --> OFFER --> ?
            // Due to node expansion, previous is guaranteed to be the issuer.
            // Previous is the issuer and receiver is an offer, so no fee or quality.
            // Previous is the issuer and has unlimited funds.
            // Offer owner is obtaining IOUs via an offer, so credit line limits are ignored.
            // As limits are ignored, don't need to adjust previous account's balance.

            saInPassAct = saInPassReq;

            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: account --> OFFER --> ? : saInPassAct=%s")
                                          % saInPassAct);
        }
        else
        {
            // offer --> OFFER --> ?
            // Compute in previous offer node how much could come in.

            terResult   = calcNodeDeliverRev (
                              uNode - 1,
                              psCur,
                              bMultiQuality,
                              uOfrOwnerID,
                              saInPassReq,
                              saInPassAct);

            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: offer --> OFFER --> ? : saInPassAct=%s")
                                          % saInPassAct);
        }

        if (tesSUCCESS != terResult)
            break;

        if (saInPassAct < saInPassReq)
        {
            // Adjust output to conform to limited input.
            saOutPassAct    = std::min (saOutPassReq, STAmount::divRound (saInPassAct, saOfrRate, saTakerGets, true));
            saOutPlusFees   = std::min (saOfferFunds, STAmount::mulRound (saOutPassAct, saOutFeeRate, true));

            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: adjusted: saOutPassAct=%s saOutPlusFees=%s")
                                          % saOutPassAct
                                          % saOutPlusFees);
        }
        else
        {
            assert (saInPassAct == saInPassReq);
        }

        // Funds were spent.
        bFundsDirty     = true;

        // Want to deduct output to limit calculations while computing reverse.  Don't actually need to send.
        // Sending could be complicated: could fund a previous offer not yet visited.
        // However, these deductions and adjustments are tenative.
        // Must reset balances when going forward to perform actual transfers.
        terResult   = lesActive.accountSend (uOfrOwnerID, uCurIssuerID, saOutPassAct);

        if (tesSUCCESS != terResult)
            break;

        // Adjust offer
        STAmount    saTakerGetsNew  = saTakerGets - saOutPassAct;
        STAmount    saTakerPaysNew  = saTakerPays - saInPassAct;

        if (saTakerPaysNew.isNegative () || saTakerGetsNew.isNegative ())
        {
            WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: NEGATIVE: saTakerPaysNew=%s saTakerGetsNew=%s")
                                             % saTakerPaysNew % saTakerGetsNew);

            terResult   = mOpenLedger
                          ? telFAILED_PROCESSING                              // Ledger is not final, can vote no.
                          : tecFAILED_PROCESSING;
            break;
        }

        sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew);
        sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew);

        lesActive.entryModify (sleOffer);

        if (saOutPassAct == saTakerGets)
        {
            // Offer became unfunded.
            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev: offer became unfunded."));

            bEntryAdvance   = true;     // XXX When don't we want to set advance?
        }
        else
        {
            assert (saOutPassAct < saTakerGets);
        }

        saOutAct    += saOutPassAct;
        saPrvDlvReq += saInPassAct;     // Accumulate what is to be delivered from previous node.
    }

    CondLog (saOutAct > saOutReq, lsWARNING, RippleCalc)
            << boost::str (boost::format ("calcNodeDeliverRev: TOO MUCH: saOutAct=%s saOutReq=%s")
                           % saOutAct
                           % saOutReq);

    assert (saOutAct <= saOutReq);

    // XXX Perhaps need to check if partial is okay to relax this?
    if (tesSUCCESS == terResult && !saOutAct)
        terResult   = tecPATH_DRY;                                              // Unable to meet request, consider path dry.

    WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverRev< saOutAct=%s saOutReq=%s saPrvDlvReq=%s")
                                  % saOutAct
                                  % saOutReq
                                  % saPrvDlvReq);

    return terResult;
}

// For current offer, get input from deliver/limbo and output to next account or deliver for next offers.
// <-- pnCur.saFwdDeliver: For calcNodeAccountFwd to know how much went through
// --> pnCur.saRevDeliver: Do not exceed.
TER RippleCalc::calcNodeDeliverFwd (
    const unsigned int          uNode,          // 0 < uNode < uLast
    PathState&                  psCur,
    const bool                  bMultiQuality,
    const uint160&              uInAccountID,   // --> Input owner's account.
    const STAmount&             saInReq,        // --> Amount to deliver.
    STAmount&                   saInAct,        // <-- Amount delivered, this invokation.
    STAmount&                   saInFees)       // <-- Fees charged, this invokation.
{
    TER terResult   = tesSUCCESS;

    PathState::Node&    pnPrv       = psCur.vpnNodes[uNode - 1];
    PathState::Node&    pnCur       = psCur.vpnNodes[uNode];
    PathState::Node&    pnNxt       = psCur.vpnNodes[uNode + 1];

    const uint160&  uNxtAccountID   = pnNxt.uAccountID;
    const uint160&  uCurCurrencyID  = pnCur.uCurrencyID;
    const uint160&  uCurIssuerID    = pnCur.uIssuerID;
    uint256 const&  uOfferIndex     = pnCur.uOfferIndex;
    const uint160&  uPrvCurrencyID  = pnPrv.uCurrencyID;
    const uint160&  uPrvIssuerID    = pnPrv.uIssuerID;
    const STAmount& saInTransRate   = pnPrv.saTransferRate;
    const STAmount& saCurDeliverMax = pnCur.saRevDeliver;   // Don't deliver more than wanted.

    STAmount&       saCurDeliverAct = pnCur.saFwdDeliver;   // Zeroed in reverse pass.

    uint256&        uDirectTip      = pnCur.uDirectTip;

    uDirectTip      = 0;                        // Restart book searching.

    saInAct.zero (saInReq);
    saInFees.zero (saInReq);

    int loopCount = 0;

    // XXX Perhaps make sure do not exceed saCurDeliverMax as another way to stop.
    while (tesSUCCESS == terResult
            && saInAct + saInFees < saInReq)        // Did not spend all inbound deliver funds.
    {
        // VFALCO TODO Why 40?
        if (++loopCount > 40)
        {
            WriteLog (lsWARNING, RippleCalc) << "calcNodeDeliverFwd: max loops cndf";
            return mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
        }

        // Determine values for pass to adjust saInAct, saInFees, and saCurDeliverAct
        terResult   = calcNodeAdvance (uNode, psCur, bMultiQuality || saInAct.isZero(), false);             // If needed, advance to next funded offer.

        if (tesSUCCESS != terResult)
        {
            nothing ();
        }
        else if (!uOfferIndex)
        {
            WriteLog (lsWARNING, RippleCalc) << "calcNodeDeliverFwd: INTERNAL ERROR: Ran out of offers.";
            return mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
        }
        else if (tesSUCCESS == terResult)
        {
            // Doesn't charge input. Input funds are in limbo.
            bool&           bEntryAdvance   = pnCur.bEntryAdvance;
            STAmount&       saOfrRate       = pnCur.saOfrRate;
            uint256&        uOfferIndex     = pnCur.uOfferIndex;
            SLE::pointer&   sleOffer        = pnCur.sleOffer;
            const uint160&  uOfrOwnerID     = pnCur.uOfrOwnerID;
            bool&           bFundsDirty     = pnCur.bFundsDirty;
            STAmount&       saOfferFunds    = pnCur.saOfferFunds;
            STAmount&       saTakerPays     = pnCur.saTakerPays;
            STAmount&       saTakerGets     = pnCur.saTakerGets;

            const STAmount  saInFeeRate     = !uPrvCurrencyID                   // XRP.
                                              || uInAccountID == uPrvIssuerID // Sender is issuer.
                                              || uOfrOwnerID == uPrvIssuerID  // Reciever is issuer.
                                              ? saOne                     // No fee.
                                              : saInTransRate;            // Transfer rate of issuer.

            // First calculate assuming no output fees: saInPassAct, saInPassFees, saOutPassAct

            STAmount    saOutFunded     = std::min (saOfferFunds, saTakerGets);                             // Offer maximum out - limited by funds with out fees.
            STAmount    saOutPassFunded = std::min (saOutFunded, saCurDeliverMax - saCurDeliverAct);        // Offer maximum out - limit by most to deliver.
            STAmount    saInFunded      = STAmount::mulRound (saOutPassFunded, saOfrRate, saTakerPays, true); // Offer maximum in - Limited by by payout.
            STAmount    saInTotal       = STAmount::mulRound (saInFunded, saInFeeRate, true);               // Offer maximum in with fees.
            STAmount    saInSum         = std::min (saInTotal, saInReq - saInAct - saInFees);               // In limited by remaining.
            STAmount    saInPassAct     = std::min (saTakerPays, STAmount::divRound (saInSum, saInFeeRate, true));     // In without fees.
            STAmount    saOutPassMax    = std::min (saOutPassFunded, STAmount::divRound (saInPassAct, saOfrRate, saTakerGets, true)); // Out limited by in remaining.

            STAmount    saInPassFeesMax = saInSum - saInPassAct;

            STAmount    saOutPassAct;   // Will be determined by next node.
            STAmount    saInPassFees;   // Will be determined by adjusted saInPassAct.


            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverFwd: uNode=%d saOutFunded=%s saOutPassFunded=%s saOfferFunds=%s saTakerGets=%s saInReq=%s saInAct=%s saInFees=%s saInFunded=%s saInTotal=%s saInSum=%s saInPassAct=%s saOutPassMax=%s")
                                          % uNode
                                          % saOutFunded
                                          % saOutPassFunded
                                          % saOfferFunds
                                          % saTakerGets
                                          % saInReq
                                          % saInAct
                                          % saInFees
                                          % saInFunded
                                          % saInTotal
                                          % saInSum
                                          % saInPassAct
                                          % saOutPassMax);

            if (!saTakerPays || !saInSum) // FIXME: We remove an offer if WE didn't want anything out of it?
            {
                WriteLog (lsINFO, RippleCalc) << "calcNodeDeliverFwd: Microscopic offer unfunded.";

                // After math offer is effectively unfunded.
                psCur.vUnfundedBecame.push_back (uOfferIndex);
                bEntryAdvance   = true;
                continue;
            }
            else if (!saInFunded)
            {
                // Previous check should catch this.
                WriteLog (lsWARNING, RippleCalc) << "calcNodeDeliverFwd: UNREACHABLE REACHED";

                // After math offer is effectively unfunded.
                psCur.vUnfundedBecame.push_back (uOfferIndex);
                bEntryAdvance   = true;
                continue;
            }
            else if (!!uNxtAccountID)
            {
                // ? --> OFFER --> account
                // Input fees: vary based upon the consumed offer's owner.
                // Output fees: none as XRP or the destination account is the issuer.

                saOutPassAct    = saOutPassMax;
                saInPassFees    = saInPassFeesMax;

                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeDeliverFwd: ? --> OFFER --> account: uOfrOwnerID=%s uNxtAccountID=%s saOutPassAct=%s saOutFunded=%s")
                                               % RippleAddress::createHumanAccountID (uOfrOwnerID)
                                               % RippleAddress::createHumanAccountID (uNxtAccountID)
                                               % saOutPassAct
                                               % saOutFunded);

                // Output: Debit offer owner, send XRP or non-XPR to next account.
                terResult   = lesActive.accountSend (uOfrOwnerID, uNxtAccountID, saOutPassAct);

                if (tesSUCCESS != terResult)
                    break;
            }
            else
            {
                // ? --> OFFER --> offer
                // Offer to offer means current order book's output currency and issuer match next order book's input current and
                // issuer.
                // Output fees: possible if issuer has fees and is not on either side.
                STAmount    saOutPassFees;

                // Output fees vary as the next nodes offer owners may vary.
                // Therefore, immediately push through output for current offer.
                terResult   = RippleCalc::calcNodeDeliverFwd (
                                  uNode + 1,
                                  psCur,
                                  bMultiQuality,
                                  uOfrOwnerID,        // --> Current holder.
                                  saOutPassMax,       // --> Amount available.
                                  saOutPassAct,       // <-- Amount delivered.
                                  saOutPassFees);     // <-- Fees charged.

                if (tesSUCCESS != terResult)
                    break;

                if (saOutPassAct == saOutPassMax)
                {
                    // No fees and entire output amount.

                    saInPassFees    = saInPassFeesMax;
                }
                else
                {
                    // Fraction of output amount.
                    // Output fees are paid by offer owner and not passed to previous.

                    assert (saOutPassAct < saOutPassMax);

                    saInPassAct     = std::min (saTakerPays, STAmount::mulRound (saOutPassAct, saOfrRate, saInReq, true));
                    saInPassFees    = std::min (saInPassFeesMax, STAmount::mulRound (saInPassAct, saInFeeRate, true));
                }

                // Do outbound debiting.
                // Send to issuer/limbo total amount including fees (issuer gets fees).
                lesActive.accountSend (uOfrOwnerID, !!uCurCurrencyID ? uCurIssuerID : ACCOUNT_XRP, saOutPassAct + saOutPassFees);

                WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverFwd: ? --> OFFER --> offer: saOutPassAct=%s saOutPassFees=%s")
                                              % saOutPassAct
                                              % saOutPassFees);
            }

            WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeDeliverFwd: uNode=%d saTakerGets=%s saTakerPays=%s saInPassAct=%s saInPassFees=%s saOutPassAct=%s saOutFunded=%s")
                                          % uNode
                                          % saTakerGets
                                          % saTakerPays
                                          % saInPassAct
                                          % saInPassFees
                                          % saOutPassAct
                                          % saOutFunded);

            // Funds were spent.
            bFundsDirty     = true;

            // Do inbound crediting.
            // Credit offer owner from in issuer/limbo (input transfer fees left with owner).
            // Don't attempt to have someone credit themselves, it is redundant.
            if (!uPrvCurrencyID                         // Always credit XRP from limbo.
                    || uInAccountID != uOfrOwnerID)         // Never send non-XRP to the same account.
            {
                terResult       = lesActive.accountSend (!!uPrvCurrencyID ? uInAccountID : ACCOUNT_XRP, uOfrOwnerID, saInPassAct);

                if (tesSUCCESS != terResult)
                    break;
            }

            // Adjust offer
            // Fees are considered paid from a seperate budget and are not named in the offer.
            STAmount    saTakerGetsNew  = saTakerGets - saOutPassAct;
            STAmount    saTakerPaysNew  = saTakerPays - saInPassAct;

            if (saTakerPaysNew.isNegative () || saTakerGetsNew.isNegative ())
            {
                WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeDeliverFwd: NEGATIVE: saTakerPaysNew=%s saTakerGetsNew=%s")
                                                 % saTakerPaysNew % saTakerGetsNew);

                terResult   = mOpenLedger
                              ? telFAILED_PROCESSING                              // Ledger is not final, can vote no.
                              : tecFAILED_PROCESSING;
                break;
            }

            sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew);
            sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew);

            lesActive.entryModify (sleOffer);

            if ((saOutPassAct == saOutFunded) || saTakerGetsNew.isZero())
            {
                // Offer became unfunded.

                WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeDeliverFwd: unfunded: saOutPassAct=%s saOutFunded=%s")
                                                 % saOutPassAct % saOutFunded);

                psCur.vUnfundedBecame.push_back (uOfferIndex);
                bEntryAdvance   = true;
            }
            else
            {
                CondLog (saOutPassAct >= saOutFunded, lsWARNING, RippleCalc) << boost::str (boost::format ("calcNodeDeliverFwd: TOO MUCH: saOutPassAct=%s saOutFunded=%s")
                        % saOutPassAct % saOutFunded);

                assert (saOutPassAct < saOutFunded);
            }

            saInAct         += saInPassAct;
            saInFees        += saInPassFees;

            // Adjust amount available to next node.
            saCurDeliverAct = std::min (saCurDeliverMax, saCurDeliverAct + saOutPassAct);
        }
    }

    WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeDeliverFwd< uNode=%d saInAct=%s saInFees=%s")
                                   % uNode
                                   % saInAct
                                   % saInFees);

    return terResult;
}

// Called to drive from the last offer node in a chain.
TER RippleCalc::calcNodeOfferRev (
    const unsigned int          uNode,              // 0 < uNode < uLast
    PathState&                  psCur,
    const bool                  bMultiQuality)
{
    TER             terResult;

    PathState::Node& pnCur = psCur.vpnNodes [uNode];
    PathState::Node& pnNxt = psCur.vpnNodes [uNode + 1];

    if (!!pnNxt.uAccountID)
    {
        // Next is an account node, resolve current offer node's deliver.
        STAmount        saDeliverAct;

        WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeOfferRev: OFFER --> account: uNode=%d saRevDeliver=%s")
                                      % uNode
                                      % pnCur.saRevDeliver);

        terResult   = calcNodeDeliverRev (
                          uNode,
                          psCur,
                          bMultiQuality,

                          pnNxt.uAccountID,
                          pnCur.saRevDeliver, // The next node wants the current node to deliver this much.
                          saDeliverAct);
    }
    else
    {
        WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeOfferRev: OFFER --> offer: uNode=%d")
                                      % uNode);

        // Next is an offer. Deliver has already been resolved.
        terResult   = tesSUCCESS;
    }

    return terResult;
}

// Called to drive the from the first offer node in a chain.
// - Offer input is in issuer/limbo.
// - Current offers consumed.
//   - Current offer owners debited.
//   - Transfer fees credited to issuer.
//   - Payout to issuer or limbo.
// - Deliver is set without transfer fees.
TER RippleCalc::calcNodeOfferFwd (
    const unsigned int          uNode,              // 0 < uNode < uLast
    PathState&                  psCur,
    const bool                  bMultiQuality
)
{
    TER             terResult;
    PathState::Node& pnPrv = psCur.vpnNodes [uNode - 1];

    if (!!pnPrv.uAccountID)
    {
        // Previous is an account node, resolve its deliver.
        STAmount        saInAct;
        STAmount        saInFees;

        terResult   = calcNodeDeliverFwd (
                          uNode,
                          psCur,
                          bMultiQuality,
                          pnPrv.uAccountID,
                          pnPrv.saFwdDeliver, // Previous is sending this much.
                          saInAct,
                          saInFees);

        assert (tesSUCCESS != terResult || pnPrv.saFwdDeliver == saInAct + saInFees);
    }
    else
    {
        // Previous is an offer. Deliver has already been resolved.
        terResult   = tesSUCCESS;
    }

    return terResult;

}

// Compute how much might flow for the node for the pass. Does not actually adjust balances.
// uQualityIn -> uQualityOut
//   saPrvReq -> saCurReq
//   sqPrvAct -> saCurAct
// This is a minimizing routine: moving in reverse it propagates the send limit to the sender, moving forward it propagates the
// actual send toward the receiver.
// This routine works backwards:
// - cur is the driver: it calculates previous wants based on previous credit limits and current wants.
// This routine works forwards:
// - prv is the driver: it calculates current deliver based on previous delivery limits and current wants.
// This routine is called one or two times for a node in a pass. If called once, it will work and set a rate.  If called again,
// the new work must not worsen the previous rate.
void RippleCalc::calcNodeRipple (
    const uint32 uQualityIn,
    const uint32 uQualityOut,
    const STAmount& saPrvReq,   // --> in limit including fees, <0 = unlimited
    const STAmount& saCurReq,   // --> out limit (driver)
    STAmount& saPrvAct,         // <-> in limit including achieved so far: <-- <= -->
    STAmount& saCurAct,         // <-> out limit including achieved : <-- <= -->
    uint64& uRateMax)
{
    WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeRipple> uQualityIn=%d uQualityOut=%d saPrvReq=%s saCurReq=%s saPrvAct=%s saCurAct=%s")
                                   % uQualityIn
                                   % uQualityOut
                                   % saPrvReq
                                   % saCurReq
                                   % saPrvAct
                                   % saCurAct);

    assert (saCurReq.isPositive ()); // FIXME: saCurReq was zero
    assert (saPrvReq.getCurrency () == saCurReq.getCurrency ());
    assert (saPrvReq.getCurrency () == saPrvAct.getCurrency ());
    assert (saPrvReq.getIssuer () == saPrvAct.getIssuer ());

    const bool      bPrvUnlimited   = saPrvReq.isNegative ();
    const STAmount  saPrv           = bPrvUnlimited ? STAmount (saPrvReq) : saPrvReq - saPrvAct;
    const STAmount  saCur           = saCurReq - saCurAct;

#if 0
    WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeRipple: bPrvUnlimited=%d saPrv=%s saCur=%s")
                                  % bPrvUnlimited
                                  % saPrv
                                  % saCur);
#endif

    if (uQualityIn >= uQualityOut)
    {
        // No fee.
        WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeRipple: No fees"));

        // Only process if we are not worsing previously processed.
        if (!uRateMax || STAmount::uRateOne <= uRateMax)
        {
            // Limit amount to transfer if need.
            STAmount    saTransfer  = bPrvUnlimited ? saCur : std::min (saPrv, saCur);

            // In reverse, we want to propagate the limited cur to prv and set actual cur.
            // In forward, we want to propagate the limited prv to cur and set actual prv.
            saPrvAct    += saTransfer;
            saCurAct    += saTransfer;

            // If no rate limit, set rate limit to avoid combining with something with a worse rate.
            if (!uRateMax)
                uRateMax    = STAmount::uRateOne;
        }
    }
    else
    {
        // Fee.
        WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeRipple: Fee"));

        uint64  uRate   = STAmount::getRate (STAmount (uQualityOut), STAmount (uQualityIn));

        if (!uRateMax || uRate <= uRateMax)
        {
            const uint160   uCurrencyID     = saCur.getCurrency ();
            const uint160   uCurIssuerID    = saCur.getIssuer ();
            // const uint160    uPrvIssuerID    = saPrv.getIssuer();

            STAmount    saCurIn     = STAmount::divRound (STAmount::mulRound (saCur, uQualityOut, uCurrencyID, uCurIssuerID, true), uQualityIn, uCurrencyID, uCurIssuerID, true);

            WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeRipple: bPrvUnlimited=%d saPrv=%s saCurIn=%s") % bPrvUnlimited % saPrv % saCurIn);

            if (bPrvUnlimited || saCurIn <= saPrv)
            {
                // All of cur. Some amount of prv.
                saCurAct    += saCur;
                saPrvAct    += saCurIn;
                WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeRipple:3c: saCurReq=%s saPrvAct=%s") % saCurReq % saPrvAct);
            }
            else
            {
                // A part of cur. All of prv. (cur as driver)
                STAmount    saCurOut    = STAmount::divRound (STAmount::mulRound (saPrv, uQualityIn, uCurrencyID, uCurIssuerID, true), uQualityOut, uCurrencyID, uCurIssuerID, true);
                WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeRipple:4: saCurReq=%s") % saCurReq);

                saCurAct    += saCurOut;
                saPrvAct    = saPrvReq;

            }
            if (!uRateMax)
                uRateMax    = uRate;
        }
    }

    WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeRipple< uQualityIn=%d uQualityOut=%d saPrvReq=%s saCurReq=%s saPrvAct=%s saCurAct=%s")
                                   % uQualityIn
                                   % uQualityOut
                                   % saPrvReq
                                   % saCurReq
                                   % saPrvAct
                                   % saCurAct);
}

// Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur...
// Based on required deliverable, propagate redeem, issue, and deliver requests to the previous node.
// Inflate amount requested by required fees.
// Reedems are limited based on IOUs previous has on hand.
// Issues are limited based on credit limits and amount owed.
// No account balance adjustments as we don't know how much is going to actually be pushed through yet.
// <-- tesSUCCESS or tecPATH_DRY
TER RippleCalc::calcNodeAccountRev (const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
{
    TER                 terResult       = tesSUCCESS;
    const unsigned int  uLast           = psCur.vpnNodes.size () - 1;

    uint64              uRateMax        = 0;

    PathState::Node&        pnPrv       = psCur.vpnNodes[uNode ? uNode - 1 : 0];
    PathState::Node&        pnCur       = psCur.vpnNodes[uNode];
    PathState::Node&        pnNxt       = psCur.vpnNodes[uNode == uLast ? uLast : uNode + 1];

    // Current is allowed to redeem to next.
    const bool          bPrvAccount     = !uNode || isSetBit (pnPrv.uFlags, STPathElement::typeAccount);
    const bool          bNxtAccount     = uNode == uLast || isSetBit (pnNxt.uFlags, STPathElement::typeAccount);

    const uint160&      uCurAccountID   = pnCur.uAccountID;
    const uint160&      uPrvAccountID   = bPrvAccount ? pnPrv.uAccountID : uCurAccountID;
    const uint160&      uNxtAccountID   = bNxtAccount ? pnNxt.uAccountID : uCurAccountID;   // Offers are always issue.

    const uint160&      uCurrencyID     = pnCur.uCurrencyID;

    // XXX Don't look up quality for XRP
    const uint32        uQualityIn      = uNode ? lesActive.rippleQualityIn (uCurAccountID, uPrvAccountID, uCurrencyID) : QUALITY_ONE;
    const uint32        uQualityOut     = uNode != uLast ? lesActive.rippleQualityOut (uCurAccountID, uNxtAccountID, uCurrencyID) : QUALITY_ONE;

    // For bPrvAccount
    const STAmount      saPrvOwed       = bPrvAccount && uNode                              // Previous account is owed.
                                          ? lesActive.rippleOwed (uCurAccountID, uPrvAccountID, uCurrencyID)
                                          : STAmount (uCurrencyID, uCurAccountID);

    const STAmount      saPrvLimit      = bPrvAccount && uNode                              // Previous account may owe.
                                          ? lesActive.rippleLimit (uCurAccountID, uPrvAccountID, uCurrencyID)
                                          : STAmount (uCurrencyID, uCurAccountID);

    const STAmount      saNxtOwed       = bNxtAccount && uNode != uLast                 // Next account is owed.
                                          ? lesActive.rippleOwed (uCurAccountID, uNxtAccountID, uCurrencyID)
                                          : STAmount (uCurrencyID, uCurAccountID);

    WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev> uNode=%d/%d uPrvAccountID=%s uCurAccountID=%s uNxtAccountID=%s uCurrencyID=%s uQualityIn=%d uQualityOut=%d saPrvOwed=%s saPrvLimit=%s")
                                   % uNode
                                   % uLast
                                   % RippleAddress::createHumanAccountID (uPrvAccountID)
                                   % RippleAddress::createHumanAccountID (uCurAccountID)
                                   % RippleAddress::createHumanAccountID (uNxtAccountID)
                                   % STAmount::createHumanCurrency (uCurrencyID)
                                   % uQualityIn
                                   % uQualityOut
                                   % saPrvOwed
                                   % saPrvLimit);

    // Previous can redeem the owed IOUs it holds.
    const STAmount  saPrvRedeemReq  = saPrvOwed.isPositive () ? saPrvOwed : STAmount (saPrvOwed.getCurrency (), saPrvOwed.getIssuer ());
    STAmount&       saPrvRedeemAct  = pnPrv.saRevRedeem;

    // Previous can issue up to limit minus whatever portion of limit already used (not including redeemable amount).
    const STAmount  saPrvIssueReq   = saPrvOwed.isNegative () ? saPrvLimit + saPrvOwed : saPrvLimit;
    STAmount&       saPrvIssueAct   = pnPrv.saRevIssue;

    // For !bPrvAccount
    const STAmount  saPrvDeliverReq = STAmount (pnPrv.saRevDeliver.getCurrency (), pnPrv.saRevDeliver.getIssuer (), -1); // Unlimited.
    STAmount&       saPrvDeliverAct = pnPrv.saRevDeliver;

    // For bNxtAccount
    const STAmount& saCurRedeemReq  = pnCur.saRevRedeem;
    STAmount        saCurRedeemAct (saCurRedeemReq.getCurrency (), saCurRedeemReq.getIssuer ());

    const STAmount& saCurIssueReq   = pnCur.saRevIssue;
    STAmount        saCurIssueAct (saCurIssueReq.getCurrency (), saCurIssueReq.getIssuer ());               // Track progress.

    // For !bNxtAccount
    const STAmount& saCurDeliverReq = pnCur.saRevDeliver;
    STAmount        saCurDeliverAct (saCurDeliverReq.getCurrency (), saCurDeliverReq.getIssuer ());

    WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: saPrvRedeemReq=%s saPrvIssueReq=%s saPrvDeliverAct=%s saPrvDeliverReq=%s saCurRedeemReq=%s saCurIssueReq=%s saNxtOwed=%s")
                                   % saPrvRedeemReq
                                   % saPrvIssueReq
                                   % saPrvDeliverAct
                                   % saPrvDeliverReq
                                   % saCurRedeemReq
                                   % saCurIssueReq
                                   % saNxtOwed);

    WriteLog (lsTRACE, RippleCalc) << psCur.getJson ();

    assert (!saCurRedeemReq || (-saNxtOwed) >= saCurRedeemReq); // Current redeem req can't be more than IOUs on hand.
    assert (!saCurIssueReq                  // If not issuing, fine.
            || !saNxtOwed.isNegative ()         // saNxtOwed >= 0: Sender not holding next IOUs, saNxtOwed < 0: Sender holding next IOUs.
            || -saNxtOwed == saCurRedeemReq);   // If issue req, then redeem req must consume all owed.

    if (!uNode)
    {
        // ^ --> ACCOUNT -->  account|offer
        // Nothing to do, there is no previous to adjust.

        nothing ();
    }
    else if (bPrvAccount && bNxtAccount)
    {
        if (uNode == uLast)
        {
            // account --> ACCOUNT --> $
            // Overall deliverable.
            const STAmount  saCurWantedReq  = std::min (psCur.saOutReq - psCur.saOutAct, saPrvLimit + saPrvOwed); // If previous is an account, limit.
            STAmount        saCurWantedAct (saCurWantedReq.getCurrency (), saCurWantedReq.getIssuer ());

            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: account --> ACCOUNT --> $ : saCurWantedReq=%s")
                                           % saCurWantedReq);


            // Calculate redeem
            if (saPrvRedeemReq)                         // Previous has IOUs to redeem.
            {
                // Redeem at 1:1

                saCurWantedAct      = std::min (saPrvRedeemReq, saCurWantedReq);
                saPrvRedeemAct      = saCurWantedAct;

                uRateMax            = STAmount::uRateOne;

                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: Redeem at 1:1 saPrvRedeemReq=%s (available) saPrvRedeemAct=%s uRateMax=%s")
                                               % saPrvRedeemReq
                                               % saPrvRedeemAct
                                               % STAmount::saFromRate (uRateMax).getText ());
            }
            else
            {
                saPrvRedeemAct.zero (saPrvRedeemReq);
            }

            // Calculate issuing.
            saPrvIssueAct.zero (saPrvIssueReq);

            if (saCurWantedReq != saCurWantedAct        // Need more.
                    && saPrvIssueReq)                       // Will accept IOUs from prevous.
            {
                // Rate: quality in : 1.0

                // If we previously redeemed and this has a poorer rate, this won't be included the current increment.
                calcNodeRipple (uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq, saPrvIssueAct, saCurWantedAct, uRateMax);

                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: Issuing: Rate: quality in : 1.0 saPrvIssueAct=%s saCurWantedAct=%s")
                                               % saPrvIssueAct
                                               % saCurWantedAct);
            }

            if (!saCurWantedAct)
            {
                // Must have processed something.
                terResult   = tecPATH_DRY;
            }
        }
        else
        {
            // ^|account --> ACCOUNT --> account
            saPrvRedeemAct.zero (saPrvRedeemReq);
            saPrvIssueAct.zero (saPrvIssueReq);

            // redeem (part 1) -> redeem
            if (saCurRedeemReq                          // Next wants IOUs redeemed.
                    && saPrvRedeemReq)                      // Previous has IOUs to redeem.
            {
                // Rate : 1.0 : quality out
                calcNodeRipple (QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax);

                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: Rate : 1.0 : quality out saPrvRedeemAct=%s saCurRedeemAct=%s")
                                               % saPrvRedeemAct
                                               % saCurRedeemAct);
            }

            // issue (part 1) -> redeem
            if (saCurRedeemReq != saCurRedeemAct        // Next wants more IOUs redeemed.
                    && saPrvRedeemAct == saPrvRedeemReq)    // Previous has no IOUs to redeem remaining.
            {
                // Rate: quality in : quality out
                calcNodeRipple (uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct, uRateMax);

                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: Rate: quality in : quality out: saPrvIssueAct=%s saCurRedeemAct=%s")
                                               % saPrvIssueAct
                                               % saCurRedeemAct);
            }

            // redeem (part 2) -> issue.
            if (saCurIssueReq                           // Next wants IOUs issued.
                    && saCurRedeemAct == saCurRedeemReq     // Can only issue if completed redeeming.
                    && saPrvRedeemAct != saPrvRedeemReq)    // Did not complete redeeming previous IOUs.
            {
                // Rate : 1.0 : transfer_rate
                calcNodeRipple (QUALITY_ONE, lesActive.rippleTransferRate (uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct, uRateMax);

                WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: Rate : 1.0 : transfer_rate: saPrvRedeemAct=%s saCurIssueAct=%s")
                                              % saPrvRedeemAct
                                              % saCurIssueAct);
            }

            // issue (part 2) -> issue
            if (saCurIssueReq != saCurIssueAct          // Need wants more IOUs issued.
                    && saCurRedeemAct == saCurRedeemReq     // Can only issue if completed redeeming.
                    && saPrvRedeemReq == saPrvRedeemAct     // Previously redeemed all owed IOUs.
                    && saPrvIssueReq)                       // Previous can issue.
            {
                // Rate: quality in : 1.0
                calcNodeRipple (uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax);

                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: Rate: quality in : 1.0: saPrvIssueAct=%s saCurIssueAct=%s")
                                               % saPrvIssueAct
                                               % saCurIssueAct);
            }

            if (!saCurRedeemAct && !saCurIssueAct)
            {
                // Did not make progress.
                terResult   = tecPATH_DRY;
            }

            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: ^|account --> ACCOUNT --> account : saCurRedeemReq=%s saCurIssueReq=%s saPrvOwed=%s saCurRedeemAct=%s saCurIssueAct=%s")
                                           % saCurRedeemReq
                                           % saCurIssueReq
                                           % saPrvOwed
                                           % saCurRedeemAct
                                           % saCurIssueAct);
        }
    }
    else if (bPrvAccount && !bNxtAccount)
    {
        // account --> ACCOUNT --> offer
        // Note: deliver is always issue as ACCOUNT is the issuer for the offer input.
        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: account --> ACCOUNT --> offer"));

        saPrvRedeemAct.zero (saPrvRedeemReq);
        saPrvIssueAct.zero (saPrvIssueReq);

        // redeem -> deliver/issue.
        if (saPrvOwed.isPositive ()                 // Previous has IOUs to redeem.
                && saCurDeliverReq)                     // Need some issued.
        {
            // Rate : 1.0 : transfer_rate
            calcNodeRipple (QUALITY_ONE, lesActive.rippleTransferRate (uCurAccountID), saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct, saCurDeliverAct, uRateMax);
        }

        // issue -> deliver/issue
        if (saPrvRedeemReq == saPrvRedeemAct        // Previously redeemed all owed.
                && saCurDeliverReq != saCurDeliverAct)  // Still need some issued.
        {
            // Rate: quality in : 1.0
            calcNodeRipple (uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq, saPrvIssueAct, saCurDeliverAct, uRateMax);
        }

        if (!saCurDeliverAct)
        {
            // Must want something.
            terResult   = tecPATH_DRY;
        }

        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: saCurDeliverReq=%s saCurDeliverAct=%s saPrvOwed=%s")
                                       % saCurDeliverReq
                                       % saCurDeliverAct
                                       % saPrvOwed);
    }
    else if (!bPrvAccount && bNxtAccount)
    {
        if (uNode == uLast)
        {
            // offer --> ACCOUNT --> $
            const STAmount& saCurWantedReq  = psCur.saOutReq - psCur.saOutAct;                              // Previous is an offer, no limit: redeem own IOUs.
            STAmount        saCurWantedAct (saCurWantedReq.getCurrency (), saCurWantedReq.getIssuer ());

            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: offer --> ACCOUNT --> $ : saCurWantedReq=%s saOutAct=%s saOutReq=%s")
                                           % saCurWantedReq
                                           % psCur.saOutAct
                                           % psCur.saOutReq);

            if (!saCurWantedReq.isPositive ())
            {
                // TEMPORARY emergency fix
                WriteLog (lsFATAL, RippleCalc) << "CurWantReq was not positive";
                return tefEXCEPTION;
            }

            assert (saCurWantedReq.isPositive ()); // FIXME: We got one of these

            // Rate: quality in : 1.0
            calcNodeRipple (uQualityIn, QUALITY_ONE, saPrvDeliverReq, saCurWantedReq, saPrvDeliverAct, saCurWantedAct, uRateMax);

            if (!saCurWantedAct)
            {
                // Must have processed something.
                terResult   = tecPATH_DRY;
            }

            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: saPrvDeliverAct=%s saPrvDeliverReq=%s saCurWantedAct=%s saCurWantedReq=%s")
                                           % saPrvDeliverAct
                                           % saPrvDeliverReq
                                           % saCurWantedAct
                                           % saCurWantedReq);
        }
        else
        {
            // offer --> ACCOUNT --> account
            // Note: offer is always delivering(redeeming) as account is issuer.
            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: offer --> ACCOUNT --> account : saCurRedeemReq=%s saCurIssueReq=%s")
                                           % saCurRedeemReq % saCurIssueReq);

            // deliver -> redeem
            if (saCurRedeemReq)                         // Next wants us to redeem.
            {
                // Rate : 1.0 : quality out
                calcNodeRipple (QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq, saPrvDeliverAct, saCurRedeemAct, uRateMax);
            }

            // deliver -> issue.
            if (saCurRedeemReq == saCurRedeemAct        // Can only issue if previously redeemed all.
                    && saCurIssueReq)                       // Need some issued.
            {
                // Rate : 1.0 : transfer_rate
                calcNodeRipple (QUALITY_ONE, lesActive.rippleTransferRate (uCurAccountID), saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct, saCurIssueAct, uRateMax);
            }

            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: saCurIssueAct=%s saCurRedeemReq=%s saPrvDeliverAct=%s saCurIssueReq=%s")
                                           % saCurRedeemAct
                                           % saCurRedeemReq
                                           % saPrvDeliverAct
                                           % saCurIssueReq);

            if (!saPrvDeliverAct)
            {
                // Must want something.
                terResult   = tecPATH_DRY;
            }
        }
    }
    else
    {
        // offer --> ACCOUNT --> offer
        // deliver/redeem -> deliver/issue.
        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountRev: offer --> ACCOUNT --> offer"));

        // Rate : 1.0 : transfer_rate
        calcNodeRipple (QUALITY_ONE, lesActive.rippleTransferRate (uCurAccountID), saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct, saCurDeliverAct, uRateMax);

        if (!saCurDeliverAct)
        {
            // Must want something.
            terResult   = tecPATH_DRY;
        }
    }

    return terResult;
}

// The reverse pass has been narrowing by credit available and inflating by fees as it worked backwards.
// Now, for the current account node, take the actual amount from previous and adjust forward balances.
//
// Perform balance adjustments between previous and current node.
// - The previous node: specifies what to push through to current.
// - All of previous output is consumed.
// Then, compute current node's output for next node.
// - Current node: specify what to push through to next.
// - Output to next node is computed as input minus quality or transfer fee.
// - If next node is an offer and output is non-XRP then we are the issuer and do not need to push funds.
// - If next node is an offer and output is XRP then we need to deliver funds to limbo.
TER RippleCalc::calcNodeAccountFwd (
    const unsigned int          uNode,              // 0 <= uNode <= uLast
    PathState&                  psCur,
    const bool                  bMultiQuality)
{
    TER                 terResult   = tesSUCCESS;
    const unsigned int  uLast       = psCur.vpnNodes.size () - 1;

    uint64              uRateMax    = 0;

    PathState::Node&    pnPrv       = psCur.vpnNodes[uNode ? uNode - 1 : 0];
    PathState::Node&    pnCur       = psCur.vpnNodes[uNode];
    PathState::Node&    pnNxt       = psCur.vpnNodes[uNode == uLast ? uLast : uNode + 1];

    const bool      bPrvAccount     = isSetBit (pnPrv.uFlags, STPathElement::typeAccount);
    const bool      bNxtAccount     = isSetBit (pnNxt.uFlags, STPathElement::typeAccount);

    const uint160&  uCurAccountID   = pnCur.uAccountID;
    const uint160&  uPrvAccountID   = bPrvAccount ? pnPrv.uAccountID : uCurAccountID;
    const uint160&  uNxtAccountID   = bNxtAccount ? pnNxt.uAccountID : uCurAccountID;   // Offers are always issue.

    //  const uint160&  uCurIssuerID    = pnCur.uIssuerID;

    const uint160&  uCurrencyID     = pnCur.uCurrencyID;

    uint32          uQualityIn      = uNode ? lesActive.rippleQualityIn (uCurAccountID, uPrvAccountID, uCurrencyID) : QUALITY_ONE;
    uint32          uQualityOut     = uNode == uLast ? lesActive.rippleQualityOut (uCurAccountID, uNxtAccountID, uCurrencyID) : QUALITY_ONE;

    // When looking backward (prv) for req we care about what we just calculated: use fwd
    // When looking forward (cur) for req we care about what was desired: use rev

    // For bNxtAccount
    const STAmount& saPrvRedeemReq  = pnPrv.saFwdRedeem;
    STAmount        saPrvRedeemAct (saPrvRedeemReq.getCurrency (), saPrvRedeemReq.getIssuer ());

    const STAmount& saPrvIssueReq   = pnPrv.saFwdIssue;
    STAmount        saPrvIssueAct (saPrvIssueReq.getCurrency (), saPrvIssueReq.getIssuer ());

    // For !bPrvAccount
    const STAmount& saPrvDeliverReq = pnPrv.saFwdDeliver;
    STAmount        saPrvDeliverAct (saPrvDeliverReq.getCurrency (), saPrvDeliverReq.getIssuer ());

    // For bNxtAccount
    const STAmount& saCurRedeemReq  = pnCur.saRevRedeem;
    STAmount&       saCurRedeemAct  = pnCur.saFwdRedeem;

    const STAmount& saCurIssueReq   = pnCur.saRevIssue;
    STAmount&       saCurIssueAct   = pnCur.saFwdIssue;

    // For !bNxtAccount
    const STAmount& saCurDeliverReq = pnCur.saRevDeliver;
    STAmount&       saCurDeliverAct = pnCur.saFwdDeliver;

    // For !uNode
    STAmount&       saCurSendMaxPass    = psCur.saInPass;       // Report how much pass sends.

    WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd> uNode=%d/%d saPrvRedeemReq=%s saPrvIssueReq=%s saPrvDeliverReq=%s saCurRedeemReq=%s saCurIssueReq=%s saCurDeliverReq=%s")
                                   % uNode
                                   % uLast
                                   % saPrvRedeemReq
                                   % saPrvIssueReq
                                   % saPrvDeliverReq
                                   % saCurRedeemReq
                                   % saCurIssueReq
                                   % saCurDeliverReq);

    // Ripple through account.

    if (bPrvAccount && bNxtAccount)
    {
        // Next is an account, must be rippling.

        if (!uNode)
        {
            // ^ --> ACCOUNT --> account

            // First node, calculate amount to ripple based on what is available.

            saCurRedeemAct      = saCurRedeemReq;

            if (!psCur.saInReq.isNegative ())
            {
                // Limit by send max.
                saCurRedeemAct      = std::min (saCurRedeemAct, psCur.saInReq - psCur.saInAct);
            }

            saCurSendMaxPass    = saCurRedeemAct;

            saCurIssueAct       = saCurRedeemAct == saCurRedeemReq      // Fully redeemed.
                                  ? saCurIssueReq
                                  : STAmount (saCurIssueReq);

            if (!!saCurIssueAct && !psCur.saInReq.isNegative ())
            {
                // Limit by send max.
                saCurIssueAct       = std::min (saCurIssueAct, psCur.saInReq - psCur.saInAct - saCurRedeemAct);
            }

            saCurSendMaxPass    += saCurIssueAct;

            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saInReq=%s saInAct=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s saCurSendMaxPass=%s")
                                           % psCur.saInReq
                                           % psCur.saInAct
                                           % saCurRedeemAct
                                           % saCurIssueReq
                                           % saCurIssueAct
                                           % saCurSendMaxPass);
        }
        else if (uNode == uLast)
        {
            // account --> ACCOUNT --> $
            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: account --> ACCOUNT --> $ : uPrvAccountID=%s uCurAccountID=%s saPrvRedeemReq=%s saPrvIssueReq=%s")
                                           % RippleAddress::createHumanAccountID (uPrvAccountID)
                                           % RippleAddress::createHumanAccountID (uCurAccountID)
                                           % saPrvRedeemReq
                                           % saPrvIssueReq);

            // Last node.  Accept all funds.  Calculate amount actually to credit.

            STAmount&   saCurReceive    = psCur.saOutPass;

            STAmount    saIssueCrd      = uQualityIn >= QUALITY_ONE
                                          ? saPrvIssueReq                                                 // No fee.
                                          : STAmount::mulRound (saPrvIssueReq, STAmount (CURRENCY_ONE, ACCOUNT_ONE, uQualityIn, -9), true); // Amount to credit.

            // Amount to credit. Credit for less than received as a surcharge.
            saCurReceive    = saPrvRedeemReq + saIssueCrd;

            if (saCurReceive)
            {
                // Actually receive.
                terResult   = lesActive.rippleCredit (uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false);
            }
            else
            {
                // After applying quality, total payment was microscopic.
                terResult   = tecPATH_DRY;
            }
        }
        else
        {
            // account --> ACCOUNT --> account
            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: account --> ACCOUNT --> account"));

            saCurRedeemAct.zero (saCurRedeemReq);
            saCurIssueAct.zero (saCurIssueReq);

            // Previous redeem part 1: redeem -> redeem
            if (saPrvRedeemReq && saCurRedeemReq)           // Previous wants to redeem.
            {
                // Rate : 1.0 : quality out
                calcNodeRipple (QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax);
            }

            // Previous issue part 1: issue -> redeem
            if (saPrvIssueReq != saPrvIssueAct              // Previous wants to issue.
                    && saCurRedeemReq != saCurRedeemAct)        // Current has more to redeem to next.
            {
                // Rate: quality in : quality out
                calcNodeRipple (uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct, uRateMax);
            }

            // Previous redeem part 2: redeem -> issue.
            if (saPrvRedeemReq != saPrvRedeemAct            // Previous still wants to redeem.
                    && saCurRedeemReq == saCurRedeemAct         // Current redeeming is done can issue.
                    && saCurIssueReq)                           // Current wants to issue.
            {
                // Rate : 1.0 : transfer_rate
                calcNodeRipple (QUALITY_ONE, lesActive.rippleTransferRate (uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct, uRateMax);
            }

            // Previous issue part 2 : issue -> issue
            if (saPrvIssueReq != saPrvIssueAct              // Previous wants to issue.
                    && saCurRedeemReq == saCurRedeemAct        // Current redeeming is done can issue.
                    && saCurIssueReq)                           // Current wants to issue.
            {
                // Rate: quality in : 1.0
                calcNodeRipple (uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax);
            }

            STAmount    saProvide   = saCurRedeemAct + saCurIssueAct;

            // Adjust prv --> cur balance : take all inbound
            terResult   = saProvide
                          ? lesActive.rippleCredit (uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false)
                          : tecPATH_DRY;
        }
    }
    else if (bPrvAccount && !bNxtAccount)
    {
        // Current account is issuer to next offer.
        // Determine deliver to offer amount.
        // Don't adjust outbound balances- keep funds with issuer as limbo.
        // If issuer hold's an offer owners inbound IOUs, there is no fee and redeem/issue will transparently happen.

        if (uNode)
        {
            // Non-XRP, current node is the issuer.
            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: account --> ACCOUNT --> offer"));

            saCurDeliverAct.zero (saCurDeliverReq);

            // redeem -> issue/deliver.
            // Previous wants to redeem.
            // Current is issuing to an offer so leave funds in account as "limbo".
            if (saPrvRedeemReq)                             // Previous wants to redeem.
            {
                // Rate : 1.0 : transfer_rate
                // XXX Is having the transfer rate here correct?
                calcNodeRipple (QUALITY_ONE, lesActive.rippleTransferRate (uCurAccountID), saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct, saCurDeliverAct, uRateMax);
            }

            // issue -> issue/deliver
            if (saPrvRedeemReq == saPrvRedeemAct            // Previous done redeeming: Previous has no IOUs.
                    && saPrvIssueReq)                           // Previous wants to issue. To next must be ok.
            {
                // Rate: quality in : 1.0
                calcNodeRipple (uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq, saPrvIssueAct, saCurDeliverAct, uRateMax);
            }

            // Adjust prv --> cur balance : take all inbound
            terResult   = saCurDeliverAct
                          ? lesActive.rippleCredit (uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false)
                          : tecPATH_DRY;  // Didn't actually deliver anything.
        }
        else
        {
            // Delivering amount requested from downstream.
            saCurDeliverAct     = saCurDeliverReq;

            // If limited, then limit by send max and available.
            if (!psCur.saInReq.isNegative ())
            {
                // Limit by send max.
                saCurDeliverAct     = std::min (saCurDeliverAct, psCur.saInReq - psCur.saInAct);

                // Limit XRP by available. No limit for non-XRP as issuer.
                if (uCurrencyID.isZero ())
                    saCurDeliverAct = std::min (saCurDeliverAct, lesActive.accountHolds (uCurAccountID, CURRENCY_XRP, ACCOUNT_XRP));

            }

            saCurSendMaxPass    = saCurDeliverAct;                      // Record amount sent for pass.

            if (!saCurDeliverAct)
            {
                terResult   = tecPATH_DRY;
            }
            else if (!!uCurrencyID)
            {
                // Non-XRP, current node is the issuer.
                // We could be delivering to multiple accounts, so we don't know which ripple balance will be adjusted.  Assume
                // just issuing.

                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: ^ --> ACCOUNT -- !XRP --> offer"));

                // As the issuer, would only issue.
                // Don't need to actually deliver. As from delivering leave in the issuer as limbo.
                nothing ();
            }
            else
            {
                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: ^ --> ACCOUNT -- XRP --> offer"));

                // Deliver XRP to limbo.
                terResult   = lesActive.accountSend (uCurAccountID, ACCOUNT_XRP, saCurDeliverAct);
            }
        }
    }
    else if (!bPrvAccount && bNxtAccount)
    {
        if (uNode == uLast)
        {
            // offer --> ACCOUNT --> $
            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: offer --> ACCOUNT --> $ : %s") % saPrvDeliverReq);

            STAmount&   saCurReceive    = psCur.saOutPass;

            // Amount to credit.
            saCurReceive    = saPrvDeliverReq;

            // No income balance adjustments necessary.  The paying side inside the offer paid to this account.
        }
        else
        {
            // offer --> ACCOUNT --> account
            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: offer --> ACCOUNT --> account"));

            saCurRedeemAct.zero (saCurRedeemReq);
            saCurIssueAct.zero (saCurIssueReq);

            // deliver -> redeem
            if (saPrvDeliverReq && saCurRedeemReq)          // Previous wants to deliver and can current redeem.
            {
                // Rate : 1.0 : quality out
                calcNodeRipple (QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq, saPrvDeliverAct, saCurRedeemAct, uRateMax);
            }

            // deliver -> issue
            // Wants to redeem and current would and can issue.
            if (saPrvDeliverReq != saPrvDeliverAct          // Previous still wants to deliver.
                    && saCurRedeemReq == saCurRedeemAct         // Current has more to redeem to next.
                    && saCurIssueReq)                           // Current wants issue.
            {
                // Rate : 1.0 : transfer_rate
                calcNodeRipple (QUALITY_ONE, lesActive.rippleTransferRate (uCurAccountID), saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct, saCurIssueAct, uRateMax);
            }

            // No income balance adjustments necessary.  The paying side inside the offer paid and the next link will receive.
            STAmount    saProvide   = saCurRedeemAct + saCurIssueAct;

            if (!saProvide)
                terResult   = tecPATH_DRY;
        }
    }
    else
    {
        // offer --> ACCOUNT --> offer
        // deliver/redeem -> deliver/issue.
        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeAccountFwd: offer --> ACCOUNT --> offer"));

        saCurDeliverAct.zero (saCurDeliverReq);

        if (saPrvDeliverReq                                 // Previous wants to deliver
                && saCurIssueReq)                               // Current wants issue.
        {
            // Rate : 1.0 : transfer_rate
            calcNodeRipple (QUALITY_ONE, lesActive.rippleTransferRate (uCurAccountID), saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct, saCurDeliverAct, uRateMax);
        }

        // No income balance adjustments necessary.  The paying side inside the offer paid and the next link will receive.
        if (!saCurDeliverAct)
            terResult   = tecPATH_DRY;
    }

    return terResult;
}

TER RippleCalc::calcNodeFwd (const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
{
    const PathState::Node& pnCur       = psCur.vpnNodes[uNode];
    const bool              bCurAccount = isSetBit (pnCur.uFlags,  STPathElement::typeAccount);

    WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeFwd> uNode=%d") % uNode);

    TER                     terResult   = bCurAccount
                                          ? calcNodeAccountFwd (uNode, psCur, bMultiQuality)
                                          : calcNodeOfferFwd (uNode, psCur, bMultiQuality);

    if (tesSUCCESS == terResult && uNode + 1 != psCur.vpnNodes.size ())
    {
        terResult   = calcNodeFwd (uNode + 1, psCur, bMultiQuality);
    }

    WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeFwd< uNode=%d terResult=%d") % uNode % terResult);

    return terResult;
}

// Calculate a node and its previous nodes.
// From the destination work in reverse towards the source calculating how much must be asked for.
// Then work forward, figuring out how much can actually be delivered.
// <-- terResult: tesSUCCESS or tecPATH_DRY
// <-> pnNodes:
//     --> [end]saWanted.mAmount
//     --> [all]saWanted.mCurrency
//     --> [all]saAccount
//     <-> [0]saWanted.mAmount : --> limit, <-- actual
TER RippleCalc::calcNodeRev (const unsigned int uNode, PathState& psCur, const bool bMultiQuality)
{
    PathState::Node& pnCur       = psCur.vpnNodes[uNode];
    bool const       bCurAccount = isSetBit (pnCur.uFlags,  STPathElement::typeAccount);
    TER              terResult;

    // Do current node reverse.
    const uint160&  uCurIssuerID    = pnCur.uIssuerID;
    STAmount&       saTransferRate  = pnCur.saTransferRate;

    saTransferRate  = STAmount::saFromRate (lesActive.rippleTransferRate (uCurIssuerID));

    WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("calcNodeRev> uNode=%d bCurAccount=%d uIssuerID=%s saTransferRate=%s")
                                   % uNode
                                   % bCurAccount
                                   % RippleAddress::createHumanAccountID (uCurIssuerID)
                                   % saTransferRate);

    terResult   = bCurAccount
                  ? calcNodeAccountRev (uNode, psCur, bMultiQuality)
                  : calcNodeOfferRev (uNode, psCur, bMultiQuality);

    // Do previous.
    if (tesSUCCESS != terResult)
    {
        // Error, don't continue.
        nothing ();
    }
    else if (uNode)
    {
        // Continue in reverse.

        terResult   = calcNodeRev (uNode - 1, psCur, bMultiQuality);
    }

    WriteLog (lsINFO, RippleCalc) << boost::str (boost::format ("calcNodeRev< uNode=%d terResult=%s/%d") % uNode % transToken (terResult) % terResult);

    return terResult;
}

// Calculate the next increment of a path.
// The increment is what can satisfy a portion or all of the requested output at the best quality.
// <-- psCur.uQuality
void RippleCalc::pathNext (PathState::ref psrCur, const bool bMultiQuality, const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent)
{
    // The next state is what is available in preference order.
    // This is calculated when referenced accounts changed.
    const unsigned int  uLast           = psrCur->vpnNodes.size () - 1;

    psrCur->bConsumed   = false;

    // YYY This clearing should only be needed for nice logging.
    psrCur->saInPass    = STAmount (psrCur->saInReq.getCurrency (), psrCur->saInReq.getIssuer ());
    psrCur->saOutPass   = STAmount (psrCur->saOutReq.getCurrency (), psrCur->saOutReq.getIssuer ());

    psrCur->vUnfundedBecame.clear ();
    psrCur->umReverse.clear ();

    WriteLog (lsDEBUG, RippleCalc) << "pathNext: Path In: " << psrCur->getJson ();

    assert (psrCur->vpnNodes.size () >= 2);

    lesCurrent  = lesCheckpoint.duplicate ();       // Restore from checkpoint.

    for (unsigned int uIndex = psrCur->vpnNodes.size (); uIndex--;)
    {
        PathState::Node& pnCur   = psrCur->vpnNodes[uIndex];

        pnCur.saRevRedeem.zero ();
        pnCur.saRevIssue.zero ();
        pnCur.saRevDeliver.zero ();
        pnCur.saFwdDeliver.zero ();
    }

    psrCur->terStatus   = calcNodeRev (uLast, *psrCur, bMultiQuality);

    WriteLog (lsDEBUG, RippleCalc) << "pathNext: Path after reverse: " << psrCur->getJson ();

    if (tesSUCCESS == psrCur->terStatus)
    {
        // Do forward.
        lesCurrent  = lesCheckpoint.duplicate ();   // Restore from checkpoint.

        psrCur->terStatus   = calcNodeFwd (0, *psrCur, bMultiQuality);
    }

    if (tesSUCCESS == psrCur->terStatus)
    {
        CondLog (!psrCur->saInPass || !psrCur->saOutPass, lsDEBUG, RippleCalc)
                << boost::str (boost::format ("pathNext: Error calcNodeFwd reported success for nothing: saOutPass=%s saInPass=%s")
                               % psrCur->saOutPass
                               % psrCur->saInPass);

        if (!psrCur->saOutPass || !psrCur->saInPass)
            throw std::runtime_error ("Made no progress.");

        psrCur->uQuality    = STAmount::getRate (psrCur->saOutPass, psrCur->saInPass);  // Calculate relative quality.

        WriteLog (lsDEBUG, RippleCalc) << "pathNext: Path after forward: " << psrCur->getJson ();
    }
    else
    {
        psrCur->uQuality    = 0;
    }
}

// <-- TER: Only returns tepPATH_PARTIAL if !bPartialPayment.
TER RippleCalc::rippleCalc (
    // Compute paths vs this ledger entry set.  Up to caller to actually apply to ledger.
    LedgerEntrySet&         lesActive,              // <-> --> = Fee already applied to src balance.
    STAmount&         saMaxAmountAct,         // <-- The computed input amount.
    STAmount&         saDstAmountAct,         // <-- The computed output amount.
    std::vector<PathState::pointer>&  vpsExpanded,
    // Issuer:
    //      XRP: ACCOUNT_XRP
    //  non-XRP: uSrcAccountID (for any issuer) or another account with trust node.
    const STAmount&     saMaxAmountReq,             // --> -1 = no limit.

    // Issuer:
    //      XRP: ACCOUNT_XRP
    //  non-XRP: uDstAccountID (for any issuer) or another account with trust node.
    const STAmount&     saDstAmountReq,

    const uint160&      uDstAccountID,
    const uint160&      uSrcAccountID,
    const STPathSet&    spsPaths,
    const bool          bPartialPayment,
    const bool          bLimitQuality,
    const bool          bNoRippleDirect,
    const bool          bStandAlone,                // True, not to delete unfundeds.
    const bool          bOpenLedger
)
{
    assert (lesActive.isValid ());
    RippleCalc  rc (lesActive, bOpenLedger);

    WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("rippleCalc> saMaxAmountReq=%s saDstAmountReq=%s")
                                   % saMaxAmountReq
                                   % saDstAmountReq);

    TER         terResult   = temUNCERTAIN;

    // YYY Might do basic checks on src and dst validity as per doPayment.

    if (bNoRippleDirect && spsPaths.isEmpty ())
    {
        WriteLog (lsINFO, RippleCalc) << "rippleCalc: Invalid transaction: No paths and direct ripple not allowed.";

        return temRIPPLE_EMPTY;
    }

    // Incrementally search paths.

    // bNoRippleDirect is a slight misnomer, it really means make no ripple default path.
    if (!bNoRippleDirect)
    {
        // Build a default path.  Use saDstAmountReq and saMaxAmountReq to imply nodes.
        // XXX Might also make a XRP bridge by default.

        PathState::pointer  pspDirect   = boost::make_shared<PathState> (saDstAmountReq, saMaxAmountReq);

        if (!pspDirect)
            return temUNKNOWN;

        pspDirect->setExpanded (lesActive, STPath (), uDstAccountID, uSrcAccountID);

        pspDirect->setIndex (vpsExpanded.size ());

        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("rippleCalc: Build direct: status: %s")
                                       % transToken (pspDirect->terStatus));

        // Return if malformed.
        if (isTemMalformed (pspDirect->terStatus))
            return pspDirect->terStatus;

        if (tesSUCCESS == pspDirect->terStatus)
        {
            // Had a success.
            terResult   = tesSUCCESS;

            vpsExpanded.push_back (pspDirect);
        }
        else if (terNO_LINE != pspDirect->terStatus)
        {
            terResult   = pspDirect->terStatus;
        }
    }

    WriteLog (lsTRACE, RippleCalc) << "rippleCalc: Paths in set: " << spsPaths.size ();

    int iIndex  = 0;
    BOOST_FOREACH (const STPath & spPath, spsPaths)
    {
        PathState::pointer pspExpanded  = boost::make_shared<PathState> (saDstAmountReq, saMaxAmountReq);

        if (!pspExpanded)
            return temUNKNOWN;

        WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("rippleCalc: EXPAND: saDstAmountReq=%s saMaxAmountReq=%s uDstAccountID=%s uSrcAccountID=%s")
                                       % saDstAmountReq
                                       % saMaxAmountReq
                                       % RippleAddress::createHumanAccountID (uDstAccountID)
                                       % RippleAddress::createHumanAccountID (uSrcAccountID));

        pspExpanded->setExpanded (lesActive, spPath, uDstAccountID, uSrcAccountID);

        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("rippleCalc: Build path: %d: status: %s")
                                       % ++iIndex
                                       % transToken (pspExpanded->terStatus));

        // Return, if the path specification was malformed.
        if (isTemMalformed (pspExpanded->terStatus))
            return pspExpanded->terStatus;

        if (tesSUCCESS == pspExpanded->terStatus)
        {
            terResult   = tesSUCCESS;           // Had a success.

            pspExpanded->setIndex (vpsExpanded.size ());

            vpsExpanded.push_back (pspExpanded);
        }
        else if (terNO_LINE != pspExpanded->terStatus)
        {
            terResult   = pspExpanded->terStatus;
        }
    }

    if (tesSUCCESS != terResult)
    {
        return terResult == temUNCERTAIN ? terNO_LINE : terResult;
    }
    else
    {
        terResult   = temUNCERTAIN;
    }

    saMaxAmountAct  = STAmount (saMaxAmountReq.getCurrency (), saMaxAmountReq.getIssuer ());
    saDstAmountAct  = STAmount (saDstAmountReq.getCurrency (), saDstAmountReq.getIssuer ());

    const LedgerEntrySet    lesBase         = lesActive;                            // Checkpoint with just fees paid.
    const uint64            uQualityLimit   = bLimitQuality ? STAmount::getRate (saDstAmountReq, saMaxAmountReq) : 0;
    // When processing, don't want to complicate directory walking with deletion.
    std::vector<uint256>    vuUnfundedBecame;                                       // Offers that became unfunded.

    int iPass   = 0;

    while (temUNCERTAIN == terResult)
    {
        int                     iBest           = -1;
        const LedgerEntrySet    lesCheckpoint   = lesActive;
        int                     iDry            = 0;
        bool                    bMultiQuality   = false;                    // True, if ever computed multi-quality.

        // Find the best path.
        BOOST_FOREACH (PathState::ref pspCur, vpsExpanded)
        {
            if (pspCur->uQuality)                                           // Only do active paths.
            {
                bMultiQuality       = 1 == vpsExpanded.size () - iDry,      // Computing the only non-dry path, compute multi-quality.

                pspCur->saInAct     = saMaxAmountAct;                       // Update to current amount processed.
                pspCur->saOutAct    = saDstAmountAct;

                CondLog (pspCur->saInReq.isPositive () && pspCur->saInAct >= pspCur->saInReq, lsWARNING, RippleCalc)
                        << boost::str (boost::format ("rippleCalc: DONE: saInAct=%s saInReq=%s")
                                       % pspCur->saInAct
                                       % pspCur->saInReq);

                assert (pspCur->saInReq.isNegative () || pspCur->saInAct < pspCur->saInReq); // Error if done.

                CondLog (pspCur->saOutAct >= pspCur->saOutReq, lsWARNING, RippleCalc)
                        << boost::str (boost::format ("rippleCalc: ALREADY DONE: saOutAct=%s saOutReq=%s")
                                       % pspCur->saOutAct
                                       % pspCur->saOutReq);

                assert (pspCur->saOutAct < pspCur->saOutReq);                               // Error if done, output met.

                rc.pathNext (pspCur, bMultiQuality, lesCheckpoint, lesActive);  // Compute increment.
                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("rippleCalc: AFTER: mIndex=%d uQuality=%d rate=%s")
                                               % pspCur->mIndex
                                               % pspCur->uQuality
                                               % STAmount::saFromRate (pspCur->uQuality));

                if (!pspCur->uQuality)
                {
                    // Path was dry.

                    ++iDry;
                }
                else
                {
                    CondLog (!pspCur->saInPass || !pspCur->saOutPass, lsDEBUG, RippleCalc)
                            << boost::str (boost::format ("rippleCalc: better: uQuality=%s saInPass=%s saOutPass=%s")
                                           % STAmount::saFromRate (pspCur->uQuality)
                                           % pspCur->saInPass
                                           % pspCur->saOutPass);

                    assert (!!pspCur->saInPass && !!pspCur->saOutPass);

                    if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit)           // Quality is not limted or increment has allowed quality.
                            && (iBest < 0                                                   // Best is not yet set.
                                || PathState::lessPriority (*vpsExpanded[iBest], *pspCur))) // Current is better than set.
                    {
                        WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("rippleCalc: better: mIndex=%d uQuality=%s rate=%s saInPass=%s saOutPass=%s")
                                                       % pspCur->mIndex
                                                       % pspCur->uQuality
                                                       % STAmount::saFromRate (pspCur->uQuality)
                                                       % pspCur->saInPass
                                                       % pspCur->saOutPass);

                        assert (lesActive.isValid ());
                        lesActive.swapWith (pspCur->lesEntries);                        // For the path, save ledger state.
                        lesActive.invalidate ();

                        iBest   = pspCur->getIndex ();
                    }
                }
            }
        }

        if (ShouldLog (lsDEBUG, RippleCalc))
        {
            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("rippleCalc: Summary: Pass: %d Dry: %d Paths: %d") % ++iPass % iDry % vpsExpanded.size ());
            BOOST_FOREACH (PathState::ref pspCur, vpsExpanded)
            {
                WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("rippleCalc: Summary: %d rate: %s quality:%d best: %d consumed: %d")
                                               % pspCur->mIndex
                                               % STAmount::saFromRate (pspCur->uQuality)
                                               % pspCur->uQuality
                                               % (iBest == pspCur->getIndex ())
                                               % pspCur->bConsumed);
            }
        }

        if (iBest >= 0)
        {
            // Apply best path.
            PathState::pointer  pspBest = vpsExpanded[iBest];

            WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("rippleCalc: best: uQuality=%s saInPass=%s saOutPass=%s")
                                           % STAmount::saFromRate (pspBest->uQuality)
                                           % pspBest->saInPass
                                           % pspBest->saOutPass);

            // Record best pass' offers that became unfunded for deletion on success.
            vuUnfundedBecame.insert (vuUnfundedBecame.end (), pspBest->vUnfundedBecame.begin (), pspBest->vUnfundedBecame.end ());

            // Record best pass' LedgerEntrySet to build off of and potentially return.
            assert (pspBest->lesEntries.isValid ());
            lesActive.swapWith (pspBest->lesEntries);
            pspBest->lesEntries.invalidate ();

            saMaxAmountAct  += pspBest->saInPass;
            saDstAmountAct  += pspBest->saOutPass;

            if (pspBest->bConsumed || bMultiQuality)
            {
                ++iDry;
                pspBest->uQuality   = 0;
            }

            if (saDstAmountAct == saDstAmountReq)
            {
                // Done. Delivered requested amount.

                terResult   = tesSUCCESS;
            }
            else if (saDstAmountAct > saDstAmountReq)
            {
                WriteLog (lsFATAL, RippleCalc) << boost::str (boost::format ("rippleCalc: TOO MUCH: saDstAmountAct=%s saDstAmountReq=%s")
                                               % saDstAmountAct
                                               % saDstAmountReq);

                return tefEXCEPTION;  // TEMPORARY
                assert (false);
            }
            else if (saMaxAmountAct != saMaxAmountReq && iDry != vpsExpanded.size ())
            {
                // Have not met requested amount or max send, try to do more. Prepare for next pass.

                // Merge best pass' umReverse.
                rc.mumSource.insert (pspBest->umReverse.begin (), pspBest->umReverse.end ());

            }
            else if (!bPartialPayment)
            {
                // Have sent maximum allowed. Partial payment not allowed.

                terResult   = tecPATH_PARTIAL;
            }
            else
            {
                // Have sent maximum allowed. Partial payment allowed.  Success.

                terResult   = tesSUCCESS;
            }
        }
        // Not done and ran out of paths.
        else if (!bPartialPayment)
        {
            // Partial payment not allowed.
            terResult   = tecPATH_PARTIAL;
        }
        // Partial payment ok.
        else if (!saDstAmountAct)
        {
            // No payment at all.
            terResult   = tecPATH_DRY;
        }
        else
        {
            terResult   = tesSUCCESS;
        }
    }

    if (!bStandAlone)
    {
        if (tesSUCCESS == terResult)
        {
            // Delete became unfunded offers.
            BOOST_FOREACH (uint256 const & uOfferIndex, vuUnfundedBecame)
            {
                if (tesSUCCESS == terResult)
                {
                    WriteLog (lsDEBUG, RippleCalc) << "Became unfunded " << uOfferIndex.GetHex ();
                    terResult = lesActive.offerDelete (uOfferIndex);
                }
            }
        }

        // Delete found unfunded offers.
        BOOST_FOREACH (uint256 const & uOfferIndex, rc.musUnfundedFound)
        {
            if (tesSUCCESS == terResult)
            {
                WriteLog (lsDEBUG, RippleCalc) << "Delete unfunded " << uOfferIndex.GetHex ();
                terResult = lesActive.offerDelete (uOfferIndex);
            }
        }
    }

    return terResult;
}

//
// Rough cut of automatic bridging.
//

#if 0
// XXX Need to adjust for fees.
// Find offers to satisfy pnDst.
// - Does not adjust any balances as there is at least a forward pass to come.
// --> pnDst.saWanted: currency and amount wanted
// --> pnSrc.saIOURedeem.mCurrency: use this before saIOUIssue, limit to use.
// --> pnSrc.saIOUIssue.mCurrency: use this after saIOURedeem, limit to use.
// <-- pnDst.saReceive
// <-- pnDst.saIOUForgive
// <-- pnDst.saIOUAccept
// <-- terResult : tesSUCCESS = no error and if !bAllowPartial complelely satisfied wanted.
// <-> usOffersDeleteAlways:
// <-> usOffersDeleteOnSuccess:
TER calcOfferFill (Node& pnSrc, Node& pnDst, bool bAllowPartial)
{
    TER terResult;

    if (pnDst.saWanted.isNative ())
    {
        // Transfer XRP.

        STAmount    saSrcFunds  = pnSrc.saAccount->accountHolds (pnSrc.saAccount, uint160 (0), uint160 (0));

        if (saSrcFunds && (bAllowPartial || saSrcFunds > pnDst.saWanted))
        {
            pnSrc.saSend    = min (saSrcFunds, pnDst.saWanted);
            pnDst.saReceive = pnSrc.saSend;
        }
        else
        {
            terResult   = terINSUF_PATH;
        }
    }
    else
    {
        // Ripple funds.

        // Redeem to limit.
        terResult   = calcOfferFill (
                          accountHolds (pnSrc.saAccount, pnDst.saWanted.getCurrency (), pnDst.saWanted.getIssuer ()),
                          pnSrc.saIOURedeem,
                          pnDst.saIOUForgive,
                          bAllowPartial);

        if (tesSUCCESS == terResult)
        {
            // Issue to wanted.
            terResult   = calcOfferFill (
                              pnDst.saWanted,     // As much as wanted is available, limited by credit limit.
                              pnSrc.saIOUIssue,
                              pnDst.saIOUAccept,
                              bAllowPartial);
        }

        if (tesSUCCESS == terResult && !bAllowPartial)
        {
            STAmount    saTotal = pnDst.saIOUForgive    + pnSrc.saIOUAccept;

            if (saTotal != saWanted)
                terResult   = terINSUF_PATH;
        }
    }

    return terResult;
}
#endif

#if 0
// Get the next offer limited by funding.
// - Stop when becomes unfunded.
void TransactionEngine::calcOfferBridgeNext (
    uint256 const&      uBookRoot,      // --> Which order book to look in.
    uint256 const&      uBookEnd,       // --> Limit of how far to look.
    uint256&            uBookDirIndex,  // <-> Current directory. <-- 0 = no offer available.
    uint64&             uBookDirNode,   // <-> Which node. 0 = first.
    unsigned int&       uBookDirEntry,  // <-> Entry in node. 0 = first.
    STAmount&           saOfferIn,      // <-- How much to pay in, fee inclusive, to get saOfferOut out.
    STAmount&           saOfferOut      // <-- How much offer pays out.
)
{
    saOfferIn       = 0;    // XXX currency & issuer
    saOfferOut      = 0;    // XXX currency & issuer

    bool            bDone   = false;

    while (!bDone)
    {
        uint256         uOfferIndex;

        // Get uOfferIndex.
        mNodes.dirNext (uBookRoot, uBookEnd, uBookDirIndex, uBookDirNode, uBookDirEntry, uOfferIndex);

        SLE::pointer    sleOffer        = entryCache (ltOFFER, uOfferIndex);

        uint160         uOfferOwnerID   = sleOffer->getFieldAccount160 (sfAccount);
        STAmount        saOfferPays     = sleOffer->getFieldAmount (sfTakerGets);
        STAmount        saOfferGets     = sleOffer->getFieldAmount (sfTakerPays);

        if (sleOffer->isFieldPresent (sfExpiration) && sleOffer->getFieldU32 (sfExpiration) <= mLedger->getParentCloseTimeNC ())
        {
            // Offer is expired.
            WriteLog (lsINFO, RippleCalc) << "calcOfferFirst: encountered expired offer";
            musUnfundedFound.insert(uOfferIndex);
        }
        else
        {
            STAmount        saOfferFunds    = accountFunds (uOfferOwnerID, saOfferPays);
            // Outbound fees are paid by offer owner.
            // XXX Calculate outbound fee rate.

            if (saOfferPays.isNative ())
            {
                // No additional fees for XRP.

                nothing ();
            }
            else if (saOfferPays.getIssuer () == uOfferOwnerID)
            {
                // Offerer is issue own IOUs.
                // No fees at this exact point, XXX receiving node may charge a fee.
                // XXX Make sure has a credit line with receiver, limit by credit line.

                nothing ();
                // XXX Broken - could be issuing or redeeming or both.
            }
            else
            {
                // Offer must be redeeming IOUs.

                // No additional
                // XXX Broken
            }

            if (!saOfferFunds.isPositive ())
            {
                // Offer is unfunded.
                WriteLog (lsINFO, RippleCalc) << "calcOfferFirst: offer unfunded: delete";
            }
            else if (saOfferFunds >= saOfferPays)
            {
                // Offer fully funded.

                // Account transferring funds in to offer always pays inbound fees.

                saOfferIn   = saOfferGets;  // XXX Add in fees?

                saOfferOut  = saOfferPays;

                bDone       = true;
            }
            else
            {
                // Offer partially funded.

                // saOfferIn/saOfferFunds = saOfferGets/saOfferPays
                // XXX Round such that all saOffer funds are exhausted.
                saOfferIn   = (saOfferFunds * saOfferGets) / saOfferPays; // XXX Add in fees?
                saOfferOut  = saOfferFunds;

                bDone       = true;
            }
        }

        if (!bDone)
        {
            // musUnfundedFound.insert(uOfferIndex);
        }
    }

    while (bNext);
}
#endif

#if 0
// If either currency is not XRP, then also calculates vs XRP bridge.
// --> saWanted: Limit of how much is wanted out.
// <-- saPay: How much to pay into the offer.
// <-- saGot: How much to the offer pays out.  Never more than saWanted.
// Given two value's enforce a minimum:
// - reverse: prv is maximum to pay in (including fee) - cur is what is wanted: generally, minimizing prv
// - forward: prv is actual amount to pay in (including fee) - cur is what is wanted: generally, minimizing cur
// Value in is may be rippled or credited from limbo. Value out is put in limbo.
// If next is an offer, the amount needed is in cur redeem.
// XXX What about account mentioned multiple times via offers?
void TransactionEngine::calcNodeOffer (
    bool            bForward,
    bool            bMultiQuality,  // True, if this is the only active path: we can do multiple qualities in this pass.
    const uint160&  uPrvAccountID,  // If 0, then funds from previous offers limbo
    const uint160&  uPrvCurrencyID,
    const uint160&  uPrvIssuerID,
    const uint160&  uCurCurrencyID,
    const uint160&  uCurIssuerID,

    const STAmount& uPrvRedeemReq,  // --> In limit.
    STAmount&       uPrvRedeemAct,  // <-> In limit achieved.
    const STAmount& uCurRedeemReq,  // --> Out limit. Driver when uCurIssuerID == uNxtIssuerID (offer would redeem to next)
    STAmount&       uCurRedeemAct,  // <-> Out limit achieved.

    const STAmount& uCurIssueReq,   // --> In limit.
    STAmount&       uCurIssueAct,   // <-> In limit achieved.
    const STAmount& uCurIssueReq,   // --> Out limit. Driver when uCurIssueReq != uNxtIssuerID (offer would effectively issue or transfer to next)
    STAmount&       uCurIssueAct,   // <-> Out limit achieved.

    STAmount& saPay,
    STAmount& saGot
) const
{
    TER terResult   = temUNKNOWN;

    // Direct: not bridging via XRP
    bool            bDirectNext = true;     // True, if need to load.
    uint256         uDirectQuality;
    uint256         uDirectTip  = Ledger::getBookBase (uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID);
    uint256         uDirectEnd  = Ledger::getQualityNext (uDirectTip);

    // Bridging: bridging via XRP
    bool            bBridge     = true;     // True, if bridging active. False, missing an offer.
    uint256         uBridgeQuality;
    STAmount        saBridgeIn;             // Amount available.
    STAmount        saBridgeOut;

    bool            bInNext     = true;     // True, if need to load.
    STAmount        saInIn;                 // Amount available. Consumed in loop. Limited by offer funding.
    STAmount        saInOut;
    uint256         uInTip;                 // Current entry.
    uint256         uInEnd;
    unsigned int    uInEntry;

    bool            bOutNext    = true;
    STAmount        saOutIn;
    STAmount        saOutOut;
    uint256         uOutTip;
    uint256         uOutEnd;
    unsigned int    uOutEntry;

    saPay.zero ();
    saPay.setCurrency (uPrvCurrencyID);
    saPay.setIssuer (uPrvIssuerID);

    saNeed  = saWanted;

    if (!uCurCurrencyID && !uPrvCurrencyID)
    {
        // Bridging: Neither currency is XRP.
        uInTip      = Ledger::getBookBase (uPrvCurrencyID, uPrvIssuerID, CURRENCY_XRP, ACCOUNT_XRP);
        uInEnd      = Ledger::getQualityNext (uInTip);
        uOutTip     = Ledger::getBookBase (CURRENCY_XRP, ACCOUNT_XRP, uCurCurrencyID, uCurIssuerID);
        uOutEnd     = Ledger::getQualityNext (uInTip);
    }

    // Find our head offer.

    bool        bRedeeming      = false;
    bool        bIssuing        = false;

    // The price varies as we change between issuing and transferring, so unless bMultiQuality, we must stick with a mode once it
    // is determined.

    if (bBridge && (bInNext || bOutNext))
    {
        // Bridging and need to calculate next bridge rate.
        // A bridge can consist of multiple offers. As offers are consumed, the effective rate changes.

        if (bInNext)
        {
            //                  sleInDir    = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uInIndex, uInEnd));
            // Get the next funded offer.
            offerBridgeNext (uInIndex, uInEnd, uInEntry, saInIn, saInOut);  // Get offer limited by funding.
            bInNext     = false;
        }

        if (bOutNext)
        {
            //                  sleOutDir   = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uOutIndex, uOutEnd));
            offerNext (uOutIndex, uOutEnd, uOutEntry, saOutIn, saOutOut);
            bOutNext    = false;
        }

        if (!uInIndex || !uOutIndex)
        {
            bBridge = false;    // No more offers to bridge.
        }
        else
        {
            // Have bridge in and out entries.
            // Calculate bridge rate.  Out offer pay ripple fee.  In offer fee is added to in cost.

            saBridgeOut.zero ();

            if (saInOut < saOutIn)
            {
                // Limit by in.

                // XXX Need to include fees in saBridgeIn.
                saBridgeIn  = saInIn;   // All of in
                // Limit bridge out: saInOut/saBridgeOut = saOutIn/saOutOut
                // Round such that we would take all of in offer, otherwise would have leftovers.
                saBridgeOut = (saInOut * saOutOut) / saOutIn;
            }
            else if (saInOut > saOutIn)
            {
                // Limit by out, if at all.

                // XXX Need to include fees in saBridgeIn.
                // Limit bridge in:saInIn/saInOuts = aBridgeIn/saOutIn
                // Round such that would take all of out offer.
                saBridgeIn  = (saInIn * saOutIn) / saInOuts;
                saBridgeOut = saOutOut;     // All of out.
            }
            else
            {
                // Entries match,

                // XXX Need to include fees in saBridgeIn.
                saBridgeIn  = saInIn;   // All of in
                saBridgeOut = saOutOut; // All of out.
            }

            uBridgeQuality  = STAmount::getRate (saBridgeIn, saBridgeOut);  // Inclusive of fees.
        }
    }

    if (bBridge)
    {
        bUseBridge  = !uDirectTip || (uBridgeQuality < uDirectQuality)
    }
    else if (!!uDirectTip)
    {
        bUseBridge  = false
    }
    else
    {
        // No more offers. Declare success, even if none returned.
        saGot       = saWanted - saNeed;
        terResult   = tesSUCCESS;
    }

    if (tesSUCCESS != terResult)
    {
        STAmount&   saAvailIn   = bUseBridge ? saBridgeIn : saDirectIn;
        STAmount&   saAvailOut  = bUseBridge ? saBridgeOut : saDirectOut;

        if (saAvailOut > saNeed)
        {
            // Consume part of offer. Done.

            saNeed  = 0;
            saPay   += (saNeed * saAvailIn) / saAvailOut; // Round up, prefer to pay more.
        }
        else
        {
            // Consume entire offer.

            saNeed  -= saAvailOut;
            saPay   += saAvailIn;

            if (bUseBridge)
            {
                // Consume bridge out.
                if (saOutOut == saAvailOut)
                {
                    // Consume all.
                    saOutOut    = 0;
                    saOutIn     = 0;
                    bOutNext    = true;
                }
                else
                {
                    // Consume portion of bridge out, must be consuming all of bridge in.
                    // saOutIn/saOutOut = saSpent/saAvailOut
                    // Round?
                    saOutIn     -= (saOutIn * saAvailOut) / saOutOut;
                    saOutOut    -= saAvailOut;
                }

                // Consume bridge in.
                if (saOutIn == saAvailIn)
                {
                    // Consume all.
                    saInOut     = 0;
                    saInIn      = 0;
                    bInNext     = true;
                }
                else
                {
                    // Consume portion of bridge in, must be consuming all of bridge out.
                    // saInIn/saInOut = saAvailIn/saPay
                    // Round?
                    saInOut -= (saInOut * saAvailIn) / saInIn;
                    saInIn  -= saAvailIn;
                }
            }
            else
            {
                bDirectNext = true;
            }
        }
    }
}
#endif

// vim:ts=4
