/// <summary>
        /// Computes the present value of the payment by discounting.
        /// <para>
        /// The present value is zero if the payment date is before the valuation date.
        ///
        /// </para>
        /// </summary>
        /// <param name="payment">  the payment </param>
        /// <param name="provider">  the provider </param>
        /// <returns> the present value </returns>
        public virtual double presentValueAmount(Payment payment, BaseProvider provider)
        {
            // duplicated code to avoid looking up in the provider when not necessary
            if (provider.ValuationDate.isAfter(payment.Date))
            {
                return(0d);
            }
            double df = provider.discountFactor(payment.Currency, payment.Date);

            return(payment.Amount * df);
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Calculates the future cash flow of the payment.
        /// <para>
        /// The cash flow is returned, empty if the payment has already occurred.
        ///
        /// </para>
        /// </summary>
        /// <param name="payment">  the payment </param>
        /// <param name="provider">  the provider </param>
        /// <returns> the cash flow, empty if the payment has occurred </returns>
        public virtual CashFlows cashFlows(Payment payment, BaseProvider provider)
        {
            if (provider.ValuationDate.isAfter(payment.Date))
            {
                return(CashFlows.NONE);
            }
            double   df   = provider.discountFactor(payment.Currency, payment.Date);
            CashFlow flow = CashFlow.ofForecastValue(payment.Date, payment.Currency, payment.Amount, df);

            return(CashFlows.of(flow));
        }
        //-------------------------------------------------------------------------
        /// <summary>
        /// Computes the present value of the payment by discounting.
        /// <para>
        /// The present value is zero if the payment date is before the valuation date.
        ///
        /// </para>
        /// </summary>
        /// <param name="payment">  the payment </param>
        /// <param name="provider">  the provider </param>
        /// <returns> the present value </returns>
        public virtual CurrencyAmount presentValue(Payment payment, BaseProvider provider)
        {
            // duplicated code to avoid looking up in the provider when not necessary
            if (provider.ValuationDate.isAfter(payment.Date))
            {
                return(CurrencyAmount.zero(payment.Currency));
            }
            double df = provider.discountFactor(payment.Currency, payment.Date);

            return(payment.Value.multipliedBy(df));
        }
        /// <summary>
        /// Explains the present value of the payment.
        /// <para>
        /// This returns explanatory information about the calculation.
        ///
        /// </para>
        /// </summary>
        /// <param name="payment">  the payment </param>
        /// <param name="provider">  the provider </param>
        /// <returns> the explanatory information </returns>
        public virtual ExplainMap explainPresentValue(Payment payment, BaseProvider provider)
        {
            Currency  currency    = payment.Currency;
            LocalDate paymentDate = payment.Date;

            ExplainMapBuilder builder = ExplainMap.builder();

            builder.put(ExplainKey.ENTRY_TYPE, "Payment");
            builder.put(ExplainKey.PAYMENT_DATE, paymentDate);
            builder.put(ExplainKey.PAYMENT_CURRENCY, currency);
            if (paymentDate.isBefore(provider.ValuationDate))
            {
                builder.put(ExplainKey.COMPLETED, true);
                builder.put(ExplainKey.FORECAST_VALUE, CurrencyAmount.zero(currency));
                builder.put(ExplainKey.PRESENT_VALUE, CurrencyAmount.zero(currency));
            }
            else
            {
                builder.put(ExplainKey.DISCOUNT_FACTOR, provider.discountFactor(currency, paymentDate));
                builder.put(ExplainKey.FORECAST_VALUE, forecastValue(payment, provider));
                builder.put(ExplainKey.PRESENT_VALUE, presentValue(payment, provider));
            }
            return(builder.build());
        }