package com.paydowncalc.app; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.StreamCorruptedException; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Locale; import java.util.Map; import android.content.Context; import android.util.Log; public class Mortgage { public BigDecimal originalloanamount; public Date originalloanstartdate; public BigDecimal amountstillowed; public Calendar runningdate; public String originalloanstartdateformated; public String startmonth; public String startyear; public BigDecimal monthly; public BigDecimal runningtotalprincipal; public BigDecimal runningtotalinterest; public BigDecimal runningtotalpaid; public BigDecimal interestsaved; public BigDecimal totalinterest; public float yearssaved; public int loanlengthyears; public BigDecimal totalpaid; public float interestrate; public ArrayList<ExtraPayment> extraPayments; public String getDateFormated(Date thedate) { SimpleDateFormat df = new SimpleDateFormat(); df.applyPattern("MMM yyyy"); return df.format(thedate.getTime()); } public String getFinalPayoffDate() { return getDateFormated(runningdate.getTime()); } public String getTimeSaved() { return String.format("%.2g%n", yearssaved); } public String bigDecToMoney(BigDecimal money) { String currencyString = NumberFormat.getCurrencyInstance().format(money.doubleValue()); //Handle the weird exception of formatting whole dollar amounts with no decimal currencyString = currencyString.replaceAll("\\.00", ""); return currencyString; } public String getTotalInterestSaved() { return bigDecToMoney(interestsaved); } public String getTotalInterestPaid() { return bigDecToMoney(runningtotalinterest); } public String getMonthlyPayment() { return bigDecToMoney(monthly); } public String getTotalPaid() { return bigDecToMoney(totalpaid); } public Mortgage() { originalloanamount = new BigDecimal("100000"); amountstillowed = originalloanamount; interestrate = (float) 4.25; loanlengthyears = 30; runningdate = Calendar.getInstance(); runningtotalprincipal = new BigDecimal((int)0); runningtotalinterest = new BigDecimal((int)0); runningtotalpaid = new BigDecimal((int)0); extraPayments = new ArrayList<ExtraPayment>(); } public Mortgage(BigDecimal originalamount, float interest, int lengthyears ) { originalloanamount = originalamount; interestrate = interest; amountstillowed = originalloanamount; loanlengthyears = lengthyears; runningdate = Calendar.getInstance(); runningtotalprincipal = new BigDecimal((int)0); runningtotalinterest = new BigDecimal((int)0); runningtotalpaid = new BigDecimal((int)0); } public Mortgage readFromFile(Context ctx) { FileInputStream fis; ObjectInputStream is; Mortgage mtg = new Mortgage(); try { fis = ctx.openFileInput("mtg"); is = new ObjectInputStream(fis); mtg = (Mortgage) is.readObject(); is.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (StreamCorruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return mtg; } public void saveToFile(Context ctx) { FileOutputStream fos; try { fos = ctx.openFileOutput("mtg", Context.MODE_PRIVATE); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(this); os.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } static public double roundTwoDecimals(double d) { return ((double)Math.round(d*100)/100.0d); } public void amortize() { //runningdate.setTime(originalloanstartdate); runningdate.setTime(new Date()); //TODO use actual loan start if date is in future! runningdate.add(Calendar.MONTH, -1); Calendar nowDate = Calendar.getInstance(); nowDate.setTime(new Date()); nowDate.add(Calendar.MONTH, -2); if(nowDate.getTime().before(originalloanstartdate)) { runningdate.setTime(originalloanstartdate); } int amortizeCount = 0; BigDecimal interestratemonth = new BigDecimal(interestrate); interestratemonth = interestratemonth.divide(new BigDecimal(12), 10, BigDecimal.ROUND_FLOOR); interestratemonth = interestratemonth.divide(new BigDecimal(100), 10, BigDecimal.ROUND_FLOOR); SimpleDateFormat df = new SimpleDateFormat(); df.applyPattern("MMM yyyy"); //local declarations... BigDecimal appliedinterest; BigDecimal appliedprincipal; BigDecimal amount; Calendar originalCalendar = runningdate; BigDecimal originalAmountOwed = amountstillowed; while(amountstillowed.compareTo(BigDecimal.ZERO) > 0 && amortizeCount <= (loanlengthyears*12)) { amortizeCount++; amount = monthly; appliedinterest = amountstillowed.multiply(interestratemonth); appliedprincipal = amount.subtract(appliedinterest); if(amountstillowed.subtract(appliedprincipal).compareTo(BigDecimal.ZERO) < 0) { appliedprincipal = amountstillowed; } //extra payments for(int i = 0; i < extraPayments.size(); i++) { ExtraPayment testExtra = extraPayments.get(i); Log.v("extraPayments", "Checking extra "+ testExtra.type +":" + df.format(testExtra.start) + "/" + testExtra.amount.toString()); if(runningdate.getTime().after(testExtra.getTime())) { Log.v("extraPayments", "Apply extra payment."); appliedprincipal = appliedprincipal.add(testExtra.amount); testExtra.nextDate(); extraPayments.set(i, testExtra); /* if(extraPayments.get(i).type.equalsIgnoreCase("One-time")) { extraPayments.get(i).amount = BigDecimal.ZERO; } */ } } runningdate.add(Calendar.MONTH, 1); amountstillowed = amountstillowed.subtract(appliedprincipal); BigDecimal mtgprincipalafter = runningtotalprincipal.add(appliedprincipal); runningtotalpaid = runningtotalpaid.add(appliedinterest); runningtotalpaid = runningtotalpaid.add(appliedprincipal); runningtotalprincipal = mtgprincipalafter; runningtotalinterest = runningtotalinterest.add(appliedinterest); } totalpaid = runningtotalinterest.add(originalloanamount); Calendar finalCalendar = runningdate; //adjustments if(extraPayments.size() <= 0) { totalinterest = runningtotalinterest; interestsaved = BigDecimal.ZERO; yearssaved = 0; } else { interestsaved= BigDecimal.ZERO; yearssaved = 0; //do it again, with no extra payments. Calendar runningDateNoExtra = Calendar.getInstance(); //runningDateNoExtra.setTime(originalloanstartdate); runningDateNoExtra.setTime(new Date()); //TODO use actual loan start if date is in future! runningDateNoExtra.add(Calendar.MONTH, -1); if(nowDate.getTime().before(originalloanstartdate)) { runningDateNoExtra.setTime(originalloanstartdate); } amountstillowed = originalAmountOwed; BigDecimal totalInterestNoExtra = BigDecimal.ZERO; runningtotalprincipal = BigDecimal.ZERO; amortizeCount = 0; Log.v("no savings", "Begin no savings branch on " + getDateFormated(runningdate.getTime())); while(amountstillowed.compareTo(BigDecimal.ZERO) > 0 && amortizeCount <= (loanlengthyears*12)) { amortizeCount++; amount = monthly; appliedinterest = amountstillowed.multiply(interestratemonth); appliedprincipal = amount.subtract(appliedinterest); if(amountstillowed.subtract(appliedprincipal).compareTo(BigDecimal.ZERO) < 0) { Log.v("applypay", "Branched:" + appliedprincipal.doubleValue() + ":" + runningtotalprincipal.doubleValue() + " / " + amountstillowed.doubleValue() + " / " + appliedprincipal.add(runningtotalprincipal).doubleValue()); appliedprincipal = amountstillowed; } runningDateNoExtra.add(Calendar.MONTH, 1); amountstillowed = amountstillowed.subtract(appliedprincipal); BigDecimal mtgprincipalafter = runningtotalprincipal.add(appliedprincipal); runningtotalprincipal = mtgprincipalafter; totalInterestNoExtra = totalInterestNoExtra.add(appliedinterest); } //interest saved... interestsaved = totalInterestNoExtra.subtract(runningtotalinterest); Log.v("date with extra", getDateFormated(runningdate.getTime())); Log.v("date without extra", getDateFormated(runningDateNoExtra.getTime())); //time saved long milliEnd = runningdate.getTimeInMillis(); long milliStart = runningDateNoExtra.getTimeInMillis(); long dayDiff = ((milliStart - milliEnd) / 1000) / 86400; float yearDiff = ((float)dayDiff) / 365; yearssaved = (float)Math.round(yearDiff * 100) / 100; } } public void calculateMtg() { //function calculateMtg($originalloanamount, $interestrate, $loanlengthyears = 30) /*calculate monthly payment without extra payments... P = principal, the initial amount of the loan I = the annual interest rate (from 1 to 100 percent) L = length, the length (in years) of the loan, or at least the length over which the loan is amortized. The following assumes a typical conventional loan where the interest is compounded monthly. First I will define two more variables to make the calculations easier: J = monthly interest in decimal form = I / (12 x 100) N = number of months over which loan is amortized = L x 12 J M = P x ------------------------ 1 - ( 1 + J ) ^ -N */ int nummonths = loanlengthyears*12; float monthlyinterest = (interestrate / (12 * 100)); double denominator = 1 - Math.pow( 1 + monthlyinterest, 0-nummonths); double monthlypaymentraw = originalloanamount.doubleValue() * (monthlyinterest / denominator); double totalinterestraw = (monthlypaymentraw * nummonths) - originalloanamount.doubleValue(); //double totalinterestmonthlyraw = totalinterestraw / nummonths; double totalamountraw = totalinterestraw + originalloanamount.doubleValue(); totalpaid = new BigDecimal((totalamountraw)); totalpaid = totalpaid.setScale(2, BigDecimal.ROUND_HALF_EVEN); totalinterest = new BigDecimal((totalinterestraw)); totalinterest = totalinterest.setScale(2, BigDecimal.ROUND_HALF_EVEN); monthly = new BigDecimal((monthlypaymentraw)); monthly = monthly.setScale(2, BigDecimal.ROUND_HALF_EVEN); originalloanstartdateformated = startmonth + " " + startyear ; Log.v("extra393", "Attempting "+ originalloanstartdateformated); SimpleDateFormat sdf = new SimpleDateFormat("MMMM yyyy", Locale.US); try { originalloanstartdate = sdf.parse(originalloanstartdateformated); Log.v("extra", "Extracted "+ sdf.format(originalloanstartdate)); } catch(Exception ex) { Log.v("extra", "Parse error!"); originalloanstartdate = new Date(); } Log.v("MTG","monthly: " + monthly); Log.v("MTG","totalinterest: " + totalinterest); Log.v("MTG","totalpaid: " + totalpaid); } //There is not a god damn thing wrong with this statement; //double totalamountraw = totalinterestraw + originalloanamount.doubleValue(); //originalloanamount = originalloanamount; /* originalloanamount = Math.round($originalloanamount, 2); interestrate =Math.round($interestrate, 4); loanlengthyears = loanlengthyears; monthly = Math.round(monthlypaymentraw, 2); totalinterest = Math.round(totalinterestraw, 2); totalpaid = Math.round(totalamountraw, 2); runningtotalprincipal = 0; runningtotalinterest = 0; */ }