bool VideoFrameScheduler::PLL::fit( nsecs_t phase, nsecs_t period, size_t numSamplesToUse, int64_t *a, int64_t *b, int64_t *err) { if (numSamplesToUse > mNumSamples) { numSamplesToUse = mNumSamples; } int64_t sumX = 0; int64_t sumXX = 0; int64_t sumXY = 0; int64_t sumYY = 0; int64_t sumY = 0; int64_t x = 0; // x usually is in [0..numSamplesToUse) nsecs_t lastTime; for (size_t i = 0; i < numSamplesToUse; i++) { size_t ix = (mNumSamples - numSamplesToUse + i) % kHistorySize; nsecs_t time = mTimes[ix]; if (i > 0) { x += divRound(time - lastTime, period); } // y is usually in [-numSamplesToUse..numSamplesToUse+kRefitRefreshPeriod/kMinPeriod) << kPrecision // ideally in [0..numSamplesToUse), but shifted by -numSamplesToUse during // priming, and possibly shifted by up to kRefitRefreshPeriod/kMinPeriod // while we are not refitting. int64_t y = divRound(time - phase, period >> kPrecision); sumX += x; sumY += y; sumXX += x * x; sumXY += x * y; sumYY += y * y; lastTime = time; } int64_t div = numSamplesToUse * sumXX - sumX * sumX; if (div == 0) { return false; } int64_t a_nom = numSamplesToUse * sumXY - sumX * sumY; int64_t b_nom = sumXX * sumY - sumX * sumXY; *a = divRound(a_nom, div); *b = divRound(b_nom, div); // don't use a and b directly as the rounding error is significant *err = sumYY - divRound(a_nom * sumXY + b_nom * sumY, div); ALOGV("fitting[%zu] a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)", numSamplesToUse, (long long)*a, (*a / (float)(1 << kPrecision)), (long long)*b, (*b / (float)(1 << kPrecision)), (long long)*err, (*err / (float)(1 << (kPrecision * 2)))); return true; }
void agentsInit(){ /** * computes the exact position of the agent using the row and col position in the agent set position */ agentsSet(); pacx = gridCellSide * (pacCol); pacy = gridCellSide * (pacRow); pacCol = divRound(pacx,gridCellSide); pacRow = divRound(pacy,gridCellSide); blinky.init(); inky.init(); pinky.init(); clyde.init(); }
Amounts Taker::remaining_offer () const { // If the taker is done, then there's no offer to place. if (done ()) return Amounts (m_amount.in.zeroed(), m_amount.out.zeroed()); // Avoid math altogether if we didn't cross. if (m_amount == m_remain) return m_amount; if (m_options.sell) { assert (m_remain.in > zero); // We scale the output based on the remaining input: return Amounts (m_remain.in, divRound ( m_remain.in, m_quality.rate (), m_remain.out.issue (), true)); } assert (m_remain.out > zero); // We scale the input based on the remaining output: return Amounts (mulRound ( m_remain.out, m_quality.rate (), m_remain.in.issue (), true), m_remain.out); }
Amounts BasicTaker::remaining_offer (STAmountCalcSwitchovers const& amountCalcSwitchovers) const { // If the taker is done, then there's no offer to place. if (done ()) return Amounts (remaining_.in.zeroed(), remaining_.out.zeroed()); // Avoid math altogether if we didn't cross. if (original_ == remaining_) return original_; if (sell_) { assert (remaining_.in > zero); // We scale the output based on the remaining input: return Amounts (remaining_.in, divRound ( remaining_.in, quality_.rate (), issue_out_, true, amountCalcSwitchovers)); } assert (remaining_.out > zero); // We scale the input based on the remaining output: return Amounts (mulRound ( remaining_.out, quality_.rate (), issue_in_, true, amountCalcSwitchovers), remaining_.out); }
void handleResize(int w, int h) { /** * Handles the propertites of the objects when the window is resized */ //Tell OpenGL how to convert from coordinates to pixel values glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); //Switch to setting the camera perspective //Set the camera perspective glLoadIdentity(); //Reset the camera aspectRatio = (double)w / (double)h; winsizex = w; winsizey = h; camera(); gluPerspective(60.0, aspectRatio, 1.0, 200.0); //The visible volume is frustrum of a cone with 60 degrees if(aspectRatio > 28.0/31.0) gridCellSide = YLIM*2/31.0; else gridCellSide = XLIM*2/28.0; pacMoveDist = gridCellSide/8; // printf("Grid Cell Side = %f\n", gridCellSide); pacx = gridCellSide * (pacCol); pacy = gridCellSide * (pacRow); gridHeightby2 = 31.0 * 0.5 * gridCellSide; gridWidthby2 = 28.0 * 0.5 * gridCellSide; pacCol = divRound(pacx,gridCellSide); pacRow = divRound(pacy,gridCellSide); blinky.init(); inky.init(); pinky.init(); clyde.init(); // printf("%d %d\n%f %f\n", pacRow,pacCol,pacx,pacy); }
Amounts Quality::ceil_in (Amounts const& amount, STAmount const& limit) const { if (amount.in > limit) { Amounts result (limit, divRound ( limit, rate(), amount.out.issue (), true)); // Clamp out if (result.out > amount.out) result.out = amount.out; assert (result.in == limit); return result; } assert (amount.in <= limit); return amount; }
TER PathCursor::deliverNodeForward ( AccountID const& uInAccountID, // --> Input owner's account. STAmount const& saInReq, // --> Amount to deliver. STAmount& saInAct, // <-- Amount delivered, this invocation. STAmount& saInFees, // <-- Fees charged, this invocation. bool callerHasLiquidity) const { TER resultCode = tesSUCCESS; // Don't deliver more than wanted. // Zeroed in reverse pass. node().directory.restart(multiQuality_); saInAct.clear (saInReq); saInFees.clear (saInReq); int loopCount = 0; auto viewJ = rippleCalc_.logs_.journal ("View"); // XXX Perhaps make sure do not exceed node().saRevDeliver as another way to // stop? while (resultCode == tesSUCCESS && saInAct + saInFees < saInReq) { // Did not spend all inbound deliver funds. if (++loopCount > (multiQuality_ ? CALC_NODE_DELIVER_MAX_LOOPS_MQ : CALC_NODE_DELIVER_MAX_LOOPS)) { JLOG (j_.warn()) << "deliverNodeForward: max loops cndf"; return telFAILED_PROCESSING; } // Determine values for pass to adjust saInAct, saInFees, and // node().saFwdDeliver. advanceNode (saInAct, false, callerHasLiquidity); // If needed, advance to next funded offer. if (resultCode != tesSUCCESS) { } else if (!node().offerIndex_) { JLOG (j_.warn()) << "deliverNodeForward: INTERNAL ERROR: Ran out of offers."; return telFAILED_PROCESSING; } else if (resultCode == tesSUCCESS) { auto const xferRate = effectiveRate ( previousNode().issue_, uInAccountID, node().offerOwnerAccount_, previousNode().transferRate_); // First calculate assuming no output fees: saInPassAct, // saInPassFees, saOutPassAct. // Offer maximum out - limited by funds with out fees. auto saOutFunded = std::min ( node().saOfferFunds, node().saTakerGets); // Offer maximum out - limit by most to deliver. auto saOutPassFunded = std::min ( saOutFunded, node().saRevDeliver - node().saFwdDeliver); // Offer maximum in - Limited by by payout. auto saInFunded = mulRound ( saOutPassFunded, node().saOfrRate, node().saTakerPays.issue (), true); // Offer maximum in with fees. auto saInTotal = multiplyRound ( saInFunded, xferRate, true); auto saInRemaining = saInReq - saInAct - saInFees; if (saInRemaining < beast::zero) saInRemaining.clear(); // In limited by remaining. auto saInSum = std::min (saInTotal, saInRemaining); // In without fees. auto saInPassAct = std::min ( node().saTakerPays, divideRound (saInSum, xferRate, true)); // Out limited by in remaining. auto outPass = divRound ( saInPassAct, node().saOfrRate, node().saTakerGets.issue (), true); STAmount saOutPassMax = std::min (saOutPassFunded, outPass); STAmount saInPassFeesMax = saInSum - saInPassAct; // Will be determined by next node(). STAmount saOutPassAct; // Will be determined by adjusted saInPassAct. STAmount saInPassFees; JLOG (j_.trace()) << "deliverNodeForward:" << " nodeIndex_=" << nodeIndex_ << " saOutFunded=" << saOutFunded << " saOutPassFunded=" << saOutPassFunded << " node().saOfferFunds=" << node().saOfferFunds << " node().saTakerGets=" << node().saTakerGets << " saInReq=" << saInReq << " saInAct=" << saInAct << " saInFees=" << saInFees << " saInFunded=" << saInFunded << " saInTotal=" << saInTotal << " saInSum=" << saInSum << " saInPassAct=" << saInPassAct << " saOutPassMax=" << saOutPassMax; // FIXME: We remove an offer if WE didn't want anything out of it? if (!node().saTakerPays || saInSum <= beast::zero) { JLOG (j_.debug()) << "deliverNodeForward: Microscopic offer unfunded."; // After math offer is effectively unfunded. pathState_.unfundedOffers().push_back (node().offerIndex_); node().bEntryAdvance = true; continue; } if (!saInFunded) { // Previous check should catch this. JLOG (j_.warn()) << "deliverNodeForward: UNREACHABLE REACHED"; // After math offer is effectively unfunded. pathState_.unfundedOffers().push_back (node().offerIndex_); node().bEntryAdvance = true; continue; } if (!isXRP(nextNode().account_)) { // ? --> 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; JLOG (j_.trace()) << "deliverNodeForward: ? --> OFFER --> account:" << " offerOwnerAccount_=" << node().offerOwnerAccount_ << " nextNode().account_=" << nextNode().account_ << " saOutPassAct=" << saOutPassAct << " saOutFunded=" << saOutFunded; // Output: Debit offer owner, send XRP or non-XPR to next // account. resultCode = accountSend(view(), node().offerOwnerAccount_, nextNode().account_, saOutPassAct, viewJ); if (resultCode != tesSUCCESS) 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. resultCode = increment().deliverNodeForward ( node().offerOwnerAccount_, // --> Current holder. saOutPassMax, // --> Amount available. saOutPassAct, // <-- Amount delivered. saOutPassFees, // <-- Fees charged. saInAct > beast::zero); if (resultCode != tesSUCCESS) 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); auto inPassAct = mulRound ( saOutPassAct, node().saOfrRate, saInReq.issue (), true); saInPassAct = std::min (node().saTakerPays, inPassAct); auto inPassFees = multiplyRound ( saInPassAct, xferRate, true); saInPassFees = std::min (saInPassFeesMax, inPassFees); } // Do outbound debiting. // Send to issuer/limbo total amount including fees (issuer gets // fees). auto const& id = isXRP(node().issue_) ? xrpAccount() : node().issue_.account; auto outPassTotal = saOutPassAct + saOutPassFees; accountSend(view(), node().offerOwnerAccount_, id, outPassTotal, viewJ); JLOG (j_.trace()) << "deliverNodeForward: ? --> OFFER --> offer:" << " saOutPassAct=" << saOutPassAct << " saOutPassFees=" << saOutPassFees; } JLOG (j_.trace()) << "deliverNodeForward: " << " nodeIndex_=" << nodeIndex_ << " node().saTakerGets=" << node().saTakerGets << " node().saTakerPays=" << node().saTakerPays << " saInPassAct=" << saInPassAct << " saInPassFees=" << saInPassFees << " saOutPassAct=" << saOutPassAct << " saOutFunded=" << saOutFunded; // Funds were spent. node().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 (isXRP (previousNode().issue_.currency) || uInAccountID != node().offerOwnerAccount_) { auto id = !isXRP(previousNode().issue_.currency) ? uInAccountID : xrpAccount(); resultCode = accountSend(view(), id, node().offerOwnerAccount_, saInPassAct, viewJ); if (resultCode != tesSUCCESS) break; } // Adjust offer. // // Fees are considered paid from a seperate budget and are not named // in the offer. STAmount saTakerGetsNew = node().saTakerGets - saOutPassAct; STAmount saTakerPaysNew = node().saTakerPays - saInPassAct; if (saTakerPaysNew < beast::zero || saTakerGetsNew < beast::zero) { JLOG (j_.warn()) << "deliverNodeForward: NEGATIVE:" << " saTakerPaysNew=" << saTakerPaysNew << " saTakerGetsNew=" << saTakerGetsNew; resultCode = telFAILED_PROCESSING; break; } node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); view().update (node().sleOffer); if (saOutPassAct == saOutFunded || saTakerGetsNew == beast::zero) { // Offer became unfunded. JLOG (j_.debug()) << "deliverNodeForward: unfunded:" << " saOutPassAct=" << saOutPassAct << " saOutFunded=" << saOutFunded; pathState_.unfundedOffers().push_back (node().offerIndex_); node().bEntryAdvance = true; } else { if (saOutPassAct >= saOutFunded) { JLOG (j_.warn()) << "deliverNodeForward: TOO MUCH:" << " saOutPassAct=" << saOutPassAct << " saOutFunded=" << saOutFunded; } assert (saOutPassAct < saOutFunded); } saInAct += saInPassAct; saInFees += saInPassFees; // Adjust amount available to next node(). node().saFwdDeliver = std::min (node().saRevDeliver, node().saFwdDeliver + saOutPassAct); } } JLOG (j_.trace()) << "deliverNodeForward<" << " nodeIndex_=" << nodeIndex_ << " saInAct=" << saInAct << " saInFees=" << saInFees; return resultCode; }
// To deliver from an order book, when computing TER PathCursor::deliverNodeReverseImpl ( AccountID const& uOutAccountID, // --> Output owner's account. STAmount const& saOutReq, // --> Funds requested to be // delivered for an increment. STAmount& saOutAct, // <-- Funds actually delivered for an // increment bool callerHasLiquidity ) const { TER resultCode = tesSUCCESS; // Accumulation of what the previous node must deliver. // Possible optimization: Note this gets zeroed on each increment, ideally // only on first increment, then it could be a limit on the forward pass. saOutAct.clear (saOutReq); JLOG (j_.trace()) << "deliverNodeReverse>" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq << " saPrvDlvReq=" << previousNode().saRevDeliver; assert (saOutReq != zero); int loopCount = 0; auto viewJ = rippleCalc_.logs_.journal ("View"); // While we did not deliver as much as requested: while (saOutAct < saOutReq) { if (++loopCount > (multiQuality_ ? CALC_NODE_DELIVER_MAX_LOOPS_MQ : CALC_NODE_DELIVER_MAX_LOOPS)) { JLOG (j_.warn()) << "loop count exceeded"; return telFAILED_PROCESSING; } resultCode = advanceNode (saOutAct, true, callerHasLiquidity); // If needed, advance to next funded offer. if (resultCode != tesSUCCESS || !node().offerIndex_) // Error or out of offers. break; auto const xferRate = effectiveRate ( node().issue_, uOutAccountID, node().offerOwnerAccount_, node().transferRate_); JLOG (j_.trace()) << "deliverNodeReverse:" << " offerOwnerAccount_=" << node().offerOwnerAccount_ << " uOutAccountID=" << uOutAccountID << " node().issue_.account=" << node().issue_.account << " xferRate=" << xferRate; // Only use rate when not in multi-quality mode if (!multiQuality_) { if (!node().rateMax) { // Set initial rate. JLOG (j_.trace()) << "Set initial rate"; node().rateMax = xferRate; } else if (xferRate > node().rateMax) { // Offer exceeds initial rate. JLOG (j_.trace()) << "Offer exceeds initial rate: " << *node().rateMax; break; // Done. Don't bother looking for smaller transferRates. } else if (xferRate < node().rateMax) { // Reducing rate. Additional offers will only // be 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 xferRate, it // would be wrong to add anything with a rate // above xferRate. // // The rate would be reduced if the current // offer was from the issuer and the previous // offer wasn't. JLOG (j_.trace()) << "Reducing rate: " << *node().rateMax; node().rateMax = xferRate; } } // Amount that goes to the taker. STAmount saOutPassReq = std::min ( std::min (node().saOfferFunds, node().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 = multiplyRound ( saOutPassAct, xferRate, false); // Offer out with fees. JLOG (j_.trace()) << "deliverNodeReverse:" << " saOutReq=" << saOutReq << " saOutAct=" << saOutAct << " node().saTakerGets=" << node().saTakerGets << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees << " node().saOfferFunds=" << node().saOfferFunds; if (saOutPlusFees > node().saOfferFunds) { // Offer owner can not cover all fees, compute saOutPassAct based on // node().saOfferFunds. saOutPlusFees = node().saOfferFunds; // Round up: prefer liquidity rather than microscopic fees. But, // limit by requested. auto fee = divideRound (saOutPlusFees, xferRate, true); saOutPassAct = std::min (saOutPassReq, fee); JLOG (j_.trace()) << "deliverNodeReverse: Total exceeds fees:" << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees << " node().saOfferFunds=" << node().saOfferFunds; } // Compute portion of input needed to cover actual output. auto outputFee = mulRound ( saOutPassAct, node().saOfrRate, node().saTakerPays.issue (), true); if (*stAmountCalcSwitchover == false && ! outputFee) { JLOG (j_.fatal()) << "underflow computing outputFee " << "saOutPassAct: " << saOutPassAct << " saOfrRate: " << node ().saOfrRate; return telFAILED_PROCESSING; } STAmount saInPassReq = std::min (node().saTakerPays, outputFee); STAmount saInPassAct; JLOG (j_.trace()) << "deliverNodeReverse:" << " outputFee=" << outputFee << " saInPassReq=" << saInPassReq << " node().saOfrRate=" << node().saOfrRate << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees; if (!saInPassReq) // FIXME: This is bogus { // After rounding did not want anything. JLOG (j_.debug()) << "deliverNodeReverse: micro offer is unfunded."; node().bEntryAdvance = true; continue; } // Find out input amount actually available at current rate. else if (!isXRP(previousNode().account_)) { // 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; JLOG (j_.trace()) << "deliverNodeReverse: account --> OFFER --> ? :" << " saInPassAct=" << saInPassAct; } else { // offer --> OFFER --> ? // Compute in previous offer node how much could come in. // TODO(tom): Fix nasty recursion here! resultCode = increment(-1).deliverNodeReverseImpl( node().offerOwnerAccount_, saInPassReq, saInPassAct, saOutAct > zero); if (amendmentRIPD1141(view().info().parentCloseTime)) { // The recursive call is dry this time, but we have liquidity // from previous calls if (resultCode == tecPATH_DRY && saOutAct > zero) { resultCode = tesSUCCESS; break; } } JLOG (j_.trace()) << "deliverNodeReverse: offer --> OFFER --> ? :" << " saInPassAct=" << saInPassAct; } if (resultCode != tesSUCCESS) break; if (saInPassAct < saInPassReq) { // Adjust output to conform to limited input. auto outputRequirements = divRound (saInPassAct, node ().saOfrRate, node ().saTakerGets.issue (), true); saOutPassAct = std::min (saOutPassReq, outputRequirements); auto outputFees = multiplyRound (saOutPassAct, xferRate, true); saOutPlusFees = std::min (node().saOfferFunds, outputFees); JLOG (j_.trace()) << "deliverNodeReverse: adjusted:" << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees; } else { // TODO(tom): more logging here. assert (saInPassAct == saInPassReq); } // Funds were spent. node().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. resultCode = accountSend(view(), node().offerOwnerAccount_, node().issue_.account, saOutPassAct, viewJ); if (resultCode != tesSUCCESS) break; // Adjust offer STAmount saTakerGetsNew = node().saTakerGets - saOutPassAct; STAmount saTakerPaysNew = node().saTakerPays - saInPassAct; if (saTakerPaysNew < zero || saTakerGetsNew < zero) { JLOG (j_.warn()) << "deliverNodeReverse: NEGATIVE:" << " node().saTakerPaysNew=" << saTakerPaysNew << " node().saTakerGetsNew=" << saTakerGetsNew; resultCode = telFAILED_PROCESSING; break; } node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); view().update (node().sleOffer); if (saOutPassAct == node().saTakerGets) { // Offer became unfunded. JLOG (j_.debug()) << "deliverNodeReverse: offer became unfunded."; node().bEntryAdvance = true; // XXX When don't we want to set advance? } else { assert (saOutPassAct < node().saTakerGets); } saOutAct += saOutPassAct; // Accumulate what is to be delivered from previous node. previousNode().saRevDeliver += saInPassAct; } if (saOutAct > saOutReq) { JLOG (j_.warn()) << "deliverNodeReverse: TOO MUCH:" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq; } assert(saOutAct <= saOutReq); if (resultCode == tesSUCCESS && !saOutAct) resultCode = tecPATH_DRY; // Unable to meet request, consider path dry. // Design invariant: if nothing was actually delivered, return tecPATH_DRY. JLOG (j_.trace()) << "deliverNodeReverse<" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq << " saPrvDlvReq=" << previousNode().saRevDeliver; return resultCode; }
// To deliver from an order book, when computing TER PathCursor::deliverNodeReverse ( Account const& uOutAccountID, // --> Output owner's account. STAmount const& saOutReq, // --> Funds requested to be // delivered for an increment. STAmount& saOutAct) const // <-- Funds actually delivered for an // increment. { TER resultCode = tesSUCCESS; node().directory.restart(multiQuality_); // Accumulation of what the previous node must deliver. // Possible optimization: Note this gets zeroed on each increment, ideally // only on first increment, then it could be a limit on the forward pass. saOutAct.clear (saOutReq); WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse>" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq << " saPrvDlvReq=" << previousNode().saRevDeliver; assert (saOutReq != zero); int loopCount = 0; // While we did not deliver as much as requested: while (saOutAct < saOutReq) { if (++loopCount > CALC_NODE_DELIVER_MAX_LOOPS) { WriteLog (lsFATAL, RippleCalc) << "loop count exceeded"; return telFAILED_PROCESSING; } resultCode = advanceNode (saOutAct, true); // If needed, advance to next funded offer. if (resultCode != tesSUCCESS || !node().offerIndex_) // Error or out of offers. break; auto const hasFee = node().offerOwnerAccount_ == node().issue_.account || uOutAccountID == node().issue_.account; // Issuer sending or receiving. const STAmount saOutFeeRate = hasFee ? saOne // No fee. : node().transferRate_; // Transfer rate of issuer. WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse:" << " offerOwnerAccount_=" << node().offerOwnerAccount_ << " uOutAccountID=" << uOutAccountID << " node().issue_.account=" << node().issue_.account << " node().transferRate_=" << node().transferRate_ << " saOutFeeRate=" << saOutFeeRate; if (multiQuality_) { // In multi-quality mode, ignore rate. } else if (!node().saRateMax) { // Set initial rate. node().saRateMax = saOutFeeRate; WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse: Set initial rate:" << " node().saRateMax=" << node().saRateMax << " saOutFeeRate=" << saOutFeeRate; } else if (saOutFeeRate > node().saRateMax) { // Offer exceeds initial rate. WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse: Offer exceeds initial rate:" << " node().saRateMax=" << node().saRateMax << " saOutFeeRate=" << saOutFeeRate; break; // Done. Don't bother looking for smaller transferRates. } else if (saOutFeeRate < node().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 anything with // a rate above saOutFeeRate. // // The rate would be reduced if the current offer was from the // issuer and the previous offer wasn't. node().saRateMax = saOutFeeRate; WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse: Reducing rate:" << " node().saRateMax=" << node().saRateMax; } // Amount that goes to the taker. STAmount saOutPassReq = std::min ( std::min (node().saOfferFunds, node().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 = mulRound ( saOutPassAct, saOutFeeRate, saOutPassAct.issue (), false); // Offer out with fees. WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse:" << " saOutReq=" << saOutReq << " saOutAct=" << saOutAct << " node().saTakerGets=" << node().saTakerGets << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees << " node().saOfferFunds=" << node().saOfferFunds; if (saOutPlusFees > node().saOfferFunds) { // Offer owner can not cover all fees, compute saOutPassAct based on // node().saOfferFunds. saOutPlusFees = node().saOfferFunds; // Round up: prefer liquidity rather than microscopic fees. But, // limit by requested. auto fee = divRound (saOutPlusFees, saOutFeeRate, saOutPlusFees.issue (), true); saOutPassAct = std::min (saOutPassReq, fee); WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse: Total exceeds fees:" << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees << " node().saOfferFunds=" << node().saOfferFunds; } // Compute portion of input needed to cover actual output. auto outputFee = mulRound ( saOutPassAct, node().saOfrRate, node().saTakerPays.issue (), true); STAmount saInPassReq = std::min (node().saTakerPays, outputFee); STAmount saInPassAct; WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse:" << " outputFee=" << outputFee << " saInPassReq=" << saInPassReq << " node().saOfrRate=" << node().saOfrRate << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees; if (!saInPassReq) // FIXME: This is bogus { // After rounding did not want anything. WriteLog (lsDEBUG, RippleCalc) << "deliverNodeReverse: micro offer is unfunded."; node().bEntryAdvance = true; continue; } // Find out input amount actually available at current rate. else if (!isXRP(previousNode().account_)) { // 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 (lsTRACE, RippleCalc) << "deliverNodeReverse: account --> OFFER --> ? :" << " saInPassAct=" << saInPassAct; } else { // offer --> OFFER --> ? // Compute in previous offer node how much could come in. // TODO(tom): Fix nasty recursion here! resultCode = increment(-1).deliverNodeReverse( node().offerOwnerAccount_, saInPassReq, saInPassAct); WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse: offer --> OFFER --> ? :" << " saInPassAct=" << saInPassAct; } if (resultCode != tesSUCCESS) break; if (saInPassAct < saInPassReq) { // Adjust output to conform to limited input. auto outputRequirements = divRound ( saInPassAct, node().saOfrRate, node().saTakerGets.issue (), true); saOutPassAct = std::min (saOutPassReq, outputRequirements); auto outputFees = mulRound ( saOutPassAct, saOutFeeRate, saOutPassAct.issue (), true); saOutPlusFees = std::min (node().saOfferFunds, outputFees); WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse: adjusted:" << " saOutPassAct=" << saOutPassAct << " saOutPlusFees=" << saOutPlusFees; } else { // TODO(tom): more logging here. assert (saInPassAct == saInPassReq); } // Funds were spent. node().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. resultCode = ledger().accountSend ( node().offerOwnerAccount_, node().issue_.account, saOutPassAct); if (resultCode != tesSUCCESS) break; // Adjust offer STAmount saTakerGetsNew = node().saTakerGets - saOutPassAct; STAmount saTakerPaysNew = node().saTakerPays - saInPassAct; if (saTakerPaysNew < zero || saTakerGetsNew < zero) { WriteLog (lsWARNING, RippleCalc) << "deliverNodeReverse: NEGATIVE:" << " node().saTakerPaysNew=" << saTakerPaysNew << " node().saTakerGetsNew=" << saTakerGetsNew; resultCode = telFAILED_PROCESSING; break; } node().sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew); node().sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew); ledger().entryModify (node().sleOffer); if (saOutPassAct == node().saTakerGets) { // Offer became unfunded. WriteLog (lsDEBUG, RippleCalc) << "deliverNodeReverse: offer became unfunded."; node().bEntryAdvance = true; // XXX When don't we want to set advance? } else { assert (saOutPassAct < node().saTakerGets); } saOutAct += saOutPassAct; // Accumulate what is to be delivered from previous node. previousNode().saRevDeliver += saInPassAct; } CondLog (saOutAct > saOutReq, lsWARNING, RippleCalc) << "deliverNodeReverse: TOO MUCH:" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq; assert (saOutAct <= saOutReq); if (resultCode == tesSUCCESS && !saOutAct) resultCode = tecPATH_DRY; // Unable to meet request, consider path dry. // Design invariant: if nothing was actually delivered, return tecPATH_DRY. WriteLog (lsTRACE, RippleCalc) << "deliverNodeReverse<" << " saOutAct=" << saOutAct << " saOutReq=" << saOutReq << " saPrvDlvReq=" << previousNode().saRevDeliver; return resultCode; }
void rippleLiquidity ( RippleCalc& rippleCalc, std::uint32_t const uQualityIn, std::uint32_t const uQualityOut, STAmount const& saPrvReq, // --> in limit including fees, <0 = unlimited STAmount const& saCurReq, // --> out limit STAmount& saPrvAct, // <-> in limit including achieved so far: <-- <= --> STAmount& saCurAct, // <-> out limit including achieved so far: <-- <= --> std::uint64_t& uRateMax) { WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity>" << " uQualityIn=" << uQualityIn << " uQualityOut=" << uQualityOut << " saPrvReq=" << saPrvReq << " saCurReq=" << saCurReq << " saPrvAct=" << saPrvAct << " saCurAct=" << saCurAct; // saCurAct was once zero in a production server. assert (saCurReq != zero); assert (saCurReq > zero); assert (saPrvReq.getCurrency () == saCurReq.getCurrency ()); assert (saPrvReq.getCurrency () == saPrvAct.getCurrency ()); assert (saPrvReq.getIssuer () == saPrvAct.getIssuer ()); const bool bPrvUnlimited = (saPrvReq < zero); // -1 means unlimited. // Unlimited stays unlimited - don't do calculations. // How much could possibly flow through the previous node? const STAmount saPrv = bPrvUnlimited ? saPrvReq : saPrvReq - saPrvAct; // How much could possibly flow through the current node? const STAmount saCur = saCurReq - saCurAct; WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity: " << " bPrvUnlimited=" << bPrvUnlimited << " saPrv=" << saPrv << " saCur=" << saCur; // If nothing can flow, we might as well not do any work. if (saPrv == zero || saCur == zero) return; if (uQualityIn >= uQualityOut) { // You're getting better quality than you asked for, so no fee. WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity: No fees"; // Only process if the current rate, 1:1, is not worse than the previous // rate, uRateMax - otherwise there is no flow. if (!uRateMax || STAmount::uRateOne <= uRateMax) { // Limit amount to transfer if need - the minimum of amount being // paid and the amount that's wanted. 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. // // This is the actual flow. saPrvAct += saTransfer; saCurAct += saTransfer; // If no rate limit, set rate limit to avoid combining with // something with a worse rate. if (uRateMax == 0) uRateMax = STAmount::uRateOne; } } else { // If the quality is worse than the previous WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity: Fee"; std::uint64_t uRate = getRate ( STAmount (uQualityOut), STAmount (uQualityIn)); // If the next rate is at least as good as the current rate, process. if (!uRateMax || uRate <= uRateMax) { auto currency = saCur.getCurrency (); auto uCurIssuerID = saCur.getIssuer (); // current actual = current request * (quality out / quality in). auto numerator = mulRound ( saCur, uQualityOut, {currency, uCurIssuerID}, true); // True means "round up" to get best flow. STAmount saCurIn = divRound ( numerator, uQualityIn, {currency, uCurIssuerID}, true); WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity:" << " bPrvUnlimited=" << bPrvUnlimited << " saPrv=" << saPrv << " saCurIn=" << saCurIn; if (bPrvUnlimited || saCurIn <= saPrv) { // All of current. Some amount of previous. saCurAct += saCur; saPrvAct += saCurIn; WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity:3c:" << " saCurReq=" << saCurReq << " saPrvAct=" << saPrvAct; } else { // There wasn't enough money to start with - so given the // limited input, how much could we deliver? // current actual = previous request // * (quality in / quality out). // This is inverted compared to the code above because we're // going the other way Issue issue{currency, uCurIssuerID}; auto numerator = mulRound ( saPrv, uQualityIn, issue, true); // A part of current. All of previous. (Cur is the driver // variable.) STAmount saCurOut = divRound ( numerator, uQualityOut, issue, true); WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity:4: saCurReq=" << saCurReq; saCurAct += saCurOut; saPrvAct = saPrvReq; } if (!uRateMax) uRateMax = uRate; } } WriteLog (lsTRACE, RippleCalc) << "rippleLiquidity<" << " uQualityIn=" << uQualityIn << " uQualityOut=" << uQualityOut << " saPrvReq=" << saPrvReq << " saCurReq=" << saCurReq << " saPrvAct=" << saPrvAct << " saCurAct=" << saCurAct; }
nsecs_t VideoFrameScheduler::schedule(nsecs_t renderTime) { nsecs_t origRenderTime = renderTime; nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (now >= mVsyncRefreshAt) { updateVsync(); } // without VSYNC info, there is nothing to do if (mVsyncPeriod == 0) { ALOGV("no vsync: render=%lld", (long long)renderTime); return renderTime; } // ensure vsync time is well before (corrected) render time if (mVsyncTime > renderTime - 4 * mVsyncPeriod) { mVsyncTime -= ((mVsyncTime - renderTime) / mVsyncPeriod + 5) * mVsyncPeriod; } // Video presentation takes place at the VSYNC _after_ renderTime. Adjust renderTime // so this effectively becomes a rounding operation (to the _closest_ VSYNC.) renderTime -= mVsyncPeriod / 2; const nsecs_t videoPeriod = mPll.addSample(origRenderTime); if (videoPeriod > 0) { // Smooth out rendering size_t N = 12; nsecs_t fiveSixthDev = abs(((videoPeriod * 5 + mVsyncPeriod) % (mVsyncPeriod * 6)) - mVsyncPeriod) / (mVsyncPeriod / 100); // use 20 samples if we are doing 5:6 ratio +- 1% (e.g. playing 50Hz on 60Hz) if (fiveSixthDev < 12) { /* 12% / 6 = 2% */ N = 20; } nsecs_t offset = 0; nsecs_t edgeRemainder = 0; for (size_t i = 1; i <= N; i++) { offset += (renderTime + mTimeCorrection + videoPeriod * i - mVsyncTime) % mVsyncPeriod; edgeRemainder += (videoPeriod * i) % mVsyncPeriod; } mTimeCorrection += mVsyncPeriod / 2 - offset / N; renderTime += mTimeCorrection; nsecs_t correctionLimit = mVsyncPeriod * 3 / 5; edgeRemainder = abs(edgeRemainder / N - mVsyncPeriod / 2); if (edgeRemainder <= mVsyncPeriod / 3) { correctionLimit /= 2; } // estimate how many VSYNCs a frame will spend on the display nsecs_t nextVsyncTime = renderTime + mVsyncPeriod - ((renderTime - mVsyncTime) % mVsyncPeriod); if (mLastVsyncTime >= 0) { size_t minVsyncsPerFrame = videoPeriod / mVsyncPeriod; size_t vsyncsForLastFrame = divRound(nextVsyncTime - mLastVsyncTime, mVsyncPeriod); bool vsyncsPerFrameAreNearlyConstant = periodicError(videoPeriod, mVsyncPeriod) / (mVsyncPeriod / 20) == 0; if (mTimeCorrection > correctionLimit && (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame > minVsyncsPerFrame)) { // remove a VSYNC mTimeCorrection -= mVsyncPeriod / 2; renderTime -= mVsyncPeriod / 2; nextVsyncTime -= mVsyncPeriod; --vsyncsForLastFrame; } else if (mTimeCorrection < -correctionLimit && (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame == minVsyncsPerFrame)) { // add a VSYNC mTimeCorrection += mVsyncPeriod / 2; renderTime += mVsyncPeriod / 2; nextVsyncTime += mVsyncPeriod; ++vsyncsForLastFrame; } ATRACE_INT("FRAME_VSYNCS", vsyncsForLastFrame); } mLastVsyncTime = nextVsyncTime; } // align rendertime to the center between VSYNC edges renderTime -= (renderTime - mVsyncTime) % mVsyncPeriod; renderTime += mVsyncPeriod / 2; ALOGV("adjusting render: %lld => %lld", (long long)origRenderTime, (long long)renderTime); ATRACE_INT("FRAME_FLIP_IN(ms)", (renderTime - now) / 1000000); return renderTime; }