solve Equation a+b+c+d=a*b*c*d [closed] - algorithm

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Hello i am trying to solve this equation for a programming problem that states that you need to do a complete search algorithm to find this results.
However an O(N^4) algorithm takes a lot of time, since the range for each value of A,B,C, and D, is (0,2000] . so we can say A<=B<=C<=D
i want to make my algorithm faster translating it to a O(n^3) solution. For doing that i am taking into account certain things with A,B and C to make the algorithm runs a little faster (prunning). But the main issue is to take out the search of D, i have read some solutions for a similar problem and the way they find D derivating it from A+B+C=A*B*C is really confusing, can somebody explain to me the O(N^3) solution to this problem? thanks a lot!

The equation
A * B * C * D == A + B + C + D
has just one solution
1 1 2 4
So time complexity is O(1).
Since A <= B <= C <= D,
A + B + C + D <= 4 * D
hence
A * B * C * D <= 4 * D
and
A * B * C <= 4
Therefore, it is enough to check just a few combinations:
for(int a = 1; a <= 4; a++)
for(int b = a; b <= 4; b++)
for(int c = b; c <= 4; c++)
{
// a*b*c*d == a+b+c+d
// => d == (a+b+c) / (a*b*c - 1)
if(a * b * c - 1 != 0 && (a + b + c) % (a * b * c - 1) == 0)
{
int d = (a + b + c) / (a * b * c - 1);
if (d >= c)
Console.WriteLine("{0} {1} {2} {3}",
a, b, c, (a + b + c) / (a * b * c - 1));
}
}

Equivalently d = (a+b+c)/(abc-1). Now just walk over all values of a, b, and c, seeing which ones return an integer value for d.
Furthermore a+b+c < abc-1 when a,b,c >= 2. Should shorten your search time quite a bit...

Related

Mathematica Code with Module and If statement

Can I simply ask the logical flow of the below Mathematica code? What are the variables arg and abs doing? I have been searching for answers online and used ToMatlab but still cannot get the answer. Thank you.
Code:
PositiveCubicRoot[p_, q_, r_] :=
Module[{po3 = p/3, a, b, det, abs, arg},
b = ( po3^3 - po3 q/2 + r/2);
a = (-po3^2 + q/3);
det = a^3 + b^2;
If[det >= 0,
det = Power[Sqrt[det] - b, 1/3];
-po3 - a/det + det
,
(* evaluate real part, imaginary parts cancel anyway *)
abs = Sqrt[-a^3];
arg = ArcCos[-b/abs];
abs = Power[abs, 1/3];
abs = (abs - a/abs);
arg = -po3 + abs*Cos[arg/3]
]
]
abs and arg are being reused multiple times in the algorithm.
In a case where det > 0 the steps are
po3 = p/3;
b = (po3^3 - po3 q/2 + r/2);
a = (-po3^2 + q/3);
abs1 = Sqrt[-a^3];
arg1 = ArcCos[-b/abs1];
abs2 = Power[abs1, 1/3];
abs3 = (abs2 - a/abs2);
arg2 = -po3 + abs3*Cos[arg1/3]
abs3 can be identified as A in this answer: Using trig identity to a solve cubic equation
That is the most salient point of this answer.
Evaluating symbolically and numerically may provide some other insights.
Using demo inputs
{p, q, r} = {-2.52111798, -71.424692, -129.51520};
Copyable version of trig identity notes - NB a, b, p & q are used differently in this post
Plot[x^3 - 2.52111798 x^2 - 71.424692 x - 129.51520, {x, 0, 15}]
a = 1;
b = -2.52111798;
c = -71.424692;
d = -129.51520;
p = (3 a c - b^2)/3 a^2;
q = (2 b^3 - 9 a b c + 27 a^2 d)/27 a^3;
A = 2 Sqrt[-p/3]
A == abs3
-(b/3) + A Cos[1/3 ArcCos[
-((b/3)^3 - (b/3) c/2 + d/2)/Sqrt[-(-(b^2/9) + c/3)^3]]]
Edit
There is also a solution shown here
TRIGONOMETRIC SOLUTION TO THE CUBIC EQUATION, by Alvaro H. Salas
Clear[a, b, c]
1/3 (-a + 2 Sqrt[a^2 - 3 b] Cos[1/3 ArcCos[
(-2 a^3 + 9 a b - 27 c)/(2 (a^2 - 3 b)^(3/2))]]) /.
{a -> -2.52111798, b -> -71.424692, c -> -129.51520}
10.499

Rounding of double to nearest member of an arithmetical progression?

I have a formula of a sequence of double numbers k = a + d * n, where a and d are constant double values, n is an integer number, k >= 0, a >= 0. For example:
..., 300, 301.6, 303.2, 304.8, 306.4, ...
I want to round a given number c to a nearest value from this sequence which is lower than c.
Currently I use something like this:
double someFunc(double c) {
static double a = 1;
static double d = 2;
int n = 0;
double a1 = a;
if (c >= a) {
while (a1 < c) {
a1 += d;
}
a1 -= d;
} else {
while (a1 > c) {
a1 -= d;
}
}
return a1;
}
Is it possible to do the same without these awful cycles? I ask because the following situation may appear:
abs(a - c) >> abs(d) (the first number is much more then the second one and so a lot of iterations possible)
My question is similar to the following one. But in my case I also have a a variable which has influence on the final result. It means that a sequence may haven't number 0.
Suppose c is a number in your sequence. Then you have n = (c - a) / d.
Since you want an integer <= c, then take n = floor((c - a) / d).
Then you can round c to: a + d * floor((c - a) / d)
Suppose k = 3 + 5 * n and you round c=21.
And 3 + 5 * floor((21 - 3) / 5) = 3 + 5 * 3 = 18

Some linear constraints seem to be ignored in function NMinimize with Mathematica 8

I'm trying to minimize a non-linear function of four variables with some linear constraints. Mathematica 8 is unable to find a good solution giving complex values of the function at some point in the iteration. This implies that one or some contraints are not being enabled in the process. Is this a bug or limitation of the optimization function ?
Function to minimize is
ff[lxw_, lwz_, c_, d_] := - J1 (lxw + lwz) - 2 J2 c +
T (-Log[2] - 1/2 (1 - lxw) Log[(1 - lxw)/4] -
1/2 (1 + lxw) Log[(1 + lxw)/4] -
1/2 (1 - lwz) Log[(1 - lwz)/4] -
1/2 (1 + lwz) Log[(1 + lwz)/4] + 1/2 (1 - d) Log[(1 - d)/16] +
1/8 (1 + 2 c + d - 2 lwz - 2 lxw) Log[
1/16 (1 + 2 c + d - 2 lwz - 2 lxw)])
where
T = 10;
J1 = 1;
J2 = -0.2;
are constant parameters. Then I try
NMinimize[{ff[lxw, lwz, c, d],
2 c + d - 2 lwz - 2 lxw >= -0.999 &&
-0.999 <= lxw <= 0.999 &&
-0.999 <= lwz <= 0.999 &&
-0.999 <= c <= 0.999 &&
d <= 0.9999}, {lxw, lwz, c, d}]
with the result
NMinimize::nrnum: "The function value 5.87777[VeryThinSpace]-4.87764\ I\n
is not a real number at {c,d,lwz,lxw} = {-0.718817,-1.28595,0.69171,-0.932461}.
I would appreciate if someone can give a hint at what is happening here.
Try this:
Clear[ff];
ff[lxw_, lwz_, c_, d_] /; 2 c + d - 2 lwz - 2 lxw >= -0.999 :=
< your function def >
This will cause the cause the function to be unevaluated in case NMinimize takes an excursion out of bounds. Sorry i cant test this from here.. If that doesn't do try asking on mathematica.stackexchange.com
Aside, why use <=.999 instead of simply < 1 ?
It just might help if you fix that too ( use integer 1, not 1. )
The warning is appearing because at the values given in the warning the last term in ff is complex, due to taking the log of a negative number, i.e.
{c, d, lwz, lxw} = {
-0.7188174745559741`,
-1.2859482844800894`,
0.6917100913968041`,
-0.9324611085040573`};
Log[1/16 (1 + 2 c + d - 2 lwz - 2 lxw)]
-2.5558 + 3.14159 i
1/16 (1 + 2 c + d - 2 lwz - 2 lxw)
-0.0776301
In Mathematica 9 a result is produced in addition to the warning :-
{-4.90045, {c -> 0.94425, d -> -0.315633, lwz -> 0.900231, lxw -> -0.191476}}
I.e.
{c, d, lwz, lxw} = {
0.9442497691706085`,
-0.31563295950647885`,
0.900230825707721`,
-0.1914760216875171`};
ff[lxw, lwz, c, d]
-4.90045

How to find all possible values of four variables when squared sum to N?

A^2+B^2+C^2+D^2 = N Given an integer N, print out all possible combinations of integer values of ABCD which solve the equation.
I am guessing we can do better than brute force.
Naive brute force would be something like:
n = 3200724;
lim = sqrt (n) + 1;
for (a = 0; a <= lim; a++)
for (b = 0; b <= lim; b++)
for (c = 0; c <= lim; c++)
for (d = 0; d <= lim; d++)
if (a * a + b * b + c * c + d * d == n)
printf ("%d %d %d %d\n", a, b, c, d);
Unfortunately, this will result in over a trillion loops, not overly efficient.
You can actually do substantially better than that by discounting huge numbers of impossibilities at each level, with something like:
#include <stdio.h>
int main(int argc, char *argv[]) {
int n = atoi (argv[1]);
int a, b, c, d, na, nb, nc, nd;
int count = 0;
for (a = 0, na = n; a * a <= na; a++) {
for (b = 0, nb = na - a * a; b * b <= nb; b++) {
for (c = 0, nc = nb - b * b; c * c <= nc; c++) {
for (d = 0, nd = nc - c * c; d * d <= nd; d++) {
if (d * d == nd) {
printf ("%d %d %d %d\n", a, b, c, d);
count++;
}
tot++;
}
}
}
}
printf ("Found %d solutions\n", count);
return 0;
}
It's still brute force, but not quite as brutish inasmuch as it understands when to stop each level of looping as early as possible.
On my (relatively) modest box, that takes under a second (a) to get all solutions for numbers up to 50,000. Beyond that, it starts taking more time:
n time taken
---------- ----------
100,000 3.7s
1,000,000 6m, 18.7s
For n = ten million, it had been going about an hour and a half before I killed it.
So, I would say brute force is perfectly acceptable up to a point. Beyond that, more mathematical solutions would be needed.
For even more efficiency, you could only check those solutions where d >= c >= b >= a. That's because you could then build up all the solutions from those combinations into permutations (with potential duplicate removal where the values of two or more of a, b, c, or d are identical).
In addition, the body of the d loop doesn't need to check every value of d, just the last possible one.
Getting the results for 1,000,000 in that case takes under ten seconds rather than over six minutes:
0 0 0 1000
0 0 280 960
0 0 352 936
0 0 600 800
0 24 640 768
: : : :
424 512 512 544
428 460 500 596
432 440 480 624
436 476 532 548
444 468 468 604
448 464 520 560
452 452 476 604
452 484 484 572
500 500 500 500
Found 1302 solutions
real 0m9.517s
user 0m9.505s
sys 0m0.012s
That code follows:
#include <stdio.h>
int main(int argc, char *argv[]) {
int n = atoi (argv[1]);
int a, b, c, d, na, nb, nc, nd;
int count = 0;
for (a = 0, na = n; a * a <= na; a++) {
for (b = a, nb = na - a * a; b * b <= nb; b++) {
for (c = b, nc = nb - b * b; c * c <= nc; c++) {
for (d = c, nd = nc - c * c; d * d < nd; d++);
if (d * d == nd) {
printf ("%4d %4d %4d %4d\n", a, b, c, d);
count++;
}
}
}
}
printf ("Found %d solutions\n", count);
return 0;
}
And, as per a suggestion by DSM, the d loop can disappear altogether (since there's only one possible value of d (discounting negative numbers) and it can be calculated), which brings the one million case down to two seconds for me, and the ten million case to a far more manageable 68 seconds.
That version is as follows:
#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[]) {
int n = atoi (argv[1]);
int a, b, c, d, na, nb, nc, nd;
int count = 0;
for (a = 0, na = n; a * a <= na; a++) {
for (b = a, nb = na - a * a; b * b <= nb; b++) {
for (c = b, nc = nb - b * b; c * c <= nc; c++) {
nd = nc - c * c;
d = sqrt (nd);
if (d * d == nd) {
printf ("%d %d %d %d\n", a, b, c, d);
count++;
}
}
}
}
printf ("Found %d solutions\n", count);
return 0;
}
(a): All timings are done with the inner printf commented out so that I/O doesn't skew the figures.
The Wikipedia page has some interesting background information, but Lagrange's four-square theorem (or, more correctly, Bachet's Theorem - Lagrange only proved it) doesn't really go into detail on how to find said squares.
As I said in my comment, the solution is going to be nontrivial. This paper discusses the solvability of four-square sums. The paper alleges that:
There is no convenient algorithm (beyond the simple one mentioned in
the second paragraph of this paper) for finding additional solutions
that are indicated by the calculation of representations, but perhaps
this will streamline the search by giving an idea of what kinds of
solutions do and do not exist.
There are a few other interesting facts related to this topic. There
exist other theorems that state that every integer can be written as a
sum of four particular multiples of squares. For example, every
integer can be written as N = a^2 + 2b^2 + 4c^2 + 14d^2. There are 54
cases like this that are true for all integers, and Ramanujan provided
the complete list in the year 1917.
For more information, see Modular Forms. This is not easy to understand unless you have some background in number theory. If you could generalize Ramanujan's 54 forms, you may have an easier time with this. With that said, in the first paper I cite, there is a small snippet which discusses an algorithm that may find every solution (even though I find it a bit hard to follow):
For example, it was reported in 1911 that the calculator Gottfried
Ruckle was asked to reduce N = 15663 as a sum of four squares. He
produced a solution of 125^2 + 6^2 + 1^2 + 1^2 in 8 seconds, followed
immediately by 125^2 + 5^2 + 3^2 + 2^2. A more difficult problem
(reflected by a first term that is farther from the original number,
with correspondingly larger later terms) took 56 seconds: 11399 = 105^2
+ 15^2 + 8^2 + 5^2. In general, the strategy is to begin by setting the first term to be the largest square below N and try to represent the
smaller remainder as a sum of three squares. Then the first term is
set to the next largest square below N, and so forth. Over time a
lightning calculator would become familiar with expressing small
numbers as sums of squares, which would speed up the process.
(Emphasis mine.)
The algorithm is described as being recursive, but it could easily be implemented iteratively.
It seems as though all integers can be made by such a combination:
0 = 0^2 + 0^2 + 0^2 + 0^2
1 = 1^2 + 0^2 + 0^2 + 0^2
2 = 1^2 + 1^2 + 0^2 + 0^2
3 = 1^2 + 1^2 + 1^2 + 0^2
4 = 2^2 + 0^2 + 0^2 + 0^2, 1^2 + 1^2 + 1^2 + 1^2 + 1^2
5 = 2^2 + 1^2 + 0^2 + 0^2
6 = 2^2 + 1^2 + 1^2 + 0^2
7 = 2^2 + 1^2 + 1^2 + 1^2
8 = 2^2 + 2^2 + 0^2 + 0^2
9 = 3^2 + 0^2 + 0^2 + 0^2, 2^2 + 2^2 + 1^2 + 0^2
10 = 3^2 + 1^2 + 0^2 + 0^2, 2^2 + 2^2 + 1^2 + 1^2
11 = 3^2 + 1^2 + 1^2 + 0^2
12 = 3^2 + 1^2 + 1^2 + 1^2, 2^2 + 2^2 + 2^2 + 0^2
.
.
.
and so forth
As I did some initial working in my head, I thought that it would be only the perfect squares that had more than 1 possible solution. However after listing them out it seems to me there is no obvious order to them. However, I thought of an algorithm I think is most appropriate for this situation:
The important thing is to use a 4-tuple (a, b, c, d). In any given 4-tuple which is a solution to a^2 + b^2 + c^2 + d^2 = n, we will set ourselves a constraint that a is always the largest of the 4, b is next, and so on and so forth like:
a >= b >= c >= d
Also note that a^2 cannot be less than n/4, otherwise the sum of the squares will have to be less than n.
Then the algorithm is:
1a. Obtain floor(square_root(n)) # this is the maximum value of a - call it max_a
1b. Obtain the first value of a such that a^2 >= n/4 - call it min_a
2. For a in a range (min_a, max_a)
At this point we have selected a particular a, and are now looking at bridging the gap from a^2 to n - i.e. (n - a^2)
3. Repeat steps 1a through 2 to select a value of b. This time instead of finding
floor(square_root(n)) we find floor(square_root(n - a^2))
and so on and so forth. So the entire algorithm would look something like:
1a. Obtain floor(square_root(n)) # this is the maximum value of a - call it max_a
1b. Obtain the first value of a such that a^2 >= n/4 - call it min_a
2. For a in a range (min_a, max_a)
3a. Obtain floor(square_root(n - a^2))
3b. Obtain the first value of b such that b^2 >= (n - a^2)/3
4. For b in a range (min_b, max_b)
5a. Obtain floor(square_root(n - a^2 - b^2))
5b. Obtain the first value of b such that b^2 >= (n - a^2 - b^2)/2
6. For c in a range (min_c, max_c)
7. We now look at (n - a^2 - b^2 - c^2). If its square root is an integer, this is d.
Otherwise, this tuple will not form a solution
At steps 3b and 5b I use (n - a^2)/3, (n - a^2 - b^2)/2. We divide by 3 or 2, respectively, because of the number of values in the tuple not yet 'fixed'.
An example:
doing this on n = 12:
1a. max_a = 3
1b. min_a = 2
2. for a in range(2, 3):
use a = 2
3a. we now look at (12 - 2^2) = 8
max_b = 2
3b. min_b = 2
4. b must be 2
5a. we now look at (12 - 2^2 - 2^2) = 4
max_c = 2
5b. min_c = 2
6. c must be 2
7. (n - a^2 - b^2 - c^2) = 0, hence d = 0
so a possible tuple is (2, 2, 2, 0)
2. use a = 3
3a. we now look at (12 - 3^2) = 3
max_b = 1
3b. min_b = 1
4. b must be 1
5a. we now look at (12 - 3^2 - 1^2) = 2
max_c = 1
5b. min_c = 1
6. c must be 1
7. (n - a^2 - b^2 - c^2) = 1, hence d = 1
so a possible tuple is (3, 1, 1, 1)
These are the only two possible tuples - hey presto!
nebffa has a great answer. one suggestion:
step 3a: max_b = min(a, floor(square_root(n - a^2))) // since b <= a
max_c and max_d can be improved in the same way too.
Here is another try:
1. generate array S: {0, 1, 2^2, 3^2,.... nr^2} where nr = floor(square_root(N)).
now the problem is to find 4 numbers from the array that sum(a, b,c,d) = N;
2. according to neffa's post (step 1a & 1b), a (which is the largest among all 4 numbers) is between [nr/2 .. nr].
We can loop a from nr down to nr/2 and calculate r = N - S[a];
now the question is to find 3 numbers from S the sum(b,c,d) = r = N -S[a];
here is code:
nr = square_root(N);
S = {0, 1, 2^2, 3^2, 4^2,.... nr^2};
for (a = nr; a >= nr/2; a--)
{
r = N - S[a];
// it is now a 3SUM problem
for(b = a; b >= 0; b--)
{
r1 = r - S[b];
if (r1 < 0)
continue;
if (r1 > N/2) // because (a^2 + b^2) >= (c^2 + d^2)
break;
for (c = 0, d = b; c <= d;)
{
sum = S[c] + S[d];
if (sum == r1)
{
print a, b, c, d;
c++; d--;
}
else if (sum < r1)
c++;
else
d--;
}
}
}
runtime is O(sqare_root(N)^3).
Here is the test result running java on my VM (time in milliseconds, result# is total num of valid combination, time 1 with printout, time2 without printout):
N result# time1 time2
----------- -------- -------- -----------
1,000,000 1302 859 281
10,000,000 6262 16109 7938
100,000,000 30912 442469 344359

Generating An Amortization Schedule [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I have been tasked with creating a program that will generate an amortization schedule. I have only done a bit of research so far, but I need to calculate out payments, interest per payment and principal per payment. Can any one point me in the right direction to figure this out? While I will be writing this in RPG, I am sure others could make use of this algorithm in the future.
(Update) Okay, so how do I calculate based on a 365 day year?
When I was buying my first home recently, I wrote a JavaScript app for my personal use.
Here's a modified snippet of my code to determine the monthly payment:
var balance = 200000; // for example
var periods = 360; // 30 years
var monthlyRate = (0.065)/12; // 0.065= APR of 6.5% as decimal
var monthyPayment = (monthlyRate /(1-(Math.pow((1+monthlyRate),-(periods)))))*balance;
for (var i=0; i<360; i++) {
var interestForMonth = balance * monthlyRate;
var principalForMonth = monthlyPayment - interestForMonth;
balance -= monthlyPayment; // probably should be -= principalForMonth see comments below
// output as necessary.
}
Pretty easy to make an amortization table after that. (As others have mentioned, do watch out for rounding errors, I didn't adequately deal with this issue in my implementation.)
There are plenty of sites that offer these formulas. One is below:
http://www.math4finance.com/financial-formulas.php
No fancy algorithms are needed, as the formulas are generally quite simple.
Formula for Amortization Schedule in C#
numerator = i * principle * (Math.Pow(1 + i, n));
denominator = (Math.Pow(1 + i, n) - 1);
/* Calculation of Amortization Payment Amount */
payment = numerator / denominator;
Brian is right. The formulas are quite simple.
If you want your solution to be a good one, you'll pay attention to roundoff error. If you just let roundoff error accumulate and propagate you can off by several cents at the end of the schedule. With careful programming, you'll minimize the error.
Also, if you have MS Excel on your computer, you can download an amortization template from MS office online.
Here is what I ended up creating. I have posted the whole test program. It is written in RPG, but should be easily be figured out for any other languages.
H ActGrp(*caller) BndDir('MODULES') DftActGrp(*no)
//*********************************************************************
// Program . . . . . AMORT
//*********************************************************************
// Printer/Display Files
FAMORTDF CF E WORKSTN sfile(SFL01:rrn01)
//*********************************************************************
// Named Constants
/copy modules/qcopysrc,statuscopy
// Named Indicators
D indicatorPtr S * Inz(%Addr(*IN))
D DS Based(IndicatorPtr)
/copy modules/qcopysrc,scrncopy
// Subfile Fields
D rrn01 S 4P 0 inz(0)
//*********************************************************************
// Misc Fields
D* Monthly Payment
D m S 12P 2
D* Principal
D p S 12P 2
D* Interest
D i S 5P 3
D* Length (in Years)
D l S 3P 0
D* Monthly Interest
D j S 10P10
D* # of Months
D n S 5P 0
D* Current Monthly Int.
D h S 12P 2
D* Current Principal
D c S 12P 2
D* New Balance
D q S 12P 2
//*********************************************************************
// External Program Procedures
// Internal Subprocedures
D Init PR
D Main PR
D SubfileFilled PR N
D ClearScreen PR
D IsValidData PR N
D LoanPayment PR 12P 2
D principal 12P 2
D interest 5P 3
D loanPeriod 3P 0
D paymentsYear 3P 0
// External Subprocedures
///copy modules/qsrvsrc,p.string
//*********************************************************************
// Entry Parms
D AMORT PR extpgm('AMORT')
D AMORT PI
//*********************************************************************
/free
Init();
Main();
*inlr = *on;
/end-free
P*--------------------------------------------------
P* Procedure name: Init
P* Purpose:
P* Returns:
P*--------------------------------------------------
P Init B
D Init PI
/free
pgm = 'AMORT';
sflDsp = *off;
return;
/end-free
P Init E
P*--------------------------------------------------
P* Procedure name: Main
P* Purpose:
P* Returns:
P*--------------------------------------------------
P Main B
D Main PI
/free
dow (not F3) and (not F12);
write OVR01;
exfmt CTL01;
ClearScreen();
if (IsValidData()) and (not F3) and (not F12);
// Fill the header information
dPayment = LoanPayment(dLoanAmt:dIntRate:dLoanPrd:dPayYear);
dNumPaymnt = dLoanPrd * dPayYear;
m = dPayment + dExtraPay;
p = dLoanAmt;
q = p;
// Fill the table
if (SubfileFilled());
sflDsp = *on;
endif;
endif;
enddo;
return;
/end-free
P Main E
P*--------------------------------------------------
P* Procedure name: SubfileFilled
P* Purpose: Fill the subfile
P* Returns:
P*--------------------------------------------------
P SubfileFilled B
D SubfileFilled PI N
D isFilled S N
D x S 4P 0
D intCume S 12P 2
D extraPayCume S 12P 2
D payDate S D
D payment S 12P 2
D extraPayment S 12P 2
/free
isFilled = *on;
sflClear = *on;
write CTL01;
sflClear = *off;
rrn01 = 0;
x = 0;
// Setup the work fields
payment = dPayment;
extraPayment = dExtraPay;
payDate = dStartDate;
// Create records until there is a zero balance
dow (q > 0);
x += 1;
eval(h) h = p * j; // Monthly Interest
// Adjust for final payment
if (p < m);
m = p + h;
payment = p;
extraPayment = h;
endif;
// Calulate Principal
c = m - h;
// Calulate the new balance
q = p - c;
// Accumulate the interest and extra payments
intCume += h;
extraPayCume += extraPayment;
// Determine the next pay date
select;
when dTerms = '1'; //Yearly
payDate += %years(1);
when dTerms = '2'; //Semi-Annual
payDate += %months(6);
when dTerms = '3'; //Quarterly
payDate += %months(3);
when dTerms = '4'; //Monthly
payDate += %months(1);
when dTerms = '5'; //Bi-Weekly
payDate += %days(14);
endsl;
// Fill the subfile
sPayNum = x;
sPayDate = payDate;
sBegBal = p;
sSchedPay = payment;
sExtraPay = extraPayment;
sTotPay = m;
sInterest = h;
sPrincipal = c;
sEndBal = q;
sCumeInt = intCume;
// Move the End balance to the beginning balance
p = q;
rrn01 += 1;
write SFL01;
enddo;
// Return the calculated information to the header
dActPaymnt = x;
dTotInt = intCume;
dTotEPay = extraPayCume;
if (rrn01 < 1);
isFilled = *off;
endif;
return isFilled;
/end-free
P SubfileFilled E
P*--------------------------------------------------
P* Procedure name: ClearScreen
P* Purpose:
P* Returns:
P*--------------------------------------------------
P ClearScreen B
D ClearScreen PI
/free
c = 0;
h = 0;
i = 0;
j = 0;
l = 0;
m = 0;
n = 0;
p = 0;
q = 0;
dPayment = 0;
dNumPaymnt = 0;
dActPaymnt = 0;
dTotEPay = 0;
dTotInt = 0;
return;
/end-free
P ClearScreen E
P*--------------------------------------------------
P* Procedure name: IsValidData
P* Purpose: Validate the data on the screen
P* Returns: True or False
P*--------------------------------------------------
P IsValidData B
D IsValidData PI N
D isValid S N
/free
if (dLoanAmt <> 0) and (dIntRate <> 0) and (dLoanPrd <> 0) and
(dPayYear <> 0) and (dStartDate <> %date('0001-01-01'));
isValid = *on;
else;
isValid = *off;
endif;
return isValid;
/end-free
P IsValidData E
P*--------------------------------------------------
P* Procedure name: LoanPayment
P* Purpose: Calculates the payment
P* Returns:
P*--------------------------------------------------
P LoanPayment B
D LoanPayment PI 12P 2
D principal 12P 2
D interest 5P 3
D loanPeriod 3P 0
D paymentsYear 3P 0
D retMonthlyPayment...
D S 12P 2
/free
eval(h) n = loanPeriod * paymentsYear;
eval(h) j = interest / (paymentsYear * 100);
eval(h) m = principal * (j / (1 - (1 + j) ** -n));
return m;
/end-free
P LoanPayment E
This code has been tested against our loan calculator. If test vs. Excel, it was off about 32 cents on a $200,000 loan. I am sure that is do to rounding issues.
var annuity = P * (i + i / (Math.pow(1+i,n) -1));
//P: principal, I: periodic interest rate, N: number of periods

Resources