Skip to content

Commit

Permalink
generalise solver
Browse files Browse the repository at this point in the history
  • Loading branch information
simontreanor committed Oct 16, 2024
1 parent ff38da2 commit f1d2743
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/Apr.fs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ module Apr =
let pp = calc a'k' unitPeriodRate
let difference = Decimal.Round(pp - aa, 8)
difference
Array.solve generator 100 roughApr AroundZero ToleranceSteps.zero
Array.solve generator 0m 100 roughApr AroundZero ToleranceSteps.zero

/// APR as in https://www.consumerfinance.gov/rules-policy/regulations/1026/j/
module UsActuarial =
Expand Down Expand Up @@ -223,7 +223,7 @@ module Apr =
let pp = calc ft unitPeriodRate
let difference = Decimal.Round(pp - aa, 10)
difference
Array.solve generator 100 roughUnitPeriodRate AroundZero ToleranceSteps.zero
Array.solve generator 0m 100 roughUnitPeriodRate AroundZero ToleranceSteps.zero
match unitPeriodRate with
| Solution.Found(upr, iteration, tolerance) ->
Solution.Found (annualPercentageRate upr unitPeriodsPerYear, iteration, tolerance)
Expand Down
19 changes: 9 additions & 10 deletions src/Calculation.fs
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,12 @@ module Calculation =
/// utility functions for percent values
[<RequireQualifiedAccess>]
module Percent =
/// create a percent value from a decimal
/// create a percent value from a decimal, e.g. 0.5 -> 50%
let fromDecimal (m: decimal) = m * 100m |> Percent
/// round a percent value to two decimal places
let round (places: int) (Percent p) = Rounding.roundTo (RoundWith MidpointRounding.AwayFromZero) places p |> Percent
/// convert a percent value to a decimal
/// convert a percent value to a decimal, e.g. 50% -> 0.5
let toDecimal (Percent p) = p / 100m
/// multiply two percentages together
let multiply (Percent p1) (Percent p2) = p1 * p2 |> fromDecimal

/// the type of restriction placed on a possible value
[<RequireQualifiedAccess; Struct>]
Expand Down Expand Up @@ -188,8 +186,8 @@ module Calculation =
/// calculates the total amount based on any restrictions
let total (baseValue: int64<Cent>) amount =
match amount with
| Amount.Percentage (Percent percentage, restriction, rounding) ->
decimal baseValue * decimal percentage / 100m
| Amount.Percentage (percent, restriction, rounding) ->
decimal baseValue * Percent.toDecimal percent
|> Restriction.calculate restriction
|> Rounding.round rounding
| Amount.Simple simple ->
Expand Down Expand Up @@ -243,7 +241,7 @@ module Calculation =
/// iteratively solves for a given input using a generator function until the output is 0L<Cent> or within a set tolerance,
/// optionally relaxing the tolerance until a solution is found
[<TailCall>]
let solve (generator: decimal -> decimal) iterationLimit approximation toleranceOption (toleranceSteps: ToleranceSteps) =
let solve (generator: decimal -> decimal) target iterationLimit approximation toleranceOption (toleranceSteps: ToleranceSteps) =
let rec loop i lowerBound upperBound tolerance =
let midRange =
let x = (upperBound - lowerBound) / 2m
Expand All @@ -259,11 +257,12 @@ module Calculation =
loop 0 0m (approximation * 100m) newTolerance
else
let difference = generator newBound
let toleranceM = decimal tolerance
let lowerTolerance, upperTolerance =
match toleranceOption with
| BelowZero -> decimal -tolerance, 0m
| AroundZero -> decimal -tolerance, decimal tolerance
| AboveZero -> 0m, decimal tolerance
| BelowZero -> target - toleranceM, target
| AroundZero -> target - toleranceM, target + toleranceM
| AboveZero -> target, toleranceM
if difference >= lowerTolerance && difference <= upperTolerance then
Solution.Found(newBound, i, tolerance)
elif difference > upperTolerance then
Expand Down
2 changes: 1 addition & 1 deletion src/Scheduling.fs
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ module Scheduling =
let solution =
match sp.ScheduleConfig with
| AutoGenerateSchedule _ ->
Array.solve (generatePaymentValue initialItem sp.InterestConfig.Method) 100 (totalAddOnInterest |> decimal |> calculateLevelPayment) toleranceOption toleranceSteps
Array.solve (generatePaymentValue initialItem sp.InterestConfig.Method) 0m 100 (totalAddOnInterest |> decimal |> calculateLevelPayment) toleranceOption toleranceSteps
| FixedSchedules _
| CustomSchedule _ ->
schedule <-
Expand Down

0 comments on commit f1d2743

Please sign in to comment.