Update Spanish translation
[gnumeric.git] / plugins / fn-financial / sc-fin.c
blobd6a2fb3efc1e5294264b519b5eaf33f74a7bbf65
1 /*
2 * This implementation has been taken from the OpenOffice 1.0, see
3 * functions in scaddins/source/analysis/analysishelper.cxx. Since
4 * then there has been made some Gnumeric type system, glib and the C
5 * language specific changes.
7 * The Initial Developer of the Original Code is: Sun Microsystems, Inc.
9 * Sun has made the contents of this file available subject to the
10 * terms of GNU Lesser General Public License Version 2.1 as
11 * specified in scaddins/source/analysis/analysishelper.cxx revision
12 * 1.35 available in the OpenOffice package.
15 * Copyright: 2000 by Sun Microsystems, Inc.
17 * GNU Lesser General Public License Version 2.1
18 * =============================================
19 * Copyright 2000 by Sun Microsystems, Inc.
20 * 901 San Antonio Road, Palo Alto, CA 94303, USA
22 * This library is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU Lesser General Public
24 * License version 2.1, as published by the Free Software Foundation.
26 * This library is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 * Lesser General Public License for more details.
31 * You should have received a copy of the GNU Lesser General Public
32 * License along with this library; if not, write to the Free Software
33 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
34 * 02110-1301 USA.
37 #include <gnumeric-config.h>
38 #include <gnumeric.h>
39 #include <gnm-datetime.h>
40 #include <math.h>
41 #include <value.h>
42 #include "sc-fin.h"
45 static gnm_float
46 GetRmz ( gnm_float fZins, gnm_float fZzr, gnm_float fBw, gnm_float fZw,
47 gint nF )
49 gnm_float fRmz;
51 if ( fZins == 0.0 )
52 fRmz = ( fBw + fZw ) / fZzr;
53 else {
54 gnm_float fTerm = gnm_pow ( 1.0 + fZins, fZzr );
55 if ( nF > 0 )
56 fRmz = ( fZw * fZins / ( fTerm - 1.0 ) + fBw * fZins /
57 ( 1.0 - 1.0 / fTerm ) ) / ( 1.0 + fZins );
58 else
59 fRmz = fZw * fZins / ( fTerm - 1.0 ) + fBw * fZins /
60 ( 1.0 - 1.0 / fTerm );
63 return -fRmz;
66 static gnm_float
67 GetZw ( gnm_float fZins, gnm_float fZzr, gnm_float fRmz, gnm_float fBw,
68 gint nF )
70 gnm_float fZw;
72 if ( fZins == 0.0 )
73 fZw = fBw + fRmz * fZzr;
74 else {
75 gnm_float fTerm = gnm_pow ( 1.0 + fZins, fZzr );
76 if ( nF > 0 )
77 fZw = fBw * fTerm + fRmz * ( 1.0 + fZins ) *
78 ( fTerm - 1.0 ) / fZins;
79 else
80 fZw = fBw * fTerm + fRmz * ( fTerm - 1.0 ) / fZins;
83 return -fZw;
86 static gnm_float
87 Duration (GDate *nSettle, GDate *nMat, gnm_float fCoup, gnm_float fYield,
88 gint nFreq, gint nBase, gnm_float fNumOfCoups)
90 /* gnm_float fYearfrac = yearfrac ( nSettle, nMat, nBase ); */
91 gnm_float fDur = 0.0;
92 gnm_float t, p = 0.0;
94 const gnm_float f100 = 100.0;
96 fCoup *= f100 / (gnm_float) nFreq; /* fCoup is used as cash flow */
97 fYield /= nFreq;
98 fYield += 1.0;
100 for ( t = 1.0 ; t < fNumOfCoups ; t++ )
101 fDur += t * ( fCoup ) / gnm_pow ( fYield, t );
103 fDur += fNumOfCoups * ( fCoup + f100 ) / gnm_pow ( fYield, fNumOfCoups );
105 for ( t = 1.0 ; t < fNumOfCoups ; t++ )
106 p += fCoup / gnm_pow ( fYield, t );
108 p += ( fCoup + f100 ) / gnm_pow ( fYield, fNumOfCoups );
110 fDur /= p;
111 fDur /= (gnm_float) nFreq;
113 return ( fDur );
116 /***************************************************************************/
118 GnmValue *
119 get_amordegrc (gnm_float fCost, GDate *nDate, GDate *nFirstPer,
120 gnm_float fRestVal, gint nPer, gnm_float fRate,
121 gint nBase)
123 gint n;
124 gnm_float fAmorCoeff, fNRate, fRest, fUsePer;
126 #define Round(x,y) (go_rint (x))
128 fUsePer = 1.0 / fRate;
130 if (fUsePer < 3.0)
131 fAmorCoeff = 1.0;
132 else if (fUsePer < 5.0)
133 fAmorCoeff = 1.5;
134 else if (fUsePer <= 6.0)
135 fAmorCoeff = 2.0;
136 else
137 fAmorCoeff = 2.5;
139 fRate *= fAmorCoeff;
140 fNRate = Round ( yearfrac( nDate, nFirstPer, nBase ) * fRate *
141 fCost, 0 );
142 fCost -= fNRate;
143 fRest = fCost - fRestVal;
145 for ( n = 0 ; n < nPer ; n++ ) {
146 fNRate = Round ( fRate * fCost, 0 );
147 fRest -= fNRate;
149 if ( fRest < 0.0 ) {
150 switch ( nPer - n ) {
151 case 0:
152 case 1:
153 return value_new_float (Round ( fCost * 0.5,
154 0 ) );
155 default:
156 return value_new_float (0.0);
160 fCost -= fNRate;
162 return value_new_float (fNRate);
163 #undef Round
166 /***************************************************************************/
168 GnmValue *
169 get_amorlinc (gnm_float fCost, GDate *nDate, GDate *nFirstPer,
170 gnm_float fRestVal, gint nPer, gnm_float fRate, gint nBase)
172 gnm_float fOneRate = fCost * fRate;
173 gnm_float fCostDelta = fCost - fRestVal;
174 gnm_float f0Rate = yearfrac ( nDate, nFirstPer, nBase )
175 * fRate * fCost;
176 gint nNumOfFullPeriods = (fCost - fRestVal - f0Rate) / fOneRate;
177 gnm_float result;
179 if ( nPer == 0 )
180 result = f0Rate;
181 else if( nPer <= nNumOfFullPeriods )
182 result = fOneRate;
183 else if( nPer == nNumOfFullPeriods + 1 )
184 result = fCostDelta - fOneRate * nNumOfFullPeriods - f0Rate;
185 else
186 result = 0.0;
188 return value_new_float ( result );
191 /***************************************************************************/
193 GnmValue * get_yieldmat (GDate *nSettle, GDate *nMat, GDate *nIssue,
194 gnm_float fRate, gnm_float fPrice, gint nBase)
196 gnm_float fIssMat = yearfrac ( nIssue, nMat, nBase );
197 gnm_float fIssSet = yearfrac ( nIssue, nSettle, nBase );
198 gnm_float fSetMat = yearfrac ( nSettle, nMat, nBase );
199 gnm_float y = 1.0 + fIssMat * fRate;
201 y /= fPrice / 100.0 + fIssSet * fRate;
202 y--;
203 y /= fSetMat;
205 return value_new_float ( y );
208 /***************************************************************************/
210 GnmValue *
211 get_duration (GDate *nSettle, GDate *nMat, gnm_float fCoup,
212 gnm_float fYield, gint nFreq, gint nBase,
213 gnm_float fNumOfCoups)
215 return value_new_float ( Duration (nSettle, nMat, fCoup, fYield, nFreq,
216 nBase, fNumOfCoups) );
219 /***************************************************************************/
221 GnmValue *
222 get_mduration (GDate *nSettle, GDate *nMat, gnm_float fCoup,
223 gnm_float fYield, gint nFreq, gint nBase,
224 gnm_float fNumOfCoups)
226 gnm_float fRet = Duration (nSettle, nMat, fCoup, fYield, nFreq, nBase,
227 fNumOfCoups);
229 fRet /= 1.0 + ( fYield / (gnm_float) nFreq );
231 return value_new_float ( fRet );
234 /***************************************************************************/
236 GnmValue *
237 get_cumprinc (gnm_float fRate, gint nNumPeriods, gnm_float fVal,
238 gint nStart, gint nEnd, gint nPayType)
240 gnm_float fRmz, fKapZ;
241 gint i;
243 fRmz = GetRmz ( fRate, nNumPeriods, fVal, 0.0, nPayType );
245 fKapZ = 0.0;
247 if ( nStart == 1 ) {
248 if ( nPayType <= 0 )
249 fKapZ = fRmz + fVal * fRate;
250 else
251 fKapZ = fRmz;
253 nStart++;
256 for ( i = nStart ; i <= nEnd ; i++ ) {
257 if ( nPayType > 0 )
258 fKapZ += fRmz - ( GetZw ( fRate, ( i - 2 ), fRmz,
259 fVal, 1 ) - fRmz ) * fRate;
260 else
261 fKapZ += fRmz - GetZw( fRate, ( i - 1 ), fRmz, fVal,
262 0 ) * fRate;
265 return value_new_float ( fKapZ );
268 /***************************************************************************/
270 GnmValue *
271 get_cumipmt (gnm_float fRate, gint nNumPeriods, gnm_float fVal,
272 gint nStart, gint nEnd, gint nPayType)
274 gnm_float fRmz, fZinsZ;
275 gint i;
277 fRmz = GetRmz ( fRate, nNumPeriods, fVal, 0.0, nPayType );
279 fZinsZ = 0.0;
281 if ( nStart == 1 ) {
282 if ( nPayType <= 0 )
283 fZinsZ = -fVal;
285 nStart++;
288 for ( i = nStart ; i <= nEnd ; i++ ) {
289 if ( nPayType > 0 )
290 fZinsZ += GetZw ( fRate, ( i - 2 ), fRmz, fVal, 1 )
291 - fRmz;
292 else
293 fZinsZ += GetZw ( fRate, ( i - 1 ), fRmz, fVal, 0 );
296 fZinsZ *= fRate;
298 return value_new_float ( fZinsZ );
301 /***************************************************************************/
305 * Original source of the following functions (ScGetGDA, ScInterVDB, and
306 * get_vdb) is the OpenOffice version 1.0, `sc/source/core/tool/interpr2.cxx'.
308 * RCSfile: interpr2.cxx,v
310 * Revision: 1.11
312 * last change: Author: er Date: 2001/03/15 21:31:13
316 static gnm_float
317 ScGetGDA (gnm_float fWert, gnm_float fRest, gnm_float fDauer,
318 gnm_float fPeriode, gnm_float fFaktor)
320 gnm_float fGda, fZins, fAlterWert, fNeuerWert; /* FIXME: translate? */
322 fZins = fFaktor / fDauer;
323 if (fZins >= 1.0) {
324 fZins = 1.0;
325 if (fPeriode == 1.0)
326 fAlterWert = fWert;
327 else
328 fAlterWert = 0.0;
329 } else
330 fAlterWert = fWert * gnm_pow (1.0 - fZins, fPeriode - 1.0);
331 fNeuerWert = fWert * gnm_pow (1.0 - fZins, fPeriode);
333 if (fNeuerWert < fRest)
334 fGda = fAlterWert - fRest;
335 else
336 fGda = fAlterWert - fNeuerWert;
337 if (fGda < 0.0)
338 fGda = 0.0;
339 return fGda;
342 static gnm_float
343 ScInterVDB (gnm_float cost, gnm_float salvage, gnm_float life,
344 gnm_float life1, gnm_float period, gnm_float factor)
346 gnm_float fVdb = 0;
347 gnm_float fIntEnd = gnm_ceil (period);
348 int nLoopEnd = fIntEnd;
350 gnm_float fTerm, fLia;
351 gnm_float fRestwert = cost - salvage;
352 gboolean bNowLia = FALSE;
354 gnm_float fGda;
355 int i;
357 fLia = 0;
358 for ( i = 1; i <= nLoopEnd; i++ ) {
359 if (!bNowLia) {
360 fGda = ScGetGDA (cost, salvage, life, i, factor);
361 fLia = fRestwert / (life1 - (gnm_float) (i - 1));
363 if (fLia > fGda) {
364 fTerm = fLia;
365 bNowLia = TRUE;
366 } else {
367 fTerm = fGda;
368 fRestwert -= fGda;
370 } else
371 fTerm = fLia;
373 if ( i == nLoopEnd)
374 fTerm *= ( period + 1.0 - fIntEnd );
376 fVdb += fTerm;
378 return fVdb;
381 GnmValue *
382 get_vdb (gnm_float cost, gnm_float salvage, gnm_float life,
383 gnm_float start_period, gnm_float end_period, gnm_float factor,
384 gboolean flag)
386 gnm_float fVdb;
387 gnm_float fIntStart = gnm_floor (start_period);
388 gnm_float fIntEnd = gnm_ceil (end_period);
390 fVdb = 0.0;
392 if ( flag ) {
393 int i, nLoopStart, nLoopEnd;
395 if (fIntEnd > G_MAXINT ||
396 fIntEnd - fIntStart > 10000 /* arbitrary */)
397 return value_new_error_VALUE (NULL);
399 nLoopStart = (int) fIntStart;
400 nLoopEnd = (int) fIntEnd;
401 for (i = nLoopStart + 1; i <= nLoopEnd; i++) {
402 gnm_float fTerm;
404 fTerm = ScGetGDA (cost, salvage, life, i, factor);
405 if ( i == nLoopStart+1 )
406 fTerm *= ( MIN( end_period, fIntStart + 1.0 )
407 - start_period );
408 else if ( i == nLoopEnd )
409 fTerm *= ( end_period + 1.0 - fIntEnd );
410 fVdb += fTerm;
412 } else {
413 gnm_float fPart = 0;
414 double fIntEnd = gnm_ceil (end_period);
416 if (start_period > fIntStart) {
417 // First period is partial. Calculate the excess as
418 // the pro-rata value of the first period as-if it
419 // was not partial.
420 double tempcost = cost -
421 ScInterVDB( cost, salvage, life, life, fIntStart, factor);
422 fPart += (start_period - fIntStart) *
423 ScInterVDB( tempcost, salvage, life, life - fIntStart,
424 1, factor);
427 if (end_period < fIntEnd) {
428 // Last period is partial. Calculate the excess as
429 // the pro-rata value of the last period as-if it
430 // was not partial.
431 double em1 = fIntEnd - 1; // Start of last period
432 double tempcost = cost -
433 ScInterVDB (cost, salvage, life, life, em1, factor);
434 fPart += (fIntEnd - end_period) *
435 ScInterVDB (tempcost, salvage, life, life - em1,
436 1, factor);
439 cost -= ScInterVDB (cost, salvage, life, life, fIntStart, factor);
440 fVdb = ScInterVDB (cost, salvage, life, life - fIntStart,
441 fIntEnd - fIntStart, factor);
442 fVdb -= fPart;
444 return value_new_float (fVdb);