from datetime import datetime

def presentValue(cf, r, n):
    """Calculate present value of cash flows."""
    PV = 0
    for i in range(0, n+1):
        PV = PV + cf[i]/((1+r)**i)
    return PV

def futureValue(cf, r, n):
    """Calculate future value of cash flows."""
    PV = presentValue(cf, r, n)
    FV = PV*(1+r)**n
    return FV

def annuity(PV, r, n):
    """Calculate annuity payment."""
    A = (PV*r)/(1-(1+r)**(-n))
    return A

def emi(PV, r, n):
    """Calculate EMI (Equated Monthly Installment)."""
    emi = annuity(PV, r, n)
    return emi

def IRR(cashflows, guess=0.1, tol=1e-6, max_iter=1000):
    """Calculate IRR using Newton-Raphson method."""
    if not (any(cf > 0 for cf in cashflows) and any(cf < 0 for cf in cashflows)):
        return "IRR not found: cashflows must contain at least one positive and one negative value."

    sign_changes = sum(
        1 for i in range(1, len(cashflows))
        if cashflows[i] * cashflows[i - 1] < 0
    )
    if sign_changes > 1:
        return "IRR not found: multiple sign changes detected (multiple IRRs possible)."

    rate = guess
    for _ in range(max_iter):
        npv = sum(cf / (1 + rate) ** i for i, cf in enumerate(cashflows))
        d_npv = sum(-i * cf / (1 + rate) ** (i + 1) for i, cf in enumerate(cashflows))

        if abs(d_npv) < 1e-10:
            return "IRR not found: derivative near zero, Newton-Raphson failed."

        if abs(npv) < tol:
            return rate

        rate -= npv / d_npv

    return "IRR not found: method did not converge within iteration limit."

def calculate_mirr(cashflows, finance_rate, reinvest_rate):
    """Calculate Modified Internal Rate of Return (MIRR)."""
    n = len(cashflows)

    if not (any(cf < 0 for cf in cashflows) and any(cf > 0 for cf in cashflows)):
        return "MIRR not defined: cashflows must include at least one positive and one negative value."

    pv_neg = sum(cf / (1 + finance_rate) ** t for t, cf in enumerate(cashflows) if cf < 0)
    fv_pos = sum(cf * (1 + reinvest_rate) ** (n - 1 - t) for t, cf in enumerate(cashflows) if cf > 0)

    mirr = (fv_pos / abs(pv_neg)) ** (1 / (n - 1)) - 1
    return mirr

def calculate_xirr(cashflows, dates, guess=0.1, tol=1e-6, max_iter=100):
    """Calculate XIRR (Extended Internal Rate of Return)."""
    if len(cashflows) != len(dates):
        return "XIRR not found: cashflows and dates length mismatch."

    if not (any(cf > 0 for cf in cashflows) and any(cf < 0 for cf in cashflows)):
        return "XIRR not found: must have at least one positive and one negative cashflow."

    t0 = dates[0]
    years = [(d - t0).days / 365.0 for d in dates]

    def xnpv(rate):
        if rate <= -1:
            return float("inf")
        return sum(cf / (1 + rate) ** t for cf, t in zip(cashflows, years))

    def dxnpv(rate):
        return sum(-t * cf / (1 + rate) ** (t + 1) for cf, t in zip(cashflows, years))

    rate = guess
    for _ in range(max_iter):
        npv = xnpv(rate)
        d_npv = dxnpv(rate)

        if abs(npv) < tol:
            return rate

        if abs(d_npv) < 1e-10:
            break

        rate -= npv / d_npv

    low, high = -0.9999, 10.0
    for _ in range(max_iter * 10):
        mid = (low + high) / 2
        npv_mid = xnpv(mid)

        if abs(npv_mid) < tol:
            return mid

        if xnpv(low) * npv_mid < 0:
            high = mid
        else:
            low = mid

    return "XIRR not found: failed to converge."