Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/python
- from sys import argv
- from sys import exit
- from sys import stderr
- from math import inf
- # The magic equation solved by this program:
- #
- # growth := 1 + interest
- #
- # growth * periodic_deposit + interest * outcome
- # interest ** periods == ------------------------------------------------------
- # growth * periodic_deposit + interest * initial_deposit
- def check_inputs(periods, initial_deposit, periodic_deposit, outcome):
- print(f' # periods: {periods}', file=stderr)
- print(f' initial deposit: {initial_deposit}', file=stderr)
- print(f' periodic deposit: {periodic_deposit}', file=stderr)
- print(f' outcome: {outcome}', file=stderr)
- errors = []
- periods_ok, initial_deposit_ok, periodic_deposit_ok, outcome_ok = (
- False, False, False, False)
- try:
- periods = int(periods)
- if periods <= 0:
- errors.append(f'[# periods]: expected > 0, got: {periods}')
- else:
- periods_ok = True
- except Exception as error:
- errors.append(f'[# periods]: {error}')
- try:
- initial_deposit = int(initial_deposit)
- if initial_deposit < 0:
- errors.append(f'[initial deposit]: expected >= 0, got: {initial_deposit}')
- else:
- initial_deposit_ok = True
- except Exception as error:
- errors.append(f'[initial deposit]: {error}')
- try:
- periodic_deposit = int(periodic_deposit)
- if periodic_deposit < 0:
- errors.append('[periodic deposit]: expected >= 0, got: '
- f'{periodic_deposit}')
- else:
- periodic_deposit_ok = True
- except Exception as error:
- errors.append(f'[periodic deposit]: {error}')
- try:
- outcome = int(outcome)
- if outcome <= 0:
- errors.append(f'[outcome]: expected > 0, got: {outcome}')
- else:
- outcome_ok = True
- except Exception as error:
- errors.append(f'[outcome]: {error}')
- if initial_deposit_ok and periodic_deposit_ok:
- if initial_deposit + periodic_deposit <= 0:
- errors.append('[initial_deposit] + [periodic_deposit]: expected > 0, '
- f'got: {initial_deposit} + {periodic_deposit} <= 0')
- elif periods_ok: # elif, as this makes no sense with both deposits == 0
- sum_of_payments = initial_deposit + periods * periodic_deposit
- print(f' sum of payments: {sum_of_payments}', file=stderr)
- if outcome_ok:
- if outcome < sum_of_payments:
- errors.append('[initial deposit] + [periods] * [periodic deposit]: '
- 'expected >= [outcome], got: '
- f'{outcome} < {sum_of_payments}')
- else:
- absolute_gain = outcome - sum_of_payments
- relative_gain = f'{round(100 * absolute_gain / sum_of_payments, 6)}%'
- print(f' absolute gain: {absolute_gain} ', file=stderr)
- print(f' relative gain: {relative_gain}', file=stderr)
- if errors:
- raise ValueError('\n'.join(errors))
- return periods, initial_deposit, periodic_deposit, outcome
- class Savings:
- def __init__(self, initial_deposit, periodic_deposit, outcome):
- self.periodic_deposit = periodic_deposit / outcome # normalized
- self.outcome_periodic = self.periodic_deposit + 1 # 1 == outcome
- self.initial_periodic = self.periodic_deposit + initial_deposit / outcome
- def ugly_fraction(self, interest):
- den = interest * self.initial_periodic + self.periodic_deposit
- return (((interest * self.outcome_periodic + self.periodic_deposit) / den)
- if den else inf)
- def percent(fn):
- return lambda *args, **kwargs: f'{round(100 * fn(*args, **kwargs), 6)}%'
- @percent
- def effective_interest(periods, initial_deposit, periodic_deposit, outcome):
- savings = Savings(initial_deposit, periodic_deposit, outcome)
- lower, upper = 0, .01
- while savings.ugly_fraction(upper) > (1 + upper) ** periods:
- upper *= 2
- diff = upper - lower
- last_diff = 2 * diff
- while last_diff > diff:
- interest = (upper + lower) / 2
- if savings.ugly_fraction(interest) > (1 + interest) ** periods:
- lower = interest
- else:
- upper = interest
- diff, last_diff = upper - lower, diff
- return interest
- def usage():
- return f'''
- Usage: {argv[0]} [# periods] [initial deposit] [periodic deposit] [outcome]
- Examples:
- * 0 coins initially, 1 coin periodically, 20 coins expected in 10 periods:
- $ {argv[0]} 10 0 1 20
- {effective_interest(10, 0, 1, 20)}
- * 1 coin initially, 1 coin periodically, 20 coinst expected in 10 periods:
- $ {argv[0]} 10 1 1 20
- {effective_interest(10, 1, 1, 20)}
- * 10 coins initially, 0 coins periodically, 20 coins expected in 10 periods:
- $ {argv[0]} 10 10 0 20
- {effective_interest(10, 10, 0, 20)}
- * 10 coins initially, 1 coin periodically, 20 coins expected in 10 periods:
- $ {argv[0]} 10 10 1 20
- {effective_interest(10, 10, 1, 20)}
- '''
- if len(argv) != 5:
- print(usage(), file=stderr)
- exit(1)
- try:
- inputs = check_inputs(*argv[1:])
- print('effective interest: ', file=stderr, end='', flush=True)
- print(effective_interest(*inputs))
- except ValueError as error:
- print(f'\nERRORS:\n{error}', file=stderr)
- print(usage(), file=stderr)
- exit(2)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement