2006-03-22 Senganal T <tsenganal@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 //
11 // Copyright (c) 2002-2003 Mainsoft Corporation.
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Runtime.InteropServices;
36 using Microsoft.VisualBasic.CompilerServices;
37
38 namespace Microsoft.VisualBasic
39 {
40         [StandardModule] 
41         sealed public class Financial {
42                 // Declarations
43                 // Constructors
44                 private Financial() {} // prevent public default constructor
45                 // Properties
46                 // Methods
47                 public static double DDB (double Cost, double Salvage, double Life, double Period, 
48                                           [Optional, __DefaultArgumentValue(2)] double Factor)
49                 { 
50                         // LAMESPEC: MSDN says Life and Factor only throws exception if < 0, but Implementation throws exception if <= 0
51                         if (Cost < 0
52                             || Salvage < 0
53                             || Life <= 0
54                             || Period < 0
55                             || Factor <= 0
56                             || Period > Life)
57                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Factor"));
58                         
59                         return (((Cost - Salvage) * Factor) / Life) * Period;
60                 }
61                 
62                 public static double FV (double Rate, double NPer, double Pmt, 
63                                          [Optional, __DefaultArgumentValue(0)] double PV, 
64                                          [Optional, __DefaultArgumentValue(0)] DueDate Due)
65                 { 
66                         Pmt = -Pmt;
67                         PV = -PV;
68                         double currentRate = Math.Pow (Rate + 1, NPer);
69                         double sum = 0;
70                         
71                         if (Rate != 0)
72                                 sum = Pmt * ((currentRate - 1) / Rate);
73                         else
74                                 sum = Pmt * NPer;
75         
76                         if (Due == DueDate.BegOfPeriod)
77                                 sum *= (1 + Rate);
78                         
79                         return PV * currentRate + sum;
80                 }
81                 
82                 public static double IPmt (double Rate, double Per, double NPer, double PV, 
83                                            [Optional, __DefaultArgumentValue(0)] double FV, 
84                                            [Optional, __DefaultArgumentValue(0)] DueDate Due)
85                 { 
86                         double totalFutureVal;
87                         double totalPaymentValue;
88                         double numberOfPeriods;
89                         
90                         if ((Per <= 0) || (Per >= (NPer + 1)))
91                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Per"));
92                         if ((Due == DueDate.BegOfPeriod) && (Per == 1))
93                                 return 0;
94                         
95                         totalPaymentValue = Pmt (Rate, NPer, PV, FV, Due);
96                         if (Due == DueDate.BegOfPeriod)
97                                 PV = (PV + totalPaymentValue);
98                         
99                         numberOfPeriods = Per - ((int)Due) - 1;
100                         totalFutureVal =
101                                 Financial.FV (Rate, numberOfPeriods, totalPaymentValue, PV, DueDate.EndOfPeriod);
102                         
103                         return (totalFutureVal * Rate);
104                 }
105                 
106                 public static double IRR (ref double[] ValueArray, [Optional, __DefaultArgumentValue(0.1)] double Guess) 
107                 { 
108                         double origPV, updatedPV, updateGuess, tmp;
109                         double rateDiff = 0.0;
110                         double pvDiff = 0.0;
111                         int length;
112                         
113                         // MS.NET 2.0 docs say that Guess may not be <= -1
114                         if (Guess <= -1)        
115                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Guess"));        
116                         try {
117                                 length = ValueArray.GetLength(0);
118                         }
119                         catch {
120                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ValueArray"));
121                         }
122                         if (length < 2)        
123                             throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ValueArray"));       
124                         
125                         origPV = NPV (Guess, ref ValueArray);
126                         updateGuess = (origPV > 0) ? Guess + 0.00001 : Guess - 0.00001;   
127                         
128                         if (updateGuess < -1)
129                             throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Rate"));
130                         
131                         rateDiff =  updateGuess - Guess;       
132                         updatedPV = NPV (updateGuess, ref ValueArray);
133                         pvDiff = updatedPV - origPV;
134                         for (int i = 0; i < 20; i++) {
135                                 Guess = (updateGuess > Guess) ? (Guess - 0.00001) : (Guess + 0.00001);
136                                 origPV = NPV (Guess, ref ValueArray);
137                                 rateDiff =  updateGuess - Guess;
138                                 pvDiff = updatedPV - origPV;   
139                                 if (pvDiff == 0)
140                                         throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue"));
141                                 Guess = updateGuess - (rateDiff * updatedPV / pvDiff);
142                                 if (Guess < -1)
143                                 Guess = -1;
144                                 origPV = NPV (Guess, ref ValueArray);
145                                 if ((Math.Abs (origPV) < 0.0000001) && (Math.Abs (rateDiff) < 0.00001))
146                                         return Guess;
147                                 
148                                 tmp = Guess;
149                                 Guess = updateGuess;
150                                 updateGuess = tmp;
151                                 tmp = origPV;
152                                 origPV = updatedPV;
153                                 updatedPV = tmp;
154                         }
155                         double origPVAbs = Math.Abs (origPV);
156                         double updatedPVAbs = Math.Abs (updatedPV);
157                         if ((origPVAbs < 0.0000001) && (updatedPVAbs < 0.0000001))
158                             return (origPVAbs < updatedPVAbs) ? Guess : updateGuess;
159                         else if (origPVAbs < 0.0000001)
160                             return  Guess;
161                         else if (updatedPVAbs < 0.0000001)
162                             return updateGuess;
163                         else                
164                             throw new ArgumentException(Utils.GetResourceString ("Argument_InvalidValue"));
165                 }
166                 
167                 public static double MIRR (ref double[] ValueArray, double FinanceRate, double ReinvestRate)
168                 { 
169                         double [] array = ValueArray;
170                         double loansVal = 0;
171                         double assetsVal = 0;
172                         double currentLoanRate = 1;
173                         double currentAssetsRate = 1;
174                         double totalInterestRate = 0;
175                         int arrayLength = 0;
176                         if (array.Rank != 1)
177                                 throw new ArgumentException (Utils.GetResourceString ("Argument_RankEQOne1", "ValueArray"));
178                         else if (FinanceRate == -1)
179                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "FinanceRate"));
180                         else if (ReinvestRate == -1)
181                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ReinvestRate"));
182                         
183                         arrayLength = array.Length;
184                         if (arrayLength < 2)
185                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ValueArray"));
186                         
187                         for (int i = 0; i < arrayLength; i++) {
188                                 currentLoanRate *= (1 + FinanceRate);
189                                 currentAssetsRate *= (1 + ReinvestRate);
190                                 if (array [i] < 0)
191                                 loansVal += (array [i] / currentLoanRate);
192                                 else if (array [i] > 0)
193                                 assetsVal += (array [i] / currentAssetsRate);
194                         }
195                         
196                         if (loansVal == 0)
197                                 throw new DivideByZeroException (Utils.GetResourceString ("Financial_CalcDivByZero"));
198                         
199                         totalInterestRate =
200                                 ((-assetsVal * Math.Pow (ReinvestRate + 1, arrayLength))
201                                 / (loansVal * (FinanceRate + 1)));
202                         if (totalInterestRate < 0)
203                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue"));
204                         
205                         return (Math.Pow (totalInterestRate, 1 / (double) (arrayLength - 1))) - 1;
206                 }
207                 
208                 public static double NPer (double Rate, double Pmt, double PV, 
209                                            [Optional, __DefaultArgumentValue(0)] double FV, 
210                                            [Optional, __DefaultArgumentValue(0)] DueDate Due)
211                 { 
212                         double totalIncomeFromFlow, sumOfPvAndPayment, currentValueOfPvAndPayment;
213                         if (Rate == 0 && Pmt == 0)
214                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Pmt"));
215                         else if (Rate == 0)
216                                 return (- (PV + FV) / Pmt);
217                         // MainSoft had Rate < -1, but MS.NET 2.0 Doc says Rate should not be <= -1
218                         else if (Rate <= -1)
219                                 throw new ArgumentException(Utils.GetResourceString ("Argument_InvalidValue1", "Rate"));
220                         totalIncomeFromFlow = (Pmt / Rate);
221                         if (Due == DueDate.BegOfPeriod)
222                         totalIncomeFromFlow *= (1 + Rate);
223                         
224                         sumOfPvAndPayment = (-FV + totalIncomeFromFlow);
225                         currentValueOfPvAndPayment = (PV + totalIncomeFromFlow);
226                         if ((sumOfPvAndPayment < 0) && (currentValueOfPvAndPayment < 0)) {
227                                 sumOfPvAndPayment = -sumOfPvAndPayment;
228                                 currentValueOfPvAndPayment = -currentValueOfPvAndPayment;
229                         }
230                         else if ((sumOfPvAndPayment <= 0) || (currentValueOfPvAndPayment < 0))
231                                 throw new ArgumentException (Utils.GetResourceString ("Financial_CannotCalculateNPer"));
232                         
233                         double totalInterestRate = sumOfPvAndPayment / currentValueOfPvAndPayment;
234                         return Math.Log (totalInterestRate) / Math.Log (Rate + 1);
235                 }
236                 
237                 public static double NPV (double Rate, ref double[] ValueArray) 
238                 { 
239                         if (ValueArray == null)
240                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidNullValue1", "ValueArray"));
241                         
242                         double [] arr = ValueArray;
243                         if (arr.Rank != 1)
244                                 throw new ArgumentException (Utils.GetResourceString ("Argument_RankEQOne1", "ValueArray"));
245                         if (Rate == -1)
246                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "Rate"));
247                         int length = arr.Length;
248                         if (length < 0)
249                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "ValueArray"));
250                         
251                         double currentValue = 0;
252                         double currentRate = 1;
253                         double sum = 0;
254                         for (int index = 0; index < length; index++) {
255                                 currentValue = arr [index];
256                                 currentRate *= (1 + Rate);
257                                 sum += (currentValue / currentRate);
258                         }
259                         return sum;
260                 }
261                 
262                 public static double Pmt (double Rate, double NPer, double PV, 
263                                           [Optional, __DefaultArgumentValue(0)] double FV, 
264                                           [Optional, __DefaultArgumentValue(0)] DueDate Due)
265                 { 
266                         PV = -PV;
267                         FV = -FV;
268                         if (NPer == 0)
269                                 throw new ArgumentException (Utils.GetResourceString ("Argument_InvalidValue1", "NPer"));
270                         
271                         double totalFutureVal = 0;
272                         double geometricSum = 0; 
273                         if (Rate == 0) {
274                                 totalFutureVal = FV + PV;
275                                 geometricSum = NPer;
276                         }
277                         else if (Due == DueDate.EndOfPeriod) {
278                                 double totalRate = Math.Pow (Rate + 1, NPer);
279                                 totalFutureVal = FV + PV * totalRate;
280                                 geometricSum = (totalRate - 1) / Rate;
281                         }
282                         else if (Due == DueDate.BegOfPeriod) {
283                                 double totalRate = Math.Pow (Rate + 1, NPer);
284                                 totalFutureVal = FV + PV * totalRate;
285                                 geometricSum = ((1 + Rate) * (totalRate - 1)) / Rate;        
286                         }
287                         return (totalFutureVal) / geometricSum; 
288                 }
289                 
290                 public static double PPmt (double Rate, double Per, double NPer, double PV, 
291                                            [Optional, __DefaultArgumentValue(0)] double FV, 
292                                            [Optional, __DefaultArgumentValue(0)] DueDate Due)
293                 { 
294                         if ((Per <= 0) || (Per >= (NPer + 1)))
295                                 throw new ArgumentException(Utils.GetResourceString ("PPMT_PerGT0AndLTNPer", "Per"));
296                         double interestPayment = IPmt (Rate, Per, NPer, PV, FV, Due);
297                         double totalPayment = Pmt (Rate, NPer, PV, FV, Due);
298                         return (totalPayment - interestPayment);
299                 }
300                 
301                 public static double PV (double Rate, double NPer, double Pmt, 
302                                          [Optional, __DefaultArgumentValue(0)] double FV, 
303                                          [Optional, __DefaultArgumentValue(0)] DueDate Due)
304                 { 
305                         Pmt = -Pmt;
306                         FV = -FV;
307                         double currentRate = 1;
308                         double sum = 0;
309                         for (int index = 1; index <= NPer; index++) {
310                                 currentRate *= (1 + Rate);
311                                 sum += (Pmt / currentRate);
312                         }
313                         
314                         if (Due == DueDate.BegOfPeriod)
315                                 sum *= (1 + Rate);
316                         return sum + FV / currentRate;    
317                 }
318                 
319                 public static double Rate (double NPer, double Pmt, double PV, 
320                                            [Optional, __DefaultArgumentValue(0)] double FV, 
321                                            [Optional, __DefaultArgumentValue(0)] DueDate Due, 
322                                            [Optional, __DefaultArgumentValue(0.1)] double Guess)
323                 { 
324                         double updatedGuess, tmp, origFv, updatedFv;
325                         double rateDiff = 0.0;
326                         double fvDiff = 0.0;
327                         
328                         if (NPer < 0)
329                                 throw new ArgumentException (Utils.GetResourceString ("Rate_NPerMustBeGTZero"));
330                         origFv = -Financial.FV (Guess, NPer, Pmt, PV, Due) + FV;
331                         updatedGuess = (origFv > 0) ? (Guess / 2) : (Guess * 2);
332                         rateDiff = updatedGuess - Guess;
333                         updatedFv = -Financial.FV (updatedGuess, NPer, Pmt, PV, Due) + FV;
334                         fvDiff = updatedFv - origFv;
335                         for (int i = 0; i < 20; i++) {
336                                 Guess += (updatedGuess > Guess) ? -0.00001 : 0.00001;
337                                 origFv = -Financial.FV (Guess, NPer, Pmt, PV, Due) + FV;
338                                 rateDiff = updatedGuess - Guess;
339                                 fvDiff = updatedFv - origFv;
340                                 if (fvDiff == 0)
341                                         throw new ArgumentException (Utils.GetResourceString ("Financial_CalcDivByZero"));
342                                 Guess = updatedGuess - (rateDiff * updatedFv / fvDiff);
343                                 origFv = -Financial.FV (Guess, NPer, Pmt, PV, Due) + FV;
344                                 if (Math.Abs (origFv) < 0.0000001)
345                                         return Guess;
346                                 tmp = Guess;
347                                 Guess = updatedGuess;
348                                 updatedGuess = tmp;
349                                 tmp = origFv;
350                                 origFv = updatedFv;
351                                 updatedFv = tmp;
352                         }
353                         double origFVAbs = Math.Abs (origFv);
354                         double updatedFVAbs = Math.Abs (updatedFv);
355                         if ((origFVAbs < 0.0000001) && (updatedFVAbs < 0.0000001))
356                                 return (origFVAbs < updatedFVAbs) ? Guess : updatedGuess;
357                         else if (origFVAbs < 0.0000001)
358                                 return Guess;
359                         else if (updatedFVAbs < 0.0000001)
360                                 return updatedGuess;
361                         else
362                                 throw new ArgumentException (Utils.GetResourceString ("Financial_CannotCalculateRate")); 
363                 }
364                 
365                 public static double SLN (double Cost, double Salvage, double Life)
366                 { 
367                         if (Life == 0)
368                                 throw new ArgumentException (Utils.GetResourceString ("Financial_LifeNEZero"));
369                         
370                         return (Cost - Salvage) / Life;
371                 }
372                 
373                 public static double SYD (double Cost, double Salvage, double Life, double Period) 
374                 { 
375                         if (Period <= 0)
376                                 throw new ArgumentException (Utils.GetResourceString("Financial_ArgGTZero1", "Period"));
377                         else if (Salvage < 0)
378                                 throw new ArgumentException (Utils.GetResourceString("Financial_ArgGEZero1", "Salvage"));
379                         else if (Period > Life)
380                                 throw new ArgumentException (Utils.GetResourceString("Financial_PeriodLELife"));
381                         
382                         double depreciation =  (Cost - Salvage);
383                         double sumOfDigits = (Life + 1) * Life / 2;
384                         double currentPeriodPart = Life + 1 - Period ; 
385                         
386                         return depreciation * currentPeriodPart / sumOfDigits;
387                 }
388                 // Events
389         }
390 }