diff --git a/CHANGELOG.md b/CHANGELOG.md index 85a260f..038e5f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,12 @@ ENHANCEMENT: - sequentium.core.core.py: added __str__ method in Sequence class - sequentium.ruff.toml: added rule "ANN" - sequentium.core.core.py: added __eq__ method in Sequence clas +- sequentium.core.mixin.py: new script for mixin classes +- sequentium.core.mixin.py: added AlmostMonotonicIncreasingMixin class +- sequentium.sequences.integer.recursive.py: added A000931 +FIX: +- sequentium.core.core.py: method __getitem__ return a generator when the stop value in the slice is missing --- ## [0.0.0] - 2023-12-28 diff --git a/sequence/SEQUENCES_LIST.md b/sequence/SEQUENCES_LIST.md index 57e03f2..28bdcbe 100644 --- a/sequence/SEQUENCES_LIST.md +++ b/sequence/SEQUENCES_LIST.md @@ -24,6 +24,7 @@ List of implemented integer sequences. | A000290 | The square numbers | `A000290`, `SquareNumbers` | https://oeis.org/A000290 | | A000326 | The pentagonal numbers | `A000326`, `PentagonalNumbers` | https://oeis.org/A000326 | | A000384 | The hexagonal numbers | `A000384`, `HexagonalNumbers` | https://oeis.org/A000384 | +| A000931 | The Padovan numbers | `A000931`, `PadovanNumbers`, `PadovanSequence` | https://oeis.org/A000931 | | A001045 | The Jacobsthal numbers | `A001045`, `JacobsthalNumbers`, `JacobsthalSequence` | https://oeis.org/A001045 | | A001228 | Orders of sporadic simple groups | `A001228` | https://oeis.org/A001228 | | A001591 | The Pentanacci numbers | `A001591`, `PentanacciNumbers` | https://oeis.org/A001591 | diff --git a/sequence/core/core.py b/sequence/core/core.py index d8a415f..1983b9d 100644 --- a/sequence/core/core.py +++ b/sequence/core/core.py @@ -30,7 +30,7 @@ def __iter__(self) -> Generator: def __getitem__(self, item: Any) -> Union[List, Any]: if isinstance(item, slice): if item.stop is None and self.is_finite is False: - return InfiniteSequenceError + return islice(self._as_generator(), item.start, None) return self._as_list(start=item.start, stop=item.stop, step=item.step) return self._at(index=item) diff --git a/sequence/core/infinite_type.py b/sequence/core/infinite_type.py index e4eb238..357d949 100644 --- a/sequence/core/infinite_type.py +++ b/sequence/core/infinite_type.py @@ -69,15 +69,3 @@ def _as_generator(self) -> Generator: def property(self, number: Any) -> bool: """Abstract method to define the property for elements in the sequence.""" raise NotImplementedError - - -class MonotonicIncreasing: - """Mixin class for monotonic increasing sequences.""" - - def __contains__(self, item: Any) -> bool: - for element in self._as_generator(): - if element == item: - return True - if element > item: - return False - return False diff --git a/sequence/core/mixin.py b/sequence/core/mixin.py new file mode 100644 index 0000000..61d3097 --- /dev/null +++ b/sequence/core/mixin.py @@ -0,0 +1,20 @@ +from typing import Any, List, ClassVar + +from sequence.core.utils.functions import is_in_monotonic_increasing_generator + + +class MonotonicIncreasingMixin: + """Mixin class for monotonic increasing sequences.""" + + def __contains__(self, item: Any) -> bool: + return is_in_monotonic_increasing_generator(generator=self, item=item) + + +class AlmostMonotonicIncreasingMixin: + """Mixin class for almost monotonic increasing sequences.""" + offset: ClassVar[List[Any]] + + def __contains__(self, item: Any) -> bool: + if item in self.offset: + return True + return is_in_monotonic_increasing_generator(generator=self[len(self.offset):], item=item) diff --git a/sequence/core/utils/functions.py b/sequence/core/utils/functions.py index be69279..4c3ed04 100644 --- a/sequence/core/utils/functions.py +++ b/sequence/core/utils/functions.py @@ -1,4 +1,21 @@ from math import isqrt +from typing import Generator, Any + + +def is_in_monotonic_increasing_generator(generator: Generator, item: Any) -> bool: + """ + Check if the element 'item' is present in a monotonic increasing generator. + + Args: + - generator (Generator) + - item (Any): The item to be checked for presence in the generator + """ + for element in generator: + if element == item: + return True + if element > item: + return False + return False def is_prime(number: int) -> bool: diff --git a/sequence/sequences/integer/explicit.py b/sequence/sequences/integer/explicit.py index 7a3e433..b2b46ab 100644 --- a/sequence/sequences/integer/explicit.py +++ b/sequence/sequences/integer/explicit.py @@ -1,7 +1,7 @@ from typing import Any from sequence.core.infinite_type import Explicit -from sequence.core.infinite_type import MonotonicIncreasing +from sequence.core.mixin import MonotonicIncreasingMixin from sequence.sequences.integer.explicit_generalised_sequences import GeneralisedNexusNumbers, PolygonalNumbers @@ -67,7 +67,7 @@ def __init__(self) -> None: HexagonalNumbers = A000384 -class A001045(MonotonicIncreasing, Explicit): +class A001045(MonotonicIncreasingMixin, Explicit): """Jacobsthal numbers (https://oeis.org/A001045).""" sequence_name = 'Jacobsthal numbers' @@ -111,7 +111,7 @@ def formula(self, index: int) -> int: OddNumbers = A005408 -class A014551(MonotonicIncreasing, Explicit): +class A014551(MonotonicIncreasingMixin, Explicit): """Jacobsthal-Lucas numbers (https://oeis.org/A014551).""" sequence_name = 'Jacobsthal-Lucas numbers' diff --git a/sequence/sequences/integer/explicit_generalised_sequences.py b/sequence/sequences/integer/explicit_generalised_sequences.py index 95a6e1b..eb790b0 100644 --- a/sequence/sequences/integer/explicit_generalised_sequences.py +++ b/sequence/sequences/integer/explicit_generalised_sequences.py @@ -1,10 +1,11 @@ from typing import Any -from sequence.core.infinite_type import Explicit, MonotonicIncreasing +from sequence.core.infinite_type import Explicit +from sequence.core.mixin import MonotonicIncreasingMixin from sequence.core.utils.validation import validate_positive_integer -class GeneralisedNexusNumbers(MonotonicIncreasing, Explicit): +class GeneralisedNexusNumbers(MonotonicIncreasingMixin, Explicit): """ Class representing a sequence of Generalised Nexus Numbers (https://mathworld.wolfram.com/NexusNumber.html). diff --git a/sequence/sequences/integer/recursive.py b/sequence/sequences/integer/recursive.py index 5d20d93..4d72724 100644 --- a/sequence/sequences/integer/recursive.py +++ b/sequence/sequences/integer/recursive.py @@ -1,5 +1,6 @@ -from typing import Any - +from typing import Any, Tuple, List, ClassVar +from sequence.core.mixin import AlmostMonotonicIncreasingMixin +from sequence.core.infinite_type import Recursive from sequence.sequences.integer.recursive_generalised_sequences import ( HighOrderFibonacciNumbers, LucasSequenceU, @@ -69,6 +70,22 @@ def __init__(self) -> None: LambdaNumbers = A000129 +class A000931(AlmostMonotonicIncreasingMixin, Recursive): + """Padovan numbers (https://oeis.org/A000931)""" + sequence_name = 'Padovan numbers' + offset: ClassVar[List[int]] = [1, 0, 0, 1, 0, 1] + + def __init__(self) -> None: + super().__init__(start_terms=(1, 0, 0)) + + def formula(self, terms: Tuple[Any, ...]) -> Tuple[Any, ...]: + return terms[1], terms[2], terms[1] + terms[0] + + +PadovanNumbers = A000931 +PadovanSequence = A000931 + + class A001591(HighOrderFibonacciNumbers): """Pentanacci numbers (https://oeis.org/A001591).""" sequence_name = 'Pentanacci numbers' diff --git a/sequence/sequences/integer/recursive_generalised_sequences.py b/sequence/sequences/integer/recursive_generalised_sequences.py index 79d3b49..8d16dc2 100644 --- a/sequence/sequences/integer/recursive_generalised_sequences.py +++ b/sequence/sequences/integer/recursive_generalised_sequences.py @@ -1,10 +1,11 @@ from typing import Tuple, Any -from sequence.core.infinite_type import Recursive, MonotonicIncreasing +from sequence.core.infinite_type import Recursive +from sequence.core.mixin import MonotonicIncreasingMixin from sequence.core.utils.validation import validate_integer, validate_positive_integer -class HighOrderFibonacciNumbers(MonotonicIncreasing, Recursive): +class HighOrderFibonacciNumbers(MonotonicIncreasingMixin, Recursive): """ Class for generating high order Fibonacci numbers (https://mathworld.wolfram.com/Fibonaccin-StepNumber.html). @@ -31,7 +32,7 @@ def formula(self, terms: Tuple[Any, ...]) -> Tuple[Any, ...]: return *[terms[i] for i in range(1, self.order)], sum(terms) -class LucasSequenceU(MonotonicIncreasing, Recursive): +class LucasSequenceU(MonotonicIncreasingMixin, Recursive): """ The class generates the Lucas sequence U_n (https://en.wikipedia.org/wiki/Lucas_sequence). The sequence is defined by the recurrence relation: U_{n+2} = p * U_{n+1} - q * U_n, diff --git a/tests/tests_integer_sequences/test_recursive.py b/tests/tests_integer_sequences/test_recursive.py index 04804b4..f91ae29 100644 --- a/tests/tests_integer_sequences/test_recursive.py +++ b/tests/tests_integer_sequences/test_recursive.py @@ -52,6 +52,16 @@ class TestA000129(SequenceTestSuite): ] +class TestA000931(SequenceTestSuite): + sequence = A000931() + sequence_name = 'Padovan numbers' + ground_truth = [ + 1, 0, 0, 1, 0, 1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21, 28, 37, 49, 65, 86, 114, 151, 200, 265, 351, 465, 616, + 816, 1081, 1432, 1897, 2513, 3329, 4410, 5842, 7739, 10252, 13581, 17991, 23833, 31572, 41824, 55405, 73396, + 97229, 128801, 170625, + ] + + class TestA001591(SequenceTestSuite): sequence = A001591() sequence_name = 'Pentanacci numbers'