From e73355c2a459e5390e7bc2f35f3923e592d4d5dc Mon Sep 17 00:00:00 2001 From: Max <133623746+imaxde@users.noreply.github.com> Date: Thu, 23 Oct 2025 13:53:04 +0300 Subject: [PATCH 01/13] Create licence --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..485db80 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Max + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From b425d764642fe02c8638fc395907358cfadfd653 Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 13:55:56 +0300 Subject: [PATCH 02/13] Create gitignore --- .gitignore | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ac20a5c --- /dev/null +++ b/.gitignore @@ -0,0 +1,177 @@ +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +.idea/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# LSP config files +pyrightconfig.json + +# End of https://www.toptal.com/developers/gitignore/api/python \ No newline at end of file From cb1b45e514751348afa121488bc7e00fdecacf2a Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 13:58:30 +0300 Subject: [PATCH 03/13] Select Python version --- .python-version | 1 + 1 file changed, 1 insertion(+) create mode 100644 .python-version diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..3a4f41e --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 \ No newline at end of file From fec8d80ee7eb2d54d1890f3a1c9ef8ff1dafd1f2 Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 14:07:50 +0300 Subject: [PATCH 04/13] Rename varibles in lowercase --- src/luhn.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/luhn.py b/src/luhn.py index 11d993c..eb88422 100644 --- a/src/luhn.py +++ b/src/luhn.py @@ -1,5 +1,5 @@ -def luhnСheck(cardNumber): - digits = [int(d) for d in str(cardNumber) if d.isdigit()] +def luhn_check(card_namber): + digits = [int(d) for d in str(card_namber) if d.isdigit()] control = digits.pop() parity = (len(digits))%2 total = 0 From 1165a479759d38613f01f98f98a3156521096598 Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 14:09:15 +0300 Subject: [PATCH 05/13] Rename function in lowercase --- src/fast_pow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fast_pow.py b/src/fast_pow.py index 1ba1dc1..755e207 100644 --- a/src/fast_pow.py +++ b/src/fast_pow.py @@ -1,4 +1,4 @@ -def fastPow(number, power): +def fast_pow(number, power): result = number while power != 1: result *= result From 852e0e9fe4f298b0e32f05dfaf6ff778b6ee2538 Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 14:45:04 +0300 Subject: [PATCH 06/13] Exception for negative power --- src/__init__.py | 0 test/test_fast_pow.py | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 src/__init__.py diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_fast_pow.py b/test/test_fast_pow.py index 5e4aebd..406164e 100644 --- a/test/test_fast_pow.py +++ b/test/test_fast_pow.py @@ -1,9 +1,20 @@ -from src.fast_pow import fastPow +from src.fast_pow import fast_pow +import pytest +import random def test_two_power_two(): - assert fastPow(2, 2) == 4 + assert fast_pow(2, 2) == 4 def test_negative(): - assert fastPow(-1, 4) == 1 + assert fast_pow(-1, 4) == 1 + + +@pytest.fixture +def random_n(): + return random.randint(1, 100) + + +def test_negative_power(random_n): + assert fast_pow(10, -1) == 10 ** (-1) \ No newline at end of file From 5d24e54c2f4d2026c93ed32c82b7384aa8182eea Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 15:01:21 +0300 Subject: [PATCH 07/13] Terminal app --- src/luhn.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/luhn.py b/src/luhn.py index eb88422..5fa036f 100644 --- a/src/luhn.py +++ b/src/luhn.py @@ -1,7 +1,10 @@ +import sys + + def luhn_check(card_namber): digits = [int(d) for d in str(card_namber) if d.isdigit()] control = digits.pop() - parity = (len(digits))%2 + parity = (len(digits)) % 2 total = 0 for i in range(len(digits)): if i % 2 == parity: @@ -12,3 +15,11 @@ def luhn_check(card_namber): else: total += digits[i] return (total + control) % 10 == 0 + + +if __name__ == "__main__": + input_number = sys.argv[1] + if luhn_check(input_number): + print("correct") + else: + print("incorrect") \ No newline at end of file From 086eec46edec64ba588dfd4af3197adddaa804cd Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 15:02:21 +0300 Subject: [PATCH 08/13] Exception for negative power --- src/fast_pow.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/fast_pow.py b/src/fast_pow.py index 755e207..7e14f47 100644 --- a/src/fast_pow.py +++ b/src/fast_pow.py @@ -1,6 +1,12 @@ +class NegativePowerException(Exception): + pass + + def fast_pow(number, power): + if power < 1: + raise NegativePowerException result = number while power != 1: result *= result power = power // 2 - return result + return result \ No newline at end of file From 46a440c7f0b7e7928bf68809be56d234ceb05dab Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 15:02:55 +0300 Subject: [PATCH 09/13] Value-based tests --- test/test_fast_pow.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/test_fast_pow.py b/test/test_fast_pow.py index 406164e..647a2f7 100644 --- a/test/test_fast_pow.py +++ b/test/test_fast_pow.py @@ -1,4 +1,4 @@ -from src.fast_pow import fast_pow +from src.fast_pow import fast_pow, NegativePowerException import pytest import random @@ -13,8 +13,14 @@ def test_negative(): @pytest.fixture def random_n(): - return random.randint(1, 100) + return random.randint(1, 10) -def test_negative_power(random_n): - assert fast_pow(10, -1) == 10 ** (-1) \ No newline at end of file +def test_value_based(random_n): + assert fast_pow(5, random_n) == 5 ** random_n + + +@pytest.mark.parametrize(["number", "power"], [(0, 0), (-1, -1), (0, -1), (1, -1), (1, 0)]) +@pytest.mark.xfail(raises=NegativePowerException) +def test_negative_power(number, power): + assert fast_pow(number, power) From e8ae49fc0b41d2329b269baba9a922d33c4655a9 Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 15:13:58 +0300 Subject: [PATCH 10/13] Testing luhn.py --- test/test_luhn.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/test/test_luhn.py b/test/test_luhn.py index 1c021a2..e34a18f 100644 --- a/test/test_luhn.py +++ b/test/test_luhn.py @@ -1,9 +1,19 @@ -from src.luhn import luhnСheck +from src.luhn import luhn_check +import pytest +import random def test_good(): - assert luhnСheck("8571 2612 1234 5467") + assert luhn_check("8571 2612 1234 5467") def test_bad(): - assert not luhnСheck("4561 2612 1234 5463") \ No newline at end of file + assert not luhn_check("4561 2612 1234 5463") + + +@pytest.mark.parametrize( + ["number", "expected"], + [("8531462222641434", False), ("9245217030", False), ("2530643671", False), ("2036532071", False)] +) +def test_negative_power(number, expected): + assert luhn_check(number) == expected \ No newline at end of file From 6241a09df458e0b5f3b3bb647bb8e03acb651709 Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 15:17:12 +0300 Subject: [PATCH 11/13] Reanme error --- src/luhn.py | 2 +- test/test_fast_pow.py | 14 +++++++++----- test/test_luhn.py | 13 +++++++++---- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/luhn.py b/src/luhn.py index 5fa036f..733fb04 100644 --- a/src/luhn.py +++ b/src/luhn.py @@ -22,4 +22,4 @@ def luhn_check(card_namber): if luhn_check(input_number): print("correct") else: - print("incorrect") \ No newline at end of file + print("incorrect") diff --git a/test/test_fast_pow.py b/test/test_fast_pow.py index 647a2f7..dfd1ab3 100644 --- a/test/test_fast_pow.py +++ b/test/test_fast_pow.py @@ -1,7 +1,9 @@ -from src.fast_pow import fast_pow, NegativePowerException -import pytest import random +import pytest + +from src.fast_pow import NegativePowerError, fast_pow + def test_two_power_two(): assert fast_pow(2, 2) == 4 @@ -17,10 +19,12 @@ def random_n(): def test_value_based(random_n): - assert fast_pow(5, random_n) == 5 ** random_n + assert fast_pow(5, random_n) == 5**random_n -@pytest.mark.parametrize(["number", "power"], [(0, 0), (-1, -1), (0, -1), (1, -1), (1, 0)]) -@pytest.mark.xfail(raises=NegativePowerException) +@pytest.mark.parametrize( + ["number", "power"], [(0, 0), (-1, -1), (0, -1), (1, -1), (1, 0)] +) +@pytest.mark.xfail(raises=NegativePowerError) def test_negative_power(number, power): assert fast_pow(number, power) diff --git a/test/test_luhn.py b/test/test_luhn.py index e34a18f..ea74222 100644 --- a/test/test_luhn.py +++ b/test/test_luhn.py @@ -1,6 +1,6 @@ -from src.luhn import luhn_check import pytest -import random + +from src.luhn import luhn_check def test_good(): @@ -13,7 +13,12 @@ def test_bad(): @pytest.mark.parametrize( ["number", "expected"], - [("8531462222641434", False), ("9245217030", False), ("2530643671", False), ("2036532071", False)] + [ + ("8531462222641434", False), + ("9245217030", False), + ("2530643671", False), + ("2036532071", False), + ], ) def test_negative_power(number, expected): - assert luhn_check(number) == expected \ No newline at end of file + assert luhn_check(number) == expected From cca7d8c6e1dd3881006ff148d1d8adedc784cf4c Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 15:18:22 +0300 Subject: [PATCH 12/13] Bug fix in error class --- src/fast_pow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fast_pow.py b/src/fast_pow.py index 7e14f47..d6208de 100644 --- a/src/fast_pow.py +++ b/src/fast_pow.py @@ -1,12 +1,12 @@ -class NegativePowerException(Exception): +class NegativePowerError(Exception): pass def fast_pow(number, power): if power < 1: - raise NegativePowerException + raise NegativePowerError result = number while power != 1: result *= result power = power // 2 - return result \ No newline at end of file + return result From 6d2f8e284e63cc290c8c6e6638878df317f8160a Mon Sep 17 00:00:00 2001 From: Max Ipatov Date: Thu, 23 Oct 2025 15:20:57 +0300 Subject: [PATCH 13/13] Remove test --- test/test_fast_pow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_fast_pow.py b/test/test_fast_pow.py index dfd1ab3..8b7f7b9 100644 --- a/test/test_fast_pow.py +++ b/test/test_fast_pow.py @@ -22,9 +22,9 @@ def test_value_based(random_n): assert fast_pow(5, random_n) == 5**random_n -@pytest.mark.parametrize( +"""@pytest.mark.parametrize( ["number", "power"], [(0, 0), (-1, -1), (0, -1), (1, -1), (1, 0)] ) @pytest.mark.xfail(raises=NegativePowerError) def test_negative_power(number, power): - assert fast_pow(number, power) + assert fast_pow(number, power)"""