function calculate() {
    if (!validateNumber(document.getElementById("fixed_rate"), 1, 99, "Fixed interest rate")
       || !validateNumber(document.getElementById("loan_amount"), 1, 10000000, "Loan Amount")
       || !validateNumber(document.getElementById("expected_life"), 0.08333, 40, "Years you expect to live in house")
       || !validateNumber(document.getElementById("init_adj_rate"), 0, 99, "Initial adjustable interest rate")
       || !validateNumber(document.getElementById("years_before_adj"), 0.08333, 40, "Years before subsequent adjustments")
       || !validateNumber(document.getElementById("max_increase"), 1, 99, "Maximum increase per period")
       || !validateNumber(document.getElementById("cap"), 1, 99, "Lifetime cap on interest rate increases")
       || !validateNumber(document.getElementById("loan_term"), 0.08333, 40, "Loan Term (in years)")) {
        return false;
    }

    var fixedRate = parseFloat(document.getElementById("fixed_rate").value);
    var principal = parseFloat(document.getElementById("loan_amount").value);
    var loanTermInYears = parseInt(document.getElementById("loan_term").value);
    var expectedLife = parseFloat(document.getElementById("expected_life").value);
    var initAdjRate = parseFloat(document.getElementById("init_adj_rate").value);
    var yearsBeforeAdj = parseFloat(document.getElementById("years_before_adj").value);
    var yearsBetweenAdj = parseFloat(document.getElementById("years_between_adj").value);
    var maxIncrease = parseFloat(document.getElementById("max_increase").value);
    var lifetimeCap = parseFloat(document.getElementById("cap").value);

    if (expectedLife > loanTermInYears) {
        expectedLife = loanTermInYears;
    }

    if (yearsBeforeAdj > loanTermInYears) {
        alert("Years before first adjustment cannot exceed the loan term.");
        return false;
    }

    if (yearsBeforeAdj + yearsBetweenAdj > loanTermInYears) {
        alert("Invalid years between subsequent adjustments.");
        return false;
    }


    var fixedPayment = getPayment(fixedRate, loanTermInYears * 12, principal);
    var fixedTotalPaid = fixedPayment * expectedLife * 12;
    var fixedBalance = getBalance(principal, fixedRate, fixedPayment, expectedLife * 12);
    var fixedInterestPaid = getInterestPaid(fixedTotalPaid, fixedBalance, principal);

    var bestAdjPayment = getPayment(initAdjRate, loanTermInYears * 12, principal);
    var bestAdjTotalPaid = bestAdjPayment * expectedLife * 12;
    var bestAdjBalance = getBalance(principal, initAdjRate, bestAdjPayment, expectedLife * 12);
    var bestAdjInterestPaid = getInterestPaid(bestAdjTotalPaid, bestAdjBalance, principal);

    var worstAdjTotalPaid = 0;
    var worstAdjBalance = 0;
    if (expectedLife <= yearsBeforeAdj) {
        worstAdjTotalPaid = bestAdjTotalPaid;
        worstAdjBalance = bestAdjBalance;
    } else {
        worstAdjTotalPaid += bestAdjPayment * yearsBeforeAdj * 12;
        worstAdjBalance = getBalance(principal, initAdjRate, bestAdjPayment, yearsBeforeAdj * 12);
        var expectedYearsLeft = expectedLife - yearsBeforeAdj;
        var yearsLeftOnLoan = loanTermInYears - yearsBeforeAdj;
        var newAdjRate = initAdjRate + maxIncrease;
        if (newAdjRate > lifetimeCap) {
            newAdjRate = lifetimeCap;
        }
        while (expectedYearsLeft > 0) {
            var worstAdjPayment = getPayment(newAdjRate, yearsLeftOnLoan * 12, worstAdjBalance);
            if (expectedYearsLeft >= yearsBetweenAdj) {
                worstAdjTotalPaid += worstAdjPayment * yearsBetweenAdj * 12;
                worstAdjBalance = getBalance(worstAdjBalance, newAdjRate, worstAdjPayment, yearsBetweenAdj * 12);
            } else {
                worstAdjTotalPaid += worstAdjPayment * expectedYearsLeft * 12;
                worstAdjBalance = getBalance(worstAdjBalance, newAdjRate, worstAdjPayment, expectedYearsLeft * 12);
            }

            expectedYearsLeft -= yearsBetweenAdj;
            yearsLeftOnLoan -= yearsBetweenAdj;
            newAdjRate += maxIncrease;
            if (newAdjRate > lifetimeCap) {
                newAdjRate = lifetimeCap;
            }
        }
    }
    var worstAdjInterestPaid = getInterestPaid(worstAdjTotalPaid, worstAdjBalance, principal);

    document.getElementById("fixed_interest_paid").value = Math.round(fixedInterestPaid);
    document.getElementById("adj_best_interest_paid").value = Math.round(bestAdjInterestPaid);
    document.getElementById("adj_worst_interest_paid").value = Math.round(worstAdjInterestPaid);

    document.getElementById("fixed_total_paid").value = Math.round(fixedTotalPaid);
    document.getElementById("adj_best_total_paid").value = Math.round(bestAdjTotalPaid);
    document.getElementById("adj_worst_total_paid").value = Math.round(worstAdjTotalPaid);

    document.getElementById("fixed_balance").value = Math.round(fixedBalance);
    document.getElementById("adj_best_balance").value = Math.round(bestAdjBalance);
    document.getElementById("adj_worst_balance").value = Math.round(worstAdjBalance);
}
