I am given 100 dollars and told that I have to do the following operations on it in order:
Add 97
Add 5
Multiply by a compounded interest of 3%
So essentially, it becomes (100 + 97 + 5) * 1.3 = 208.06
Now here lies my problem, I know to get back to 100 from 208.06, I must do:
208.06 / 1.3 – (97 + 5) = 100
The above is a simple example, but as soon as I start mixing compound,non-compound, flat amounts, and the ability to not only add, but subtract the values, it gets really complicated. For example, if I am given 100 dollars again and told that I have to apply the following adjustments in order, I am not sure how to reverse it:
1) Add a compound 3%
2) Add a compound 4%
3) Subtract 90
4) Subtract 3
5) Add a non-compound 3%
6) Add a non-compound 4%
The calculation for the above is:
((100 * (1.03) * (1.04)) – 90 – 3) * (1 + 0.04 + 0.03)
For the above, I have no idea how to approach reversing it with just knowing the post adjusted value and the 6 adjustments that were added.
The above formula can be simplified to, which makes it easier to see how to reverse it:
(100 * 1.03 * (1.04)) - 93 + 7 = 21.21 and to reverse it, we just do:
(100 * 1.03 * (1.04)) - 93 + 7 = 100
My problem is how to loop through it and apply the operations in the correct order. Do I have to store the order of operations and their amounts in a class and then foreach through each calculation?
I have posted this on Math.StackExchange.com already, but they look at it as one big formula and don’t understand that from a programming perspective, I need to loop through each adjustment and take it off the grand total. This is essentially what I am stuck at, but I am unclear how to approach solving it by looping through each adjustment since it is important that I apply the adjustments in the correct order. I am not really looking for a solution, but more of a point in the right direction.
Here is an implementation. Applying it seems to work with the initial sample, but if I do my other one, it does not seem to work unless I am doing something wrong:
void Main()
{
var ops = new List<Operation>
{
new CompoundInterest(3.0m),
new CompoundInterest(4.0m),
new Subtract(90.0m),
new Subtract(3.0m),
new NoncompoundInterest(3.0m),
new NoncompoundInterest(4.0m)
};
var applied = ops.Aggregate(100m, (acc, o) => o.Apply(acc));
var unapplied = ops.Reverse().Aggregate(applied, (acc, o) => o.Unapply(acc));
}
// Define other methods and classes here
abstract class Operation
{
public abstract decimal Apply(decimal value);
public abstract decimal Unapply(decimal value);
}
class CompoundInterest : Operation
{
public decimal percent {get;set;}
public CompoundInterest(decimal percent)
{
this.percent = percent;
}
public override decimal Apply(decimal value)
{
return value * (1m + percent/100m);
}
public override decimal Unapply(decimal value)
{
return value / (1m + percent/100m);
}
}
class NoncompoundInterest : Operation {
public decimal percent {get;set;}
public NoncompoundInterest(decimal percent)
{
this.percent = percent;
}
public override decimal Apply(decimal value)
{
return value * (percent/100m);
}
public override decimal Unapply(decimal value)
{
return value / (percent/100m);
}
}
class Add : Operation {
public decimal amount {get;set;}
public Add(decimal amount)
{
this.amount = amount;
}
public override decimal Apply(decimal value)
{
return value + amount;
}
public override decimal Unapply(decimal value)
{
return value - amount;
}
}
class Subtract : Operation {
public decimal amount {get;set;}
public Subtract(decimal amount)
{
this.amount = amount;
}
public override decimal Apply(decimal value)
{
return value - amount;
}
public override decimal Unapply(decimal value)
{
return value + amount;
}
}
One way to think of this would be as a list of operations, with each operation also providing details how to undo itself. Roughly…
Now you can represent your chain of operations in a list:
Update:
Aggregate()works something like like this:(acc, o) => o.Apply(acc)is a lambda expression representing a function that returnso.Apply(acc)given the current “accumulator” (acc) and the current item in the sequence (o). The “seed” value (100m) is used as the firstacc, then the function’s return value is then passed in asaccfor the nexto, and so on.