Skip to content

Commit eec88dc

Browse files
committed
Add operations to IntValue and FloatValue
1 parent b279d40 commit eec88dc

File tree

5 files changed

+408
-21
lines changed

5 files changed

+408
-21
lines changed

src/spellbind/float_values.py

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
from __future__ import annotations
2+
from typing_extensions import Self
23

34
import operator
45
from abc import ABC, abstractmethod
5-
from typing import Generic, Callable, Sequence, TypeVar
6+
from typing import Generic, Callable, Sequence, TypeVar, overload
7+
8+
from typing_extensions import TYPE_CHECKING
69

710
from spellbind.bool_values import BoolValue
8-
from spellbind.values import Value, SimpleVariable, DerivedValue, DerivedValueBase, Constant
11+
from spellbind.values import Value, SimpleVariable, DerivedValue, DerivedValueBase, Constant, CombinedTwoValues
12+
13+
if TYPE_CHECKING:
14+
from spellbind.int_values import IntValue, IntLike
915

10-
FloatLike = int | Value[int] | float | Value[float]
16+
FloatLike = Value[int] | float | Value[float]
1117

1218
_S = TypeVar("_S")
1319
_T = TypeVar("_T")
@@ -39,6 +45,41 @@ def __truediv__(self, other: FloatLike) -> FloatValue:
3945
def __rtruediv__(self, other: int | float) -> FloatValue:
4046
return DivideValues(other, self)
4147

48+
def __pow__(self, other: FloatLike) -> FloatValue:
49+
return PowerFloatValues(self, other)
50+
51+
def __rpow__(self, other: FloatLike) -> FloatValue:
52+
return PowerFloatValues(other, self)
53+
54+
def __mod__(self, other: FloatLike) -> FloatValue:
55+
return ModuloFloatValues(self, other)
56+
57+
def __rmod__(self, other: int | float) -> FloatValue:
58+
return ModuloFloatValues(other, self)
59+
60+
def __abs__(self) -> FloatValue:
61+
return AbsFloatValue(self)
62+
63+
def floor(self) -> IntValue:
64+
from spellbind.int_values import FloorFloatValue
65+
return FloorFloatValue(self)
66+
67+
def ceil(self) -> IntValue:
68+
from spellbind.int_values import CeilFloatValue
69+
return CeilFloatValue(self)
70+
71+
@overload
72+
def round(self) -> IntValue: ...
73+
74+
@overload
75+
def round(self, ndigits: IntLike) -> FloatValue: ...
76+
77+
def round(self, ndigits: IntLike | None = None) -> FloatValue | IntValue:
78+
if ndigits is None:
79+
from spellbind.int_values import RoundFloatToIntValue
80+
return RoundFloatToIntValue(self)
81+
return RoundFloatValue(self, ndigits)
82+
4283
def __lt__(self, other: FloatLike) -> BoolValue:
4384
return CompareNumbersValues(self, other, operator.lt)
4485

@@ -54,6 +95,9 @@ def __ge__(self, other: FloatLike) -> BoolValue:
5495
def __neg__(self) -> FloatValue:
5596
return NegateFloatValue(self)
5697

98+
def __pos__(self) -> Self:
99+
return self
100+
57101

58102
class FloatConstant(FloatValue, Constant[float]):
59103
pass
@@ -106,9 +150,7 @@ def value(self) -> _U:
106150

107151

108152
class CombinedTwoFloatValues(CombinedFloatValues[_U], Generic[_U], ABC):
109-
def __init__(self,
110-
left: float | Value[int] | Value[float],
111-
right: float | Value[int] | Value[float]):
153+
def __init__(self, left: FloatLike, right: FloatLike):
112154
super().__init__(left, right)
113155

114156
def transform(self, values: Sequence[float]) -> _U:
@@ -128,9 +170,6 @@ def transform(self, values: Sequence[float]) -> float:
128170

129171

130172
class SubtractFloatValues(CombinedTwoFloatValues[float], FloatValue):
131-
def __init__(self, left: FloatLike, right: FloatLike):
132-
super().__init__(left, right)
133-
134173
def transform_two(self, left: float, right: float) -> float:
135174
return left - right
136175

@@ -147,9 +186,6 @@ def transform(self, values: Sequence[float]) -> float:
147186

148187

149188
class DivideValues(CombinedTwoFloatValues[float], FloatValue):
150-
def __init__(self, left: FloatLike, right: FloatLike):
151-
super().__init__(left, right)
152-
153189
def transform_two(self, left: float, right: float) -> float:
154190
return left / right
155191

@@ -158,6 +194,29 @@ class FloatVariable(SimpleVariable[float], FloatValue):
158194
pass
159195

160196

197+
class RoundFloatValue(CombinedTwoValues[float, int, float], FloatValue):
198+
def __init__(self, value: FloatValue, ndigits: IntLike):
199+
super().__init__(value, ndigits)
200+
201+
def transform(self, value: float, ndigits: int) -> float:
202+
return round(value, ndigits)
203+
204+
205+
class ModuloFloatValues(CombinedTwoFloatValues[float], FloatValue):
206+
def transform_two(self, left: float, right: float) -> float:
207+
return left % right
208+
209+
210+
class AbsFloatValue(DerivedValue[float, float], FloatValue):
211+
def transform(self, value: float) -> float:
212+
return abs(value)
213+
214+
215+
class PowerFloatValues(CombinedTwoFloatValues[float], FloatValue):
216+
def transform_two(self, left: float, right: float) -> float:
217+
return left ** right
218+
219+
161220
class NegateFloatValue(DerivedValue[float, float], FloatValue):
162221
def transform(self, value: float) -> float:
163222
return -value

src/spellbind/int_values.py

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
2+
from typing_extensions import Self
23

4+
import math
35
import operator
46
from abc import ABC
57
from typing import overload
@@ -92,6 +94,21 @@ def __floordiv__(self, other: IntLike) -> IntValue:
9294
def __rfloordiv__(self, other: int) -> IntValue:
9395
return FloorDivideIntValues(other, self)
9496

97+
def __pow__(self, other: IntLike) -> IntValue:
98+
return PowerIntValues(self, other)
99+
100+
def __rpow__(self, other: int) -> IntValue:
101+
return PowerIntValues(other, self)
102+
103+
def __mod__(self, other: IntLike) -> IntValue:
104+
return ModuloIntValues(self, other)
105+
106+
def __rmod__(self, other: int) -> IntValue:
107+
return ModuloIntValues(other, self)
108+
109+
def __abs__(self) -> IntValue:
110+
return AbsIntValue(self)
111+
95112
def __lt__(self, other: FloatLike) -> BoolValue:
96113
return CompareNumbersValues(self, other, operator.lt)
97114

@@ -107,6 +124,9 @@ def __ge__(self, other: FloatLike) -> BoolValue:
107124
def __neg__(self) -> IntValue:
108125
return NegateIntValue(self)
109126

127+
def __pos__(self) -> Self:
128+
return self
129+
110130

111131
class IntConstant(IntValue, Constant[int]):
112132
pass
@@ -122,9 +142,6 @@ def transform(self, *values: int) -> int:
122142

123143

124144
class SubtractIntValues(CombinedTwoValues[int, int, int], IntValue):
125-
def __init__(self, left: IntLike, right: IntLike):
126-
super().__init__(left, right)
127-
128145
def transform(self, left: int, right: int) -> int:
129146
return left - right
130147

@@ -138,21 +155,45 @@ def transform(self, *values: int) -> int:
138155

139156

140157
class DivideIntValues(CombinedTwoValues[int, int, float], FloatValue):
141-
def __init__(self, left: IntLike, right: IntLike):
142-
super().__init__(left, right)
143-
144158
def transform(self, left: int, right: int) -> float:
145159
return left / right
146160

147161

148162
class FloorDivideIntValues(CombinedTwoValues[int, int, int], IntValue):
149-
def __init__(self, left: IntLike, right: IntLike):
150-
super().__init__(left, right)
151-
152163
def transform(self, left: int, right: int) -> int:
153164
return left // right
154165

155166

167+
class PowerIntValues(CombinedTwoValues[int, int, int], IntValue):
168+
def transform(self, left: int, right: int) -> int:
169+
return left ** right
170+
171+
172+
class ModuloIntValues(CombinedTwoValues[int, int, int], IntValue):
173+
def transform(self, left: int, right: int) -> int:
174+
return left % right
175+
176+
177+
class AbsIntValue(DerivedValue[int, int], IntValue):
178+
def transform(self, value: int) -> int:
179+
return abs(value)
180+
181+
156182
class NegateIntValue(DerivedValue[int, int], IntValue):
157183
def transform(self, value: int) -> int:
158184
return -value
185+
186+
187+
class FloorFloatValue(DerivedValue[float, int], IntValue):
188+
def transform(self, value: float) -> int:
189+
return math.floor(value)
190+
191+
192+
class CeilFloatValue(DerivedValue[float, int], IntValue):
193+
def transform(self, value: float) -> int:
194+
return math.ceil(value)
195+
196+
197+
class RoundFloatToIntValue(DerivedValue[float, int], IntValue):
198+
def transform(self, value: float) -> int:
199+
return round(value)

tests/test_float_values_arithmatic.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,145 @@ def test_negate_float_value_zero():
247247

248248
v0.value = 7.8
249249
assert v1.value == -7.8
250+
251+
252+
# Power Tests
253+
def test_power_float_values():
254+
v0 = FloatVariable(2.0)
255+
v1 = FloatVariable(3.0)
256+
v2 = v0 ** v1
257+
assert v2.value == 8.0
258+
259+
v0.value = 3.0
260+
assert v2.value == 27.0
261+
262+
263+
def test_power_float_value_to_float():
264+
v0 = FloatVariable(2.0)
265+
v2 = v0 ** 3.0
266+
assert v2.value == 8.0
267+
268+
v0.value = 3.0
269+
assert v2.value == 27.0
270+
271+
272+
def test_power_float_value_to_int():
273+
v0 = FloatVariable(2.5)
274+
v2 = v0 ** 2
275+
assert v2.value == 6.25
276+
277+
v0.value = 3.0
278+
assert v2.value == 9.0
279+
280+
281+
def test_power_float_value_to_int_value():
282+
v0 = FloatVariable(2.0)
283+
v1 = IntVariable(3)
284+
v2 = v0 ** v1
285+
assert v2.value == 8.0
286+
287+
v0.value = 3.0
288+
assert v2.value == 27.0
289+
290+
291+
def test_power_float_to_float_value():
292+
v1 = FloatVariable(3.0)
293+
v2 = 2.0 ** v1
294+
assert v2.value == 8.0
295+
296+
v1.value = 4.0
297+
assert v2.value == 16.0
298+
299+
300+
def test_power_int_to_float_value():
301+
v1 = FloatVariable(3.0)
302+
v2 = 2 ** v1
303+
assert v2.value == 8.0
304+
305+
v1.value = 4.0
306+
assert v2.value == 16.0
307+
308+
309+
# Modulo Tests
310+
def test_modulo_float_values():
311+
v0 = FloatVariable(10.5)
312+
v1 = FloatVariable(3.0)
313+
v2 = v0 % v1
314+
assert v2.value == 1.5
315+
316+
v0.value = 15.5
317+
assert v2.value == 0.5
318+
319+
320+
def test_modulo_float_value_by_float():
321+
v0 = FloatVariable(10.5)
322+
v2 = v0 % 3.0
323+
assert v2.value == 1.5
324+
325+
v0.value = 15.5
326+
assert v2.value == 0.5
327+
328+
329+
def test_modulo_float_value_by_int():
330+
v0 = FloatVariable(10.5)
331+
v2 = v0 % 3
332+
assert v2.value == 1.5
333+
334+
v0.value = 15.5
335+
assert v2.value == 0.5
336+
337+
338+
def test_modulo_float_value_by_int_value():
339+
v0 = FloatVariable(10.5)
340+
v1 = IntVariable(3)
341+
v2 = v0 % v1
342+
assert v2.value == 1.5
343+
344+
v0.value = 15.5
345+
assert v2.value == 0.5
346+
347+
348+
def test_modulo_float_by_float_value():
349+
v1 = FloatVariable(3.0)
350+
v2 = 10.5 % v1
351+
assert v2.value == 1.5
352+
353+
v1.value = 4.0
354+
assert v2.value == 2.5
355+
356+
357+
def test_modulo_int_by_float_value():
358+
v1 = FloatVariable(3.0)
359+
v2 = 10 % v1
360+
assert v2.value == 1.0
361+
362+
v1.value = 4.0
363+
assert v2.value == 2.0
364+
365+
366+
# Absolute Value Tests
367+
def test_abs_float_value_positive():
368+
v0 = FloatVariable(5.5)
369+
v1 = abs(v0)
370+
assert v1.value == 5.5
371+
372+
v0.value = 10.8
373+
assert v1.value == 10.8
374+
375+
376+
def test_abs_float_value_negative():
377+
v0 = FloatVariable(-5.5)
378+
v1 = abs(v0)
379+
assert v1.value == 5.5
380+
381+
v0.value = -10.8
382+
assert v1.value == 10.8
383+
384+
385+
def test_abs_float_value_zero():
386+
v0 = FloatVariable(0.0)
387+
v1 = abs(v0)
388+
assert v1.value == 0.0
389+
390+
v0.value = -7.2
391+
assert v1.value == 7.2

0 commit comments

Comments
 (0)