3 # © Copyright 2021-2023, Scott Gasch
5 """A class to represent a rate of change."""
7 from typing import Optional
11 """A class to represent a rate of change."""
15 multiplier: Optional[float] = None,
17 percentage: Optional[float] = None,
18 percent_change: Optional[float] = None,
20 """Constructs a new :class:`Rate` from a multiplier, percentage, or
21 percent change. One and only one of these may be passed. These are
22 a little confusing so here's an example...
26 A multiplier of 1.5x is the same as a percentage of 150% and
27 is also the same as a 50% change. Let's examine an original
28 amount of 100. Multiplying it by a 1.5x multiplier yields 150.
29 Multiplying it by 150% yields 150. Increasing it by 50% also
33 multiplier: provides the number that you would multiply a base
34 amount by to modify it
35 percentage: provides the multiplier as a percentage
36 percent_change: provides the multiplier as a percent change to
40 ValueError: if more than one of percentage, percent_change and
41 multiplier is provided
44 if multiplier is not None:
45 if isinstance(multiplier, str):
46 multiplier = multiplier.replace("%", "")
49 self.multiplier: float = m
51 self.multiplier = multiplier
53 if percentage is not None:
54 self.multiplier = percentage / 100
56 if percent_change is not None:
57 self.multiplier = 1.0 + percent_change / 100
61 "Exactly one of percentage, percent_change or multiplier is required."
64 def apply_to(self, other):
65 """Applies the rate to a base number.
68 other: the base to apply the change rate to.
71 The result after the change.
73 return self.__mul__(other)
76 """Applies the rate to a base number.
79 other: the base to apply the change rate to.
82 The result after the change.
84 return self.__mul__(other)
87 return self.multiplier
89 def __mul__(self, other):
90 return self.multiplier * float(other)
94 def __truediv__(self, other):
95 return self.multiplier / float(other)
97 def __add__(self, other):
98 return self.multiplier + float(other)
102 def __sub__(self, other):
103 return self.multiplier - float(other)
105 def __eq__(self, other):
106 return self.multiplier == float(other)
108 def __ne__(self, other):
109 return not self.__eq__(other)
111 def __lt__(self, other):
112 return self.multiplier < float(other)
114 def __gt__(self, other):
115 return self.multiplier > float(other)
117 def __le__(self, other):
118 return self < other or self == other
120 def __ge__(self, other):
121 return self > other or self == other
124 return self.multiplier
126 def __repr__(self, *, relative=False, places=3):
128 percentage = (self.multiplier - 1.0) * 100.0
130 percentage = self.multiplier * 100.0
131 return f"{percentage:+.{places}f}%"