-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
1,242 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
!coverage.py: This is a private format, don't read it directly!{"arcs":{"/home/vpayno/git_vpayno/exercism-workspace/python/luhn/test/__init__.py":[[0,0],[0,-1]],"/home/vpayno/git_vpayno/exercism-workspace/python/luhn/luhn.py":[[0,1],[1,3],[3,4],[4,7],[7,7],[7,8],[8,10],[10,27],[27,-7],[7,55],[55,55],[55,56],[56,58],[58,61],[61,95],[95,137],[137,-55],[55,-1],[58,59],[59,-58],[61,87],[87,88],[88,89],[10,25],[25,-10],[89,93],[95,129],[27,37],[37,50],[50,52],[37,48],[48,-37],[52,52],[52,-27],[129,131],[137,175],[175,177],[177,178],[178,180],[180,182],[182,183],[183,185],[185,186],[186,188],[188,182],[182,191],[191,193],[193,195],[195,198],[198,-137],[131,133],[133,135],[135,-95],[93,-61],[195,196],[196,198],[87,91],[91,-61],[185,188],[89,91]]}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
<?xml version="1.0" ?> | ||
<coverage branch-rate="0.9" branches-covered="9" branches-valid="10" complexity="0" line-rate="0.9737" lines-covered="37" lines-valid="38" timestamp="1713414237576" version="4.5.4"> | ||
<!-- Generated by coverage.py: https://coverage.readthedocs.io --> | ||
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd --> | ||
<sources> | ||
<source>/home/vpayno/git_vpayno/exercism-workspace/python/luhn</source> | ||
</sources> | ||
<packages> | ||
<package branch-rate="0.9" complexity="0" line-rate="0.9737" name="."> | ||
<classes> | ||
<class branch-rate="0.9" complexity="0" filename="luhn.py" line-rate="0.9737" name="luhn.py"> | ||
<methods/> | ||
<lines> | ||
<line hits="0" number="0"/> | ||
<line hits="1" number="3"/> | ||
<line hits="1" number="4"/> | ||
<line hits="1" number="7"/> | ||
<line hits="1" number="10"/> | ||
<line hits="1" number="25"/> | ||
<line hits="1" number="27"/> | ||
<line hits="1" number="37"/> | ||
<line hits="1" number="48"/> | ||
<line hits="1" number="50"/> | ||
<line branch="true" condition-coverage="50% (1/2)" hits="1" missing-branches="exit" number="52"/> | ||
<line hits="1" number="55"/> | ||
<line hits="1" number="58"/> | ||
<line hits="1" number="59"/> | ||
<line hits="1" number="61"/> | ||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="86"/> | ||
<line hits="1" number="91"/> | ||
<line hits="1" number="93"/> | ||
<line hits="1" number="95"/> | ||
<line hits="1" number="129"/> | ||
<line hits="1" number="131"/> | ||
<line hits="1" number="133"/> | ||
<line hits="1" number="135"/> | ||
<line hits="1" number="137"/> | ||
<line hits="1" number="175"/> | ||
<line hits="1" number="177"/> | ||
<line hits="1" number="178"/> | ||
<line hits="1" number="180"/> | ||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="182"/> | ||
<line hits="1" number="183"/> | ||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="185"/> | ||
<line hits="1" number="186"/> | ||
<line hits="1" number="188"/> | ||
<line hits="1" number="191"/> | ||
<line hits="1" number="193"/> | ||
<line branch="true" condition-coverage="100% (2/2)" hits="1" number="195"/> | ||
<line hits="1" number="196"/> | ||
<line hits="1" number="198"/> | ||
</lines> | ||
</class> | ||
</classes> | ||
</package> | ||
<package branch-rate="1" complexity="0" line-rate="1" name="test"> | ||
<classes> | ||
<class branch-rate="1" complexity="0" filename="test/__init__.py" line-rate="1" name="__init__.py"> | ||
<methods/> | ||
<lines/> | ||
</class> | ||
</classes> | ||
</package> | ||
</packages> | ||
</coverage> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[run] | ||
omit = __init__.py, *_test.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
../.pylintrc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,198 @@ | ||
class Luhn: | ||
def __init__(self, card_num): | ||
pass | ||
"""Python Luhn Exercism""" | ||
|
||
def valid(self): | ||
pass | ||
import re | ||
from itertools import chain | ||
|
||
|
||
class String(str): | ||
"""Extending String class/type""" | ||
|
||
def match(self, regex: str) -> bool: | ||
"""Does the String match a regex? | ||
>>> s: String = String("1234 5678") | ||
>>> s | ||
'1234 5678' | ||
>>> s.match(r"^[0-9 ]+$") | ||
True | ||
>>> s.match(r"^[a-z ]+$") | ||
False | ||
:param regex: rstr | ||
:return: bool | ||
""" | ||
|
||
return bool(re.match(regex, self)) | ||
|
||
def extract_digits(self) -> list[int]: | ||
"""Extracts digits from a code. | ||
>>> s: String = String("1234 5678") | ||
>>> s.extract_digits() | ||
[1, 2, 3, 4, 5, 6, 7, 8] | ||
:return: list[int] | ||
""" | ||
|
||
def is_digit(rune: str) -> bool: | ||
"""Test function for filter. | ||
>>> is_digit("a") | ||
False | ||
>>> is_digit("7") | ||
True | ||
:return: True if rune is a digit | ||
""" | ||
|
||
return rune.isdigit() | ||
|
||
filtered = filter(is_digit, list(self)) | ||
|
||
return [int(r) for r in filtered] | ||
|
||
|
||
class Luhn: # pylint: disable=too-few-public-methods | ||
"""Determine if a number is a valid Luhn number.""" | ||
|
||
def __init__(self, card_num: str) -> None: | ||
self.code: String = String(card_num.strip()) | ||
|
||
def valid(self) -> bool: | ||
"""Is this a valid Luhn number? | ||
>>> l: Luhn = Luhn("055 444 285") | ||
>>> l.valid() | ||
True | ||
>>> l: Luhn = Luhn("055 444 286") | ||
>>> l.valid() | ||
False | ||
>>> l: Luhn = Luhn("234 567 891 234") | ||
>>> l.valid() | ||
True | ||
>>> n: String = String("234 567 891 234") | ||
>>> n == "0" | ||
False | ||
>>> len(n) == 0 | ||
False | ||
>>> n == "0" | ||
False | ||
>>> n.match(r"^([0-9 ])+$") | ||
True | ||
:return: bool | ||
""" | ||
|
||
if ( | ||
self.code == "0" | ||
or len(self.code) == 0 | ||
or not self.code.match(r"^([0-9 ])+$") | ||
): | ||
return False | ||
|
||
return self.__is_luhn_number() | ||
|
||
def __is_luhn_number(self) -> bool: | ||
"""Is the code a valid luhn number? | ||
>>> l: Luhn = Luhn("055 444 285") | ||
>>> l._Luhn__is_luhn_number() | ||
True | ||
>>> l: Luhn = Luhn("055 444 286") | ||
>>> l._Luhn__is_luhn_number() | ||
False | ||
>>> digits: list[int] = l.code.extract_digits() | ||
>>> digits | ||
[0, 5, 5, 4, 4, 4, 2, 8, 6] | ||
>>> numbers: list[int] = l._Luhn__step_one_and_two(digits) | ||
>>> numbers | ||
[1, 5, 8, 4, 8, 2, 7, 6, 0] | ||
>>> digit_sum: int = sum(numbers) | ||
>>> digit_sum | ||
41 | ||
>>> l: Luhn = Luhn("234 567 891 234") | ||
>>> l._Luhn__is_luhn_number() | ||
True | ||
>>> digits: list[int] = l.code.extract_digits() | ||
>>> digits | ||
[2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4] | ||
>>> numbers: list[int] = l._Luhn__step_one_and_two(digits) | ||
>>> numbers | ||
[4, 3, 8, 5, 3, 7, 7, 9, 2, 2, 6, 4] | ||
>>> digit_sum: int = sum(numbers) | ||
>>> digit_sum | ||
60 | ||
:return: bool | ||
""" | ||
|
||
digits: list[int] = self.code.extract_digits() | ||
|
||
numbers: list[int] = self.__step_one_and_two(digits) | ||
|
||
digit_sum: int = sum(numbers) | ||
|
||
return (digit_sum % 10) == 0 | ||
|
||
def __step_one_and_two(self, digits: list[int]) -> list[int]: | ||
"""Performs steps one and two of the luhn number verification algorithm. | ||
>>> l: Luhn = Luhn("0123456789") | ||
>>> d: list[int] = l.code.extract_digits() | ||
>>> d | ||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] | ||
>>> l._Luhn__step_one_and_two(d) | ||
[0, 1, 4, 3, 8, 5, 3, 7, 7, 9] | ||
>>> l: Luhn = Luhn("59") | ||
>>> d: list[int] = l.code.extract_digits() | ||
>>> d | ||
[5, 9] | ||
>>> s = l._Luhn__step_one_and_two(d) | ||
>>> s | ||
[1, 9] | ||
>>> sum(s) | ||
10 | ||
>>> l: Luhn = Luhn(" 0") | ||
>>> d: list[int] = l.code.extract_digits() | ||
>>> d | ||
[0] | ||
>>> l._Luhn__step_one_and_two(d) | ||
[0] | ||
>>> sum(l._Luhn__step_one_and_two(d)) | ||
0 | ||
>>> l: Luhn = Luhn("234 567 891 234") | ||
>>> d: list[int] = l.code.extract_digits() | ||
>>> d | ||
[2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4] | ||
>>> l._Luhn__step_one_and_two(d) | ||
[4, 3, 8, 5, 3, 7, 7, 9, 2, 2, 6, 4] | ||
>>> sum(l._Luhn__step_one_and_two(d)) | ||
57 | ||
:return: list[int] | ||
""" | ||
|
||
digits.reverse() | ||
|
||
even: list[int] = digits[0::2] | ||
odd: list[int] = digits[1::2] | ||
|
||
new_odd: list[int] = [] | ||
|
||
for n in odd: | ||
n *= 2 | ||
|
||
if n > 9: | ||
n -= 9 | ||
|
||
new_odd.append(n) | ||
|
||
# flatten the list of tuples | ||
new_list: list[int] = list(chain.from_iterable(zip(even, new_odd))) | ||
|
||
new_list.reverse() | ||
|
||
if len(even) != len(odd): | ||
new_list.append(even[-1]) | ||
|
||
return new_list |
Oops, something went wrong.