2004-06-24 Anirban Bhattacharjee <banirban@novell.com>
[mono.git] / mcs / class / Microsoft.VisualBasic / Microsoft.VisualBasic / Financial.cs
1 //
2 // Financial.cs
3 //
4 // Author:
5 //   Chris J Breisch (cjbreisch@altavista.net) 
6 //
7 // (C) 2002 Chris J Breisch
8 //
9
10 using System;
11 using System.ComponentModel;\r
12 using System.Runtime.InteropServices;
13 using Microsoft.VisualBasic.CompilerServices;
14 \r
15 namespace Microsoft.VisualBasic\r
16 {
17         [StandardModule] 
18         sealed public class Financial {
19                 // Declarations
20                 // Constructors
21                 private Financial() {} // prevent public default constructor
22                 // Properties
23                 // Methods
24                 public static double DDB (double Cost, double Salvage, double Life, double Period, 
25                                           [Optional, DefaultValue (2)] double Factor)
26                 { 
27                         // LAMESPEC: MSDN says Life and Factor only throws exception if < 0, but Implementation throws exception if <= 0
28                         if (Cost < 0
29                             || Salvage < 0
30                             || Life <= 0
31                             || Period < 0
32                             || Factor <= 0
33                             || Period > Life)
34                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Factor"));
35                         
36                         return (((Cost - Salvage) * Factor) / Life) * Period;
37                 }
38                 
39                 public static double FV (double Rate, double NPer, double Pmt, 
40                                          [Optional, DefaultValue (0)] double PV, 
41                                          [Optional, DefaultValue (0)] DueDate Due)
42                 { 
43                         Pmt = -Pmt;
44                         PV = -PV;
45                         double currentRate = Math.Pow (Rate + 1, NPer);
46                         double sum = 0;
47                         
48                         if (Rate != 0)
49                                 sum = Pmt * ((currentRate - 1) / Rate);
50                         else
51                                 sum = Pmt * NPer;
52         
53                         if (Due == DueDate.BegOfPeriod)
54                                 sum *= (1 + Rate);
55                         
56                         return PV * currentRate + sum;
57                 }
58                 
59                 public static double IPmt (double Rate, double Per, double NPer, double PV, 
60                                            [Optional, DefaultValue (0)] double FV, 
61                                            [Optional, DefaultValue (0)] DueDate Due)
62                 { 
63                         double totalFutureVal;
64                         double totalPaymentValue;
65                         double numberOfPeriods;
66                         
67                         if ((Per <= 0) || (Per >= (NPer + 1)))
68                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Per"));
69                         if ((Due == DueDate.BegOfPeriod) && (Per == 1))
70                                 return 0;
71                         
72                         totalPaymentValue = Pmt (Rate, NPer, PV, FV, Due);
73                         if (Due == DueDate.BegOfPeriod)
74                                 PV = (PV + totalPaymentValue);
75                         
76                         numberOfPeriods = Per - ((int)Due) - 1;
77                         totalFutureVal =
78                                 Financial.FV (Rate, numberOfPeriods, totalPaymentValue, PV, DueDate.EndOfPeriod);
79                         
80                         return (totalFutureVal * Rate);
81                 }
82                 
83                 public static double IRR (ref double[] ValueArray, [Optional, DefaultValue (0.1)] double Guess) 
84                 { 
85                         double origPV, updatedPV, updateGuess, tmp;
86                         double rateDiff = 0.0;
87                         double pvDiff = 0.0;
88                         int length;
89                         
90                         // MS.NET 2.0 docs say that Guess may not be <= -1
91                         if (Guess <= -1)        
92                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Guess"));        
93                         try {
94                                 length = ValueArray.GetLength(0);
95                         }
96                         catch {
97                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ValueArray"));
98                         }
99                         if (length < 2)        
100                             throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ValueArray"));       
101                         
102                         origPV = NPV (Guess, ref ValueArray);
103                         updateGuess = (origPV > 0) ? Guess + 0.00001 : Guess - 0.00001;   
104                         
105                         if (updateGuess < -1)
106                             throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Rate"));
107                         
108                         rateDiff =  updateGuess - Guess;       
109                         updatedPV = NPV (updateGuess, ref ValueArray);
110                         pvDiff = updatedPV - origPV;
111                         for (int i = 0; i < 20; i++) {
112                                 Guess = (updateGuess > Guess) ? (Guess - 0.00001) : (Guess + 0.00001);
113                                 origPV = NPV (Guess, ref ValueArray);
114                                 rateDiff =  updateGuess - Guess;
115                                 pvDiff = updatedPV - origPV;   
116                                 if (pvDiff == 0)
117                                         throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue"));
118                                 Guess = updateGuess - (rateDiff * updatedPV / pvDiff);
119                                 if (Guess < -1)
120                                 Guess = -1;
121                                 origPV = NPV (Guess, ref ValueArray);
122                                 if ((Math.Abs (origPV) < 0.0000001) && (Math.Abs (rateDiff) < 0.00001))
123                                         return Guess;
124                                 
125                                 tmp = Guess;
126                                 Guess = updateGuess;
127                                 updateGuess = tmp;
128                                 tmp = origPV;
129                                 origPV = updatedPV;
130                                 updatedPV = tmp;
131                         }
132                         double origPVAbs = Math.Abs (origPV);
133                         double updatedPVAbs = Math.Abs (updatedPV);
134                         if ((origPVAbs < 0.0000001) && (updatedPVAbs < 0.0000001))
135                             return (origPVAbs < updatedPVAbs) ? Guess : updateGuess;
136                         else if (origPVAbs < 0.0000001)
137                             return  Guess;
138                         else if (updatedPVAbs < 0.0000001)
139                             return updateGuess;
140                         else                
141                             throw new ArgumentException(Utils.GetResourceString ("Argument_InvalidValue"));
142                 }
143                 
144                 public static double MIRR (ref double[] ValueArray, double FinanceRate, double ReinvestRate)
145                 { 
146                         double [] array = ValueArray;
147                         double loansVal = 0;
148                         double assetsVal = 0;
149                         double currentLoanRate = 1;
150                         double currentAssetsRate = 1;
151                         double totalInterestRate = 0;
152                         int arrayLength = 0;
153                         if (array.Rank != 1)
154                                 throw new ArgumentException (Utils.GetResourceString ("Argument_RankEQOne1", "ValueArray"));
155                         else if (FinanceRate == -1)
156                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "FinanceRate"));
157                         else if (ReinvestRate == -1)
158                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ReinvestRate"));
159                         
160                         arrayLength = array.Length;
161                         if (arrayLength < 2)
162                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ValueArray"));
163                         
164                         for (int i = 0; i < arrayLength; i++) {
165                                 currentLoanRate *= (1 + FinanceRate);
166                                 currentAssetsRate *= (1 + ReinvestRate);
167                                 if (array [i] < 0)
168                                 loansVal += (array [i] / currentLoanRate);
169                                 else if (array [i] > 0)
170                                 assetsVal += (array [i] / currentAssetsRate);
171                         }
172                         
173                         if (loansVal == 0)
174                                 throw new DivideByZeroException (Utils.GetResourceString ("Financial_CalcDivByZero"));
175                         
176                         totalInterestRate =
177                                 ((-assetsVal * Math.Pow (ReinvestRate + 1, arrayLength))
178                                 / (loansVal * (FinanceRate + 1)));
179                         if (totalInterestRate < 0)
180                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue"));
181                         
182                         return (Math.Pow (totalInterestRate, 1 / (double) (arrayLength - 1))) - 1;
183                 }
184                 
185                 public static double NPer (double Rate, double Pmt, double PV, 
186                                            [Optional, DefaultValue (0)] double FV, 
187                                            [Optional, DefaultValue (0)] DueDate Due)
188                 { 
189                         double totalIncomeFromFlow, sumOfPvAndPayment, currentValueOfPvAndPayment;
190                         if (Rate == 0 && Pmt == 0)
191                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Pmt"));
192                         else if (Rate == 0)
193                                 return (- (PV + FV) / Pmt);
194                         // MainSoft had Rate < -1, but MS.NET 2.0 Doc says Rate should not be <= -1
195                         else if (Rate <= -1)
196                                 throw new ArgumentException(Utils.GetResourceString ("Argument_InvalidValue1", "Rate"));
197                         totalIncomeFromFlow = (Pmt / Rate);
198                         if (Due == DueDate.BegOfPeriod)
199                         totalIncomeFromFlow *= (1 + Rate);
200                         
201                         sumOfPvAndPayment = (-FV + totalIncomeFromFlow);
202                         currentValueOfPvAndPayment = (PV + totalIncomeFromFlow);
203                         if ((sumOfPvAndPayment < 0) && (currentValueOfPvAndPayment < 0)) {
204                                 sumOfPvAndPayment = -sumOfPvAndPayment;
205                                 currentValueOfPvAndPayment = -currentValueOfPvAndPayment;
206                         }
207                         else if ((sumOfPvAndPayment <= 0) || (currentValueOfPvAndPayment < 0))
208                                 throw new ArgumentException (Utils.GetResourceString ("Financial_CannotCalculateNPer"));
209                         
210                         double totalInterestRate = sumOfPvAndPayment / currentValueOfPvAndPayment;
211                         return Math.Log (totalInterestRate) / Math.Log (Rate + 1);
212                 }
213                 
214                 public static double NPV (double Rate, ref double[] ValueArray) 
215                 { 
216                         if (ValueArray == null)
217                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidNullValue1", "ValueArray"));
218                         
219                         double [] arr = ValueArray;
220                         if (arr.Rank != 1)
221                                 throw new ArgumentException (Utils.GetResourceString ("Argument_RankEQOne1", "ValueArray"));
222                         if (Rate == -1)
223                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Rate"));
224                         int length = arr.Length;
225                         if (length < 0)
226                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ValueArray"));
227                         
228                         double currentValue = 0;
229                         double currentRate = 1;
230                         double sum = 0;
231                         for (int index = 0; index < length; index++) {
232                                 currentValue = arr [index];
233                                 currentRate *= (1 + Rate);
234                                 sum += (currentValue / currentRate);
235                         }
236                         return sum;
237                 }
238                 
239                 public static double Pmt (double Rate, double NPer, double PV, 
240                                           [Optional, DefaultValue (0)] double FV, 
241                                           [Optional, DefaultValue (0)] DueDate Due)
242                 { 
243                         PV = -PV;
244                         FV = -FV;
245                         if (NPer == 0)
246                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "NPer"));
247                         
248                         double totalFutureVal = 0;
249                         double geometricSum = 0; 
250                         if (Rate == 0) {
251                                 totalFutureVal = FV + PV;
252                                 geometricSum = NPer;
253                         }
254                         else if (Due == DueDate.EndOfPeriod) {
255                                 double totalRate = Math.Pow (Rate + 1, NPer);
256                                 totalFutureVal = FV + PV * totalRate;
257                                 geometricSum = (totalRate - 1) / Rate;
258                         }
259                         else if (Due == DueDate.BegOfPeriod) {
260                                 double totalRate = Math.Pow (Rate + 1, NPer);
261                                 totalFutureVal = FV + PV * totalRate;
262                                 geometricSum = ((1 + Rate) * (totalRate - 1)) / Rate;        
263                         }
264                         return (totalFutureVal) / geometricSum; 
265                 }
266                 
267                 public static double PPmt (double Rate, double Per, double NPer, double PV, 
268                                            [Optional, DefaultValue (0)] double FV, 
269                                            [Optional, DefaultValue (0)] DueDate Due)
270                 { 
271                         if ((Per <= 0) || (Per >= (NPer + 1)))
272                                 throw new ArgumentException(Utils.GetResourceString ("PPMT_PerGT0AndLTNPer", "Per"));
273                         double interestPayment = IPmt (Rate, Per, NPer, PV, FV, Due);
274                         double totalPayment = Pmt (Rate, NPer, PV, FV, Due);
275                         return (totalPayment - interestPayment);
276                 }
277                 
278                 public static double PV (double Rate, double NPer, double Pmt, 
279                                          [Optional, DefaultValue (0)] double FV, 
280                                          [Optional, DefaultValue (0)] DueDate Due)
281                 { 
282                         Pmt = -Pmt;
283                         FV = -FV;
284                         double currentRate = 1;
285                         double sum = 0;
286                         for (int index = 1; index <= NPer; index++) {
287                                 currentRate *= (1 + Rate);
288                                 sum += (Pmt / currentRate);
289                         }
290                         
291                         if (Due == DueDate.BegOfPeriod)
292                                 sum *= (1 + Rate);
293                         return sum + FV / currentRate;    
294                 }
295                 
296                 public static double Rate (double NPer, double Pmt, double PV, 
297                                            [Optional, DefaultValue (0)] double FV, 
298                                            [Optional, DefaultValue (0)] DueDate Due, 
299                                            [Optional, DefaultValue (0.1)] double Guess)
300                 { 
301                         double updatedGuess, tmp, origFv, updatedFv;
302                         double rateDiff = 0.0;
303                         double fvDiff = 0.0;
304                         
305                         if (NPer < 0)
306                                 throw new ArgumentException (Utils.GetResourceString ("Rate_NPerMustBeGTZero"));
307                         origFv = -Financial.FV (Guess, NPer, Pmt, PV, Due) + FV;
308                         updatedGuess = (origFv > 0) ? (Guess / 2) : (Guess * 2);
309                         rateDiff = updatedGuess - Guess;
310                         updatedFv = -Financial.FV (updatedGuess, NPer, Pmt, PV, Due) + FV;
311                         fvDiff = updatedFv - origFv;
312                         for (int i = 0; i < 20; i++) {
313                                 Guess += (updatedGuess > Guess) ? -0.00001 : 0.00001;
314                                 origFv = -Financial.FV (Guess, NPer, Pmt, PV, Due) + FV;
315                                 rateDiff = updatedGuess - Guess;
316                                 fvDiff = updatedFv - origFv;
317                                 if (fvDiff == 0)
318                                         throw new ArgumentException (Utils.GetResourceString ("Financial_CalcDivByZero"));
319                                 Guess = updatedGuess - (rateDiff * updatedFv / fvDiff);
320                                 origFv = -Financial.FV (Guess, NPer, Pmt, PV, Due) + FV;
321                                 if (Math.Abs (origFv) < 0.0000001)
322                                         return Guess;
323                                 tmp = Guess;
324                                 Guess = updatedGuess;
325                                 updatedGuess = tmp;
326                                 tmp = origFv;
327                                 origFv = updatedFv;
328                                 updatedFv = tmp;
329                         }
330                         double origFVAbs = Math.Abs (origFv);
331                         double updatedFVAbs = Math.Abs (updatedFv);
332                         if ((origFVAbs < 0.0000001) && (updatedFVAbs < 0.0000001))
333                                 return (origFVAbs < updatedFVAbs) ? Guess : updatedGuess;
334                         else if (origFVAbs < 0.0000001)
335                                 return Guess;
336                         else if (updatedFVAbs < 0.0000001)
337                                 return updatedGuess;
338                         else
339                                 throw new ArgumentException (Utils.GetResourceString ("Financial_CannotCalculateRate")); 
340                 }
341                 
342                 public static double SLN (double Cost, double Salvage, double Life)
343                 { 
344                         if (Life == 0)
345                                 throw new ArgumentException (Utils.GetResourceString ("Financial_LifeNEZero"));
346                         
347                         return (Cost - Salvage) / Life;
348                 }
349                 
350                 public static double SYD (double Cost, double Salvage, double Life, double Period) 
351                 { 
352                         if (Period <= 0)
353                                 throw new ArgumentException (Utils.GetResourceString("Financial_ArgGTZero1", "Period"));
354                         else if (Salvage < 0)
355                                 throw new ArgumentException (Utils.GetResourceString("Financial_ArgGEZero1", "Salvage"));
356                         else if (Period > Life)
357                                 throw new ArgumentException (Utils.GetResourceString("Financial_PeriodLELife"));
358                         
359                         double depreciation =  (Cost - Salvage);
360                         double sumOfDigits = (Life + 1) * Life / 2;
361                         double currentPeriodPart = Life + 1 - Period ; 
362                         
363                         return depreciation * currentPeriodPart / sumOfDigits;
364                 }
365                 // Events
366         }
367 }