#!/usr/bin/python
""" Rational Street Performer Protocol round calculator
To use this module, first create a list of Pledge objects. Pass this list
to "solve" to find the maximum that can be raised. Given this maximum,
individual donations can be calculated with the Pledge.donation_given
method.
See the end of the file for an example.
The Pledge class defines a simple bounded linear pledge, but other
pledge curves are possible. To define your own pledge curve, make
a class with "upper_bound" and "donation_given" methods. A pledge curve
in this implementation must be monotonically increasing (but it would
be fairly straightforward to remove this constraint).
"""
__version__ = "0.1"
class Pledge:
""" Represent a conditional pledge of form
I will donate one dollar in every raised
over , up to a total donation of """
def __init__(self, name, must_raise_over, dollar_in_every, upper_bound):
self._name = name
self._must_raise_over = must_raise_over
self._dollar_in_every = dollar_in_every
self._upper_bound = upper_bound
def name(self):
return self._name
def upper_bound(self):
""" What is the most that I would ever pay? """
return self._upper_bound
def donation_given(self, total):
""" Given that the total raised is , how much am I willing to
pay? """
donation = (total - self._must_raise_over) / self._dollar_in_every
if donation < 0.0:
return 0.0
elif donation > self._upper_bound:
return self._upper_bound
else:
return donation
def _solve_range(list, lower_bound, upper_bound, accuracy):
""" Find the maximum solution for the pledges given in within the
range .
If there is no solution, return 0.0 """
# What would be raised, given the upper bound?
total = 0.0
for item in list:
total = total + item.donation_given(upper_bound)
# Is the upper bound itself a solution?
if total >= upper_bound:
return total
# Is a solution impossible within this range?
# Have we reached the desired accuracy and not found a solution?
if total < lower_bound or upper_bound - lower_bound < accuracy:
return 0.0
# Is there a solution in the upper half of the range?
mid_point = (lower_bound + upper_bound) / 2.0
result = _solve_range(list, mid_point, upper_bound, accuracy)
if result:
return result
# Is there a solution in the lower half of the range?
return _solve_range(list, lower_bound, mid_point, accuracy)
def solve(list, accuracy=0.0001):
""" Find the maximum solution for the pledges given in , to a
given . """
# What is an upper bound on the amount that could be raised?
max = 0.0
for item in list:
max = max + item.upper_bound()
# Search for a solution within the largest possible range
return _solve_range(list, 0.0, max, accuracy)
if __name__ == "__main__":
# Specify a set of pledges
pledges = [
# Peter will pay $1 in every $2 raised over $10 up to a maximum of $100
Pledge("Peter", 10.0, 2.0, 100.0),
Pledge("Robin", 0.0, 3.0, 200.0),
Pledge("Garry", 10.0, 3.0, 15.0)
]
# Find the total amount raised
solution = solve(pledges)
print "Total raised: $%.2f\n" % solution
# Calculate each donator's contribution
for item in pledges:
print item.name(), "pays $%.2f" % item.donation_given(solution)