Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 71 additions & 12 deletions src/spellbind/float_values.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
from __future__ import annotations
from typing_extensions import Self

import operator
from abc import ABC, abstractmethod
from typing import Generic, Callable, Sequence, TypeVar
from typing import Generic, Callable, Sequence, TypeVar, overload

from typing_extensions import TYPE_CHECKING

from spellbind.bool_values import BoolValue
from spellbind.values import Value, SimpleVariable, DerivedValue, DerivedValueBase, Constant
from spellbind.values import Value, SimpleVariable, DerivedValue, DerivedValueBase, Constant, CombinedTwoValues

if TYPE_CHECKING:
from spellbind.int_values import IntValue, IntLike

FloatLike = int | Value[int] | float | Value[float]
FloatLike = Value[int] | float | Value[float]

_S = TypeVar("_S")
_T = TypeVar("_T")
Expand Down Expand Up @@ -39,6 +45,41 @@ def __truediv__(self, other: FloatLike) -> FloatValue:
def __rtruediv__(self, other: int | float) -> FloatValue:
return DivideValues(other, self)

def __pow__(self, other: FloatLike) -> FloatValue:
return PowerFloatValues(self, other)

def __rpow__(self, other: FloatLike) -> FloatValue:
return PowerFloatValues(other, self)

def __mod__(self, other: FloatLike) -> FloatValue:
return ModuloFloatValues(self, other)

def __rmod__(self, other: int | float) -> FloatValue:
return ModuloFloatValues(other, self)

def __abs__(self) -> FloatValue:
return AbsFloatValue(self)

def floor(self) -> IntValue:
from spellbind.int_values import FloorFloatValue
return FloorFloatValue(self)

def ceil(self) -> IntValue:
from spellbind.int_values import CeilFloatValue
return CeilFloatValue(self)

@overload
def round(self) -> IntValue: ...

@overload
def round(self, ndigits: IntLike) -> FloatValue: ...

def round(self, ndigits: IntLike | None = None) -> FloatValue | IntValue:
if ndigits is None:
from spellbind.int_values import RoundFloatToIntValue
return RoundFloatToIntValue(self)
return RoundFloatValue(self, ndigits)

def __lt__(self, other: FloatLike) -> BoolValue:
return CompareNumbersValues(self, other, operator.lt)

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

def __pos__(self) -> Self:
return self


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


class CombinedTwoFloatValues(CombinedFloatValues[_U], Generic[_U], ABC):
def __init__(self,
left: float | Value[int] | Value[float],
right: float | Value[int] | Value[float]):
def __init__(self, left: FloatLike, right: FloatLike):
super().__init__(left, right)

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


class SubtractFloatValues(CombinedTwoFloatValues[float], FloatValue):
def __init__(self, left: FloatLike, right: FloatLike):
super().__init__(left, right)

def transform_two(self, left: float, right: float) -> float:
return left - right

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


class DivideValues(CombinedTwoFloatValues[float], FloatValue):
def __init__(self, left: FloatLike, right: FloatLike):
super().__init__(left, right)

def transform_two(self, left: float, right: float) -> float:
return left / right

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


class RoundFloatValue(CombinedTwoValues[float, int, float], FloatValue):
def __init__(self, value: FloatValue, ndigits: IntLike):
super().__init__(value, ndigits)

def transform(self, value: float, ndigits: int) -> float:
return round(value, ndigits)


class ModuloFloatValues(CombinedTwoFloatValues[float], FloatValue):
def transform_two(self, left: float, right: float) -> float:
return left % right


class AbsFloatValue(DerivedValue[float, float], FloatValue):
def transform(self, value: float) -> float:
return abs(value)


class PowerFloatValues(CombinedTwoFloatValues[float], FloatValue):
def transform_two(self, left: float, right: float) -> float:
return left ** right


class NegateFloatValue(DerivedValue[float, float], FloatValue):
def transform(self, value: float) -> float:
return -value
Expand Down
59 changes: 50 additions & 9 deletions src/spellbind/int_values.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations
from typing_extensions import Self

import math
import operator
from abc import ABC
from typing import overload
Expand Down Expand Up @@ -92,6 +94,21 @@ def __floordiv__(self, other: IntLike) -> IntValue:
def __rfloordiv__(self, other: int) -> IntValue:
return FloorDivideIntValues(other, self)

def __pow__(self, other: IntLike) -> IntValue:
return PowerIntValues(self, other)

def __rpow__(self, other: int) -> IntValue:
return PowerIntValues(other, self)

def __mod__(self, other: IntLike) -> IntValue:
return ModuloIntValues(self, other)

def __rmod__(self, other: int) -> IntValue:
return ModuloIntValues(other, self)

def __abs__(self) -> IntValue:
return AbsIntValue(self)

def __lt__(self, other: FloatLike) -> BoolValue:
return CompareNumbersValues(self, other, operator.lt)

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

def __pos__(self) -> Self:
return self


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


class SubtractIntValues(CombinedTwoValues[int, int, int], IntValue):
def __init__(self, left: IntLike, right: IntLike):
super().__init__(left, right)

def transform(self, left: int, right: int) -> int:
return left - right

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


class DivideIntValues(CombinedTwoValues[int, int, float], FloatValue):
def __init__(self, left: IntLike, right: IntLike):
super().__init__(left, right)

def transform(self, left: int, right: int) -> float:
return left / right


class FloorDivideIntValues(CombinedTwoValues[int, int, int], IntValue):
def __init__(self, left: IntLike, right: IntLike):
super().__init__(left, right)

def transform(self, left: int, right: int) -> int:
return left // right


class PowerIntValues(CombinedTwoValues[int, int, int], IntValue):
def transform(self, left: int, right: int) -> int:
return left ** right


class ModuloIntValues(CombinedTwoValues[int, int, int], IntValue):
def transform(self, left: int, right: int) -> int:
return left % right


class AbsIntValue(DerivedValue[int, int], IntValue):
def transform(self, value: int) -> int:
return abs(value)


class NegateIntValue(DerivedValue[int, int], IntValue):
def transform(self, value: int) -> int:
return -value


class FloorFloatValue(DerivedValue[float, int], IntValue):
def transform(self, value: float) -> int:
return math.floor(value)


class CeilFloatValue(DerivedValue[float, int], IntValue):
def transform(self, value: float) -> int:
return math.ceil(value)


class RoundFloatToIntValue(DerivedValue[float, int], IntValue):
def transform(self, value: float) -> int:
return round(value)
142 changes: 142 additions & 0 deletions tests/test_float_values_arithmatic.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,3 +247,145 @@ def test_negate_float_value_zero():

v0.value = 7.8
assert v1.value == -7.8


# Power Tests
def test_power_float_values():
v0 = FloatVariable(2.0)
v1 = FloatVariable(3.0)
v2 = v0 ** v1
assert v2.value == 8.0

v0.value = 3.0
assert v2.value == 27.0


def test_power_float_value_to_float():
v0 = FloatVariable(2.0)
v2 = v0 ** 3.0
assert v2.value == 8.0

v0.value = 3.0
assert v2.value == 27.0


def test_power_float_value_to_int():
v0 = FloatVariable(2.5)
v2 = v0 ** 2
assert v2.value == 6.25

v0.value = 3.0
assert v2.value == 9.0


def test_power_float_value_to_int_value():
v0 = FloatVariable(2.0)
v1 = IntVariable(3)
v2 = v0 ** v1
assert v2.value == 8.0

v0.value = 3.0
assert v2.value == 27.0


def test_power_float_to_float_value():
v1 = FloatVariable(3.0)
v2 = 2.0 ** v1
assert v2.value == 8.0

v1.value = 4.0
assert v2.value == 16.0


def test_power_int_to_float_value():
v1 = FloatVariable(3.0)
v2 = 2 ** v1
assert v2.value == 8.0

v1.value = 4.0
assert v2.value == 16.0


# Modulo Tests
def test_modulo_float_values():
v0 = FloatVariable(10.5)
v1 = FloatVariable(3.0)
v2 = v0 % v1
assert v2.value == 1.5

v0.value = 15.5
assert v2.value == 0.5


def test_modulo_float_value_by_float():
v0 = FloatVariable(10.5)
v2 = v0 % 3.0
assert v2.value == 1.5

v0.value = 15.5
assert v2.value == 0.5


def test_modulo_float_value_by_int():
v0 = FloatVariable(10.5)
v2 = v0 % 3
assert v2.value == 1.5

v0.value = 15.5
assert v2.value == 0.5


def test_modulo_float_value_by_int_value():
v0 = FloatVariable(10.5)
v1 = IntVariable(3)
v2 = v0 % v1
assert v2.value == 1.5

v0.value = 15.5
assert v2.value == 0.5


def test_modulo_float_by_float_value():
v1 = FloatVariable(3.0)
v2 = 10.5 % v1
assert v2.value == 1.5

v1.value = 4.0
assert v2.value == 2.5


def test_modulo_int_by_float_value():
v1 = FloatVariable(3.0)
v2 = 10 % v1
assert v2.value == 1.0

v1.value = 4.0
assert v2.value == 2.0


# Absolute Value Tests
def test_abs_float_value_positive():
v0 = FloatVariable(5.5)
v1 = abs(v0)
assert v1.value == 5.5

v0.value = 10.8
assert v1.value == 10.8


def test_abs_float_value_negative():
v0 = FloatVariable(-5.5)
v1 = abs(v0)
assert v1.value == 5.5

v0.value = -10.8
assert v1.value == 10.8


def test_abs_float_value_zero():
v0 = FloatVariable(0.0)
v1 = abs(v0)
assert v1.value == 0.0

v0.value = -7.2
assert v1.value == 7.2
Loading