/*
* Copyright (C) 2007 Snorre Gylterud, Stein Magnus Jodal, Johannes Knutsen,
* Erik Bagge Ottesen, Ralf Bjarne Taraldset, and Iterate AS
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*/
package no.ntnu.mmfplanner.model;
/**
* Holds the project ROI values. Also has a method for calculating the values.
*/
public class ProjectRoi {
public Mmf mmfs[];
public int values[][];
public int cash[];
public int investment[];
public int presentValue[];
public int rollingNpv[];
public double interestRate;
public double roi;
public int selfFundingPeriod;
public int breakevenPeriod;
public double breakevenRegression;
/**
* This method will calculate the most important variables in valuing the
* project. All values for each MMF and each period are given, as well as
* relevant summations and NPVs.
*
* @return A complete ProjectRoi object with the projected return on
* investment for this project.
*/
public static ProjectRoi getRoiTable(Project project, double interestRate, boolean waterfall) {
ProjectRoi roi = new ProjectRoi();
int mmfCount = project.size();
int periods = project.getPeriods();
// roi.interestRate
roi.interestRate = interestRate;
// roi.mmfs
roi.mmfs = new Mmf[mmfCount];
int nextRoiMmf = 0;
for (int i = 1; i <= periods; i++) {
for (Mmf mmf : project.getMmfs()) {
if (i == mmf.getPeriod()) {
roi.mmfs[nextRoiMmf] = mmf;
nextRoiMmf++;
}
}
}
for (Mmf mmf : project.getMmfs()) {
if (mmf.getPeriod() > periods) {
roi.mmfs[nextRoiMmf] = mmf;
nextRoiMmf++;
}
}
// roi.values
roi.values = new int[mmfCount][periods + 1];
for (int i = 0; i < mmfCount; i++) {
Mmf mmf = roi.mmfs[i];
int sum = 0;
int skipPeriods = mmf.getPeriod() - 1;
for (int p = skipPeriods; p < periods; p++) {
roi.values[i][p] = mmf.getRevenue(p - skipPeriods + 1);
sum += roi.values[i][p];
}
roi.values[i][periods] = sum;
}
if (waterfall) {
// for waterfall we move all positive revenue beyond the last
// negative revenue
// first we find the last period with negative revenue for any mmf
int negativeMax = 0;
int negative[] = new int[mmfCount];
for (int i = 0; i < mmfCount; i++) {
Mmf mmf = roi.mmfs[i];
if (mmf.getPeriod() > periods) {
negative[i] = periods;
continue;
}
for (int p = 1; p <= periods; p++) {
if (mmf.getRevenue(p) >= 0) {
negative[i] = p + mmf.getPeriod() - 2;
negativeMax = Math.max(negativeMax, negative[i]);
break;
}
}
}
negativeMax = Math.min(negativeMax, periods);
// then move all the positive revenue (and update the net value)
for (int i = 0; i < mmfCount; i++) {
if (negative[i] >= negativeMax) {
continue;
}
// we first swap all the revenues to the left
for (int p = periods - 1; p >= negativeMax; p--) {
int pd = p - (negativeMax - negative[i]);
int tmp = roi.values[i][p];
roi.values[i][p] = roi.values[i][pd];
roi.values[i][pd] = tmp;
}
// and then remove all the ones that are left
for (int p = negative[i]; p < negativeMax; p++) {
roi.values[i][periods] -= roi.values[i][p];
roi.values[i][p] = 0;
}
}
}
// roi.cash, investement, presentValue, rollingNpv
roi.cash = new int[periods + 1];
roi.investment = new int[periods + 1];
roi.presentValue = new int[periods + 1];
roi.rollingNpv = new int[periods];
double sumPV = 0.0;
double minPV = -0.001;
for (int p = 0; p < periods; p++) {
// roi.cash
int sum = 0;
for (int[] values : roi.values) {
sum += values[p];
}
roi.cash[p] = sum;
roi.cash[periods] += sum;
// roi.investment
roi.investment[p] = (roi.cash[p] < 0 ? roi.cash[p] : 0);
roi.investment[periods] += roi.investment[p];
// roi.presentValue
double pv = roi.cash[p] / Math.pow(1 + project.getInterestRate(), p + 1);
sumPV += pv;
roi.presentValue[p] = (int) Math.round(pv);
// roi.rollingNpv
roi.rollingNpv[p] = (int) Math.round(sumPV);
// roi.selfFundingPeriod
if (sumPV <= minPV) {
minPV = roi.rollingNpv[p];
if (p + 1 < periods) {
roi.selfFundingPeriod = p + 2;
} else {
roi.selfFundingPeriod = 0;
}
}
// roi.breakevenPeriod
if ((0 == roi.breakevenPeriod) && (p > 0)
&& (0 <= roi.rollingNpv[p]) && (0 > roi.rollingNpv[p - 1])) {
roi.breakevenPeriod = p + 1;
// roi.breakevenRegression
if (p > 0) {
roi.breakevenRegression = p
+ 1
- (roi.rollingNpv[p - 1] / (double) (roi.rollingNpv[p] - roi.rollingNpv[p - 1]));
}
}
}
roi.presentValue[periods] = (int) Math.round(sumPV);
// roi.roi
roi.roi = ((double) -roi.cash[periods]) / roi.investment[periods];
return roi;
}
}