From c8c07a4b3a903cf19b60a455f81e5e1ac3506194 Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Sun, 24 Oct 2021 00:25:49 +0530 Subject: [PATCH 01/14] Add credit card validator --- strings/cc_validator.py | 64 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 strings/cc_validator.py diff --git a/strings/cc_validator.py b/strings/cc_validator.py new file mode 100644 index 000000000000..934e4a3b8e46 --- /dev/null +++ b/strings/cc_validator.py @@ -0,0 +1,64 @@ +def validate_initial_digits(credit_card_number: str) -> bool: + first_digit=int(credit_card_number[0]) + if first_digit==4 or first_digit==5 or first_digit==6: + return True + elif first_digit==3: + second_digit=int(credit_card_number[1]) + if second_digit==7 or second_digit==4 or second_digit==5: + return True + else: + return False + return False + +def luhn_validation(credit_card_number: str) -> bool: + cc_number = credit_card_number + total=0 + half_len=len(cc_number)-2 + for i in range(half_len,-1,-2): + # double the value of every second digit + digit = int(cc_number[i]) + digit *= 2 + # If doubling of a number results in a two digit number i.e greater than 9(e.g., 6 × 2 = 12), + # then add the digits of the product (e.g., 12: 1 + 2 = 3, 15: 1 + 5 = 6), + # to get a single digit number. + if digit>9: + digit%=10 + digit+=1 + cc_number=cc_number[:i]+str(digit)+cc_number[i+1:] + total+=digit + + # Sum up the remaining digits + for i in range(len(cc_number)-1,-1,-2): + total+=int(cc_number[i]) + + if total%10==0: + return True + + return False + + +def validate_credit_card_number(number: str): + ''' + Function to validate the given credit card number + ''' + credit_card_number = str(number) + if credit_card_number.isdigit(): + credit_card_number_length = len(credit_card_number) + if credit_card_number_length >= 13 and credit_card_number_length <= 16: + if validate_initial_digits(credit_card_number): + if luhn_validation(credit_card_number): + print(f"Given number({number}) is Valid") + else: + print(f"Invalid number({number}) given: Invalid Number") + else: + print(f"Invalid number({number}) given: Check starting number") + else: + print(f"Invalid number({number}) given: Check number length") + else: + print(f"Invalid number({number}) given: Contains alphabets or special charecters") + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From ed940d5d7e74a8acc524b8249ead518295df1394 Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Sun, 24 Oct 2021 22:01:41 +0530 Subject: [PATCH 02/14]  --- strings/cc_validator.py | 44 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/strings/cc_validator.py b/strings/cc_validator.py index 934e4a3b8e46..9895b47bb6b3 100644 --- a/strings/cc_validator.py +++ b/strings/cc_validator.py @@ -1,46 +1,48 @@ def validate_initial_digits(credit_card_number: str) -> bool: - first_digit=int(credit_card_number[0]) - if first_digit==4 or first_digit==5 or first_digit==6: + first_digit = int(credit_card_number[0]) + if first_digit == 4 or first_digit == 5 or first_digit == 6: return True - elif first_digit==3: - second_digit=int(credit_card_number[1]) - if second_digit==7 or second_digit==4 or second_digit==5: + elif first_digit == 3: + second_digit = int(credit_card_number[1]) + if second_digit == 7 or second_digit == 4 or second_digit == 5: return True else: return False return False + def luhn_validation(credit_card_number: str) -> bool: cc_number = credit_card_number - total=0 - half_len=len(cc_number)-2 - for i in range(half_len,-1,-2): + total = 0 + half_len = len(cc_number) - 2 + for i in range(half_len, -1, -2): # double the value of every second digit digit = int(cc_number[i]) digit *= 2 - # If doubling of a number results in a two digit number i.e greater than 9(e.g., 6 × 2 = 12), + # If doubling of a number results in a two digit number + # i.e greater than 9(e.g., 6 × 2 = 12), # then add the digits of the product (e.g., 12: 1 + 2 = 3, 15: 1 + 5 = 6), # to get a single digit number. - if digit>9: - digit%=10 - digit+=1 - cc_number=cc_number[:i]+str(digit)+cc_number[i+1:] - total+=digit + if digit > 9: + digit %= 10 + digit += 1 + cc_number = cc_number[:i] + str(digit) + cc_number[i + 1 :] + total += digit # Sum up the remaining digits - for i in range(len(cc_number)-1,-1,-2): - total+=int(cc_number[i]) + for i in range(len(cc_number) - 1, -1, -2): + total += int(cc_number[i]) - if total%10==0: + if total % 10 == 0: return True return False def validate_credit_card_number(number: str): - ''' + """ Function to validate the given credit card number - ''' + """ credit_card_number = str(number) if credit_card_number.isdigit(): credit_card_number_length = len(credit_card_number) @@ -55,7 +57,9 @@ def validate_credit_card_number(number: str): else: print(f"Invalid number({number}) given: Check number length") else: - print(f"Invalid number({number}) given: Contains alphabets or special charecters") + print( + f"Invalid number({number}) given: Contains alphabets or special characters" + ) if __name__ == "__main__": From d71d1c44d1f764b09562df393e1c5bb47f2ddecf Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Sun, 24 Oct 2021 22:32:26 +0530 Subject: [PATCH 03/14] Add return type hint --- strings/cc_validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/cc_validator.py b/strings/cc_validator.py index 9895b47bb6b3..ec9c58c3c687 100644 --- a/strings/cc_validator.py +++ b/strings/cc_validator.py @@ -39,7 +39,7 @@ def luhn_validation(credit_card_number: str) -> bool: return False -def validate_credit_card_number(number: str): +def validate_credit_card_number(number: str) -> None: """ Function to validate the given credit card number """ From 72c4d5441160694eea7111be5362ca4a64e95e9e Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Mon, 25 Oct 2021 00:09:54 +0530 Subject: [PATCH 04/14] Add test cases for validator function --- strings/cc_validator.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/strings/cc_validator.py b/strings/cc_validator.py index ec9c58c3c687..64603f9d52bd 100644 --- a/strings/cc_validator.py +++ b/strings/cc_validator.py @@ -41,7 +41,19 @@ def luhn_validation(credit_card_number: str) -> bool: def validate_credit_card_number(number: str) -> None: """ - Function to validate the given credit card number + Function to validate the given credit card number. + >>> validate_credit_card_number('4111111111111111') + Given number(4111111111111111) is Valid + >>> validate_credit_card_number('helloworld$') + Invalid number(helloworld$) given: Contains alphabets or special characters + >>> validate_credit_card_number('32323') + Invalid number(32323) given: Check number length + >>> validate_credit_card_number('32323323233232332323') + Invalid number(32323323233232332323) given: Check number length + >>> validate_credit_card_number('36111111111111') + Invalid number(36111111111111) given: Check starting number + >>> validate_credit_card_number('41111111111111') + Invalid number(41111111111111) given: Invalid Number """ credit_card_number = str(number) if credit_card_number.isdigit(): From 5cc789c2720bb09886d8f876634168d825be04a5 Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Mon, 25 Oct 2021 00:21:27 +0530 Subject: [PATCH 05/14] Add test cases --- strings/cc_validator.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/strings/cc_validator.py b/strings/cc_validator.py index 64603f9d52bd..e0eaa9658951 100644 --- a/strings/cc_validator.py +++ b/strings/cc_validator.py @@ -1,4 +1,27 @@ def validate_initial_digits(credit_card_number: str) -> bool: + """ + Function to validate initial digits of a given credit card number. + >>> validate_initial_digits('4111111111111111') + True + >>> validate_initial_digits('32323') + False + >>> validate_initial_digits('36111111111111') + False + >>> validate_initial_digits('41111111111111') + True + >>> validate_initial_digits('37') + True + >>> validate_initial_digits('34') + True + >>> validate_initial_digits('35') + True + >>> validate_initial_digits('412345') + True + >>> validate_initial_digits('523456') + True + >>> validate_initial_digits('634567') + True + """ first_digit = int(credit_card_number[0]) if first_digit == 4 or first_digit == 5 or first_digit == 6: return True @@ -12,6 +35,15 @@ def validate_initial_digits(credit_card_number: str) -> bool: def luhn_validation(credit_card_number: str) -> bool: + """ + Function to luhn algorithm validation for a given credit card number. + >>> luhn_validation('4111111111111111') + True + >>> luhn_validation('36111111111111') + True + >>> luhn_validation('41111111111111') + False + """ cc_number = credit_card_number total = 0 half_len = len(cc_number) - 2 From 0ac42d7c9c9fc74064e0e6d02b7da282949bddf9 Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Mon, 25 Oct 2021 22:21:06 +0530 Subject: [PATCH 06/14] Feature: Rename file --- strings/{cc_validator.py => credit_card_validator.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename strings/{cc_validator.py => credit_card_validator.py} (100%) diff --git a/strings/cc_validator.py b/strings/credit_card_validator.py similarity index 100% rename from strings/cc_validator.py rename to strings/credit_card_validator.py From 007ff6c9b9181053e178b0ca94e6f7665ef0556b Mon Sep 17 00:00:00 2001 From: "@im_8055" <38890773+Bhargavishnu@users.noreply.github.com> Date: Mon, 25 Oct 2021 22:21:36 +0530 Subject: [PATCH 07/14] Update strings/cc_validator.py Co-authored-by: Christian Clauss --- strings/cc_validator.py | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/strings/cc_validator.py b/strings/cc_validator.py index e0eaa9658951..d6f52dc20df3 100644 --- a/strings/cc_validator.py +++ b/strings/cc_validator.py @@ -1,25 +1,11 @@ def validate_initial_digits(credit_card_number: str) -> bool: """ Function to validate initial digits of a given credit card number. - >>> validate_initial_digits('4111111111111111') + >>> valid = "4111111111111111 41111111111111 34 35 37 412345 523456 634567" + >>> all(validate_initial_digits(cc) for cc in valid.split()) True - >>> validate_initial_digits('32323') - False - >>> validate_initial_digits('36111111111111') - False - >>> validate_initial_digits('41111111111111') - True - >>> validate_initial_digits('37') - True - >>> validate_initial_digits('34') - True - >>> validate_initial_digits('35') - True - >>> validate_initial_digits('412345') - True - >>> validate_initial_digits('523456') - True - >>> validate_initial_digits('634567') + >>> invalid = "32323 36111111111111" + >>> all(validate_initial_digits(cc) is False for cc in invalid.split()) True """ first_digit = int(credit_card_number[0]) From 2832a3374fc99dcc7f65135203faf4a13830567e Mon Sep 17 00:00:00 2001 From: "@im_8055" <38890773+Bhargavishnu@users.noreply.github.com> Date: Mon, 25 Oct 2021 22:28:32 +0530 Subject: [PATCH 08/14] Update strings/cc_validator.py Co-authored-by: Christian Clauss --- strings/cc_validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/cc_validator.py b/strings/cc_validator.py index d6f52dc20df3..3f93e41ef27d 100644 --- a/strings/cc_validator.py +++ b/strings/cc_validator.py @@ -9,7 +9,7 @@ def validate_initial_digits(credit_card_number: str) -> bool: True """ first_digit = int(credit_card_number[0]) - if first_digit == 4 or first_digit == 5 or first_digit == 6: + if first_digit in (4, 5, 6): return True elif first_digit == 3: second_digit = int(credit_card_number[1]) From c8b141ad4018db94a6f718e39f955a023df17a52 Mon Sep 17 00:00:00 2001 From: "@im_8055" <38890773+Bhargavishnu@users.noreply.github.com> Date: Mon, 25 Oct 2021 22:28:42 +0530 Subject: [PATCH 09/14] Update strings/cc_validator.py Co-authored-by: Christian Clauss --- strings/cc_validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/cc_validator.py b/strings/cc_validator.py index 3f93e41ef27d..afbe27b73508 100644 --- a/strings/cc_validator.py +++ b/strings/cc_validator.py @@ -13,7 +13,7 @@ def validate_initial_digits(credit_card_number: str) -> bool: return True elif first_digit == 3: second_digit = int(credit_card_number[1]) - if second_digit == 7 or second_digit == 4 or second_digit == 5: + return second_digit in (4, 5, 7) return True else: return False From b81054c2c3b7f9e2fdf97e4a158c78c7965fd6bb Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Mon, 25 Oct 2021 22:34:35 +0530 Subject: [PATCH 10/14] Review: Fix redundant checks --- strings/credit_card_validator.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/strings/credit_card_validator.py b/strings/credit_card_validator.py index afbe27b73508..db7e019c6525 100644 --- a/strings/credit_card_validator.py +++ b/strings/credit_card_validator.py @@ -14,9 +14,7 @@ def validate_initial_digits(credit_card_number: str) -> bool: elif first_digit == 3: second_digit = int(credit_card_number[1]) return second_digit in (4, 5, 7) - return True - else: - return False + return False @@ -51,10 +49,7 @@ def luhn_validation(credit_card_number: str) -> bool: for i in range(len(cc_number) - 1, -1, -2): total += int(cc_number[i]) - if total % 10 == 0: - return True - - return False + return total % 10 == 0 def validate_credit_card_number(number: str) -> None: From 981282b6a5acee573dfc02c85220169727c9bc0a Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Mon, 25 Oct 2021 22:47:05 +0530 Subject: [PATCH 11/14] Review: Refactor --- strings/credit_card_validator.py | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/strings/credit_card_validator.py b/strings/credit_card_validator.py index db7e019c6525..818c895c2260 100644 --- a/strings/credit_card_validator.py +++ b/strings/credit_card_validator.py @@ -52,7 +52,7 @@ def luhn_validation(credit_card_number: str) -> bool: return total % 10 == 0 -def validate_credit_card_number(number: str) -> None: +def validate_credit_card_number(number: str) -> bool: """ Function to validate the given credit card number. >>> validate_credit_card_number('4111111111111111') @@ -68,23 +68,27 @@ def validate_credit_card_number(number: str) -> None: >>> validate_credit_card_number('41111111111111') Invalid number(41111111111111) given: Invalid Number """ + error_message = f"Invalid number({number}) given: " credit_card_number = str(number) - if credit_card_number.isdigit(): - credit_card_number_length = len(credit_card_number) - if credit_card_number_length >= 13 and credit_card_number_length <= 16: - if validate_initial_digits(credit_card_number): - if luhn_validation(credit_card_number): - print(f"Given number({number}) is Valid") - else: - print(f"Invalid number({number}) given: Invalid Number") - else: - print(f"Invalid number({number}) given: Check starting number") - else: - print(f"Invalid number({number}) given: Check number length") - else: - print( - f"Invalid number({number}) given: Contains alphabets or special characters" - ) + if not credit_card_number.isdigit(): + print(error_message + "Contains alphabets or special characters") + return False + + credit_card_number_length = len(credit_card_number) + if credit_card_number_length < 13 and credit_card_number_length > 16: + print(error_message + "Check number length") + return False + + if not validate_initial_digits(credit_card_number): + print(error_message + "Check starting number") + return False + + if not luhn_validation(credit_card_number): + print(error_message + "Invalid Number") + return False + + print(f"Given number({number}) is Valid") + return True if __name__ == "__main__": From a09dca480dc1892ef6ccda988f0b1121d70329b9 Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Mon, 25 Oct 2021 22:57:57 +0530 Subject: [PATCH 12/14] Fix: Update test cases --- strings/credit_card_validator.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/strings/credit_card_validator.py b/strings/credit_card_validator.py index 818c895c2260..5c193dc83b73 100644 --- a/strings/credit_card_validator.py +++ b/strings/credit_card_validator.py @@ -5,7 +5,7 @@ def validate_initial_digits(credit_card_number: str) -> bool: >>> all(validate_initial_digits(cc) for cc in valid.split()) True >>> invalid = "32323 36111111111111" - >>> all(validate_initial_digits(cc) is False for cc in invalid.split()) + >>> all(validate_initial_digits(cc) is False for cc in invalid.split()) True """ first_digit = int(credit_card_number[0]) @@ -57,16 +57,22 @@ def validate_credit_card_number(number: str) -> bool: Function to validate the given credit card number. >>> validate_credit_card_number('4111111111111111') Given number(4111111111111111) is Valid + True >>> validate_credit_card_number('helloworld$') Invalid number(helloworld$) given: Contains alphabets or special characters + False >>> validate_credit_card_number('32323') Invalid number(32323) given: Check number length + False >>> validate_credit_card_number('32323323233232332323') Invalid number(32323323233232332323) given: Check number length + False >>> validate_credit_card_number('36111111111111') Invalid number(36111111111111) given: Check starting number + False >>> validate_credit_card_number('41111111111111') Invalid number(41111111111111) given: Invalid Number + False """ error_message = f"Invalid number({number}) given: " credit_card_number = str(number) @@ -75,7 +81,7 @@ def validate_credit_card_number(number: str) -> bool: return False credit_card_number_length = len(credit_card_number) - if credit_card_number_length < 13 and credit_card_number_length > 16: + if not (credit_card_number_length >= 13 and credit_card_number_length <= 16): print(error_message + "Check number length") return False @@ -86,7 +92,7 @@ def validate_credit_card_number(number: str) -> bool: if not luhn_validation(credit_card_number): print(error_message + "Invalid Number") return False - + print(f"Given number({number}) is Valid") return True From ac83913afcbd10ce7d94dc611d717b632c05434e Mon Sep 17 00:00:00 2001 From: Bhargav Vishnu Date: Mon, 25 Oct 2021 23:10:20 +0530 Subject: [PATCH 13/14] Refactor --- strings/credit_card_validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/strings/credit_card_validator.py b/strings/credit_card_validator.py index 5c193dc83b73..1e0d5e2fada0 100644 --- a/strings/credit_card_validator.py +++ b/strings/credit_card_validator.py @@ -81,7 +81,7 @@ def validate_credit_card_number(number: str) -> bool: return False credit_card_number_length = len(credit_card_number) - if not (credit_card_number_length >= 13 and credit_card_number_length <= 16): + if not (13 <= len(credit_card_number) <= 16): print(error_message + "Check number length") return False From 6985f7b0cdab07cc2c43c1719de347062a5de87e Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 26 Oct 2021 07:16:24 +0200 Subject: [PATCH 14/14] Update credit_card_validator.py --- strings/credit_card_validator.py | 50 +++++++++++++++++--------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/strings/credit_card_validator.py b/strings/credit_card_validator.py index 1e0d5e2fada0..3b5a1aae6dc9 100644 --- a/strings/credit_card_validator.py +++ b/strings/credit_card_validator.py @@ -1,3 +1,10 @@ +""" +Functions for testing the validity of credit card numbers. + +https://en.wikipedia.org/wiki/Luhn_algorithm +""" + + def validate_initial_digits(credit_card_number: str) -> bool: """ Function to validate initial digits of a given credit card number. @@ -8,14 +15,9 @@ def validate_initial_digits(credit_card_number: str) -> bool: >>> all(validate_initial_digits(cc) is False for cc in invalid.split()) True """ - first_digit = int(credit_card_number[0]) - if first_digit in (4, 5, 6): - return True - elif first_digit == 3: - second_digit = int(credit_card_number[1]) - return second_digit in (4, 5, 7) - - return False + if len(credit_card_number) < 2: + return False + return credit_card_number[0] in "456" or credit_card_number[1] in "457" def luhn_validation(credit_card_number: str) -> bool: @@ -52,48 +54,46 @@ def luhn_validation(credit_card_number: str) -> bool: return total % 10 == 0 -def validate_credit_card_number(number: str) -> bool: +def validate_credit_card_number(credit_card_number: str) -> bool: """ Function to validate the given credit card number. >>> validate_credit_card_number('4111111111111111') - Given number(4111111111111111) is Valid + 4111111111111111 is a valid credit card number. True >>> validate_credit_card_number('helloworld$') - Invalid number(helloworld$) given: Contains alphabets or special characters + helloworld$ is an invalid credit card number because it has nonnumerical characters. False >>> validate_credit_card_number('32323') - Invalid number(32323) given: Check number length + 32323 is an invalid credit card number because of its length. False >>> validate_credit_card_number('32323323233232332323') - Invalid number(32323323233232332323) given: Check number length + 32323323233232332323 is an invalid credit card number because of its length. False >>> validate_credit_card_number('36111111111111') - Invalid number(36111111111111) given: Check starting number + 36111111111111 is an invalid credit card number because of its first two digits. False >>> validate_credit_card_number('41111111111111') - Invalid number(41111111111111) given: Invalid Number + 41111111111111 is an invalid credit card number because it fails the Lhun check. False """ - error_message = f"Invalid number({number}) given: " - credit_card_number = str(number) + error_message = f"{credit_card_number} is an invalid credit card number because" if not credit_card_number.isdigit(): - print(error_message + "Contains alphabets or special characters") + print(f"{error_message} it has nonnumerical characters.") return False - credit_card_number_length = len(credit_card_number) - if not (13 <= len(credit_card_number) <= 16): - print(error_message + "Check number length") + if not 13 <= len(credit_card_number) <= 16: + print(f"{error_message} of its length.") return False if not validate_initial_digits(credit_card_number): - print(error_message + "Check starting number") + print(f"{error_message} of its first two digits.") return False if not luhn_validation(credit_card_number): - print(error_message + "Invalid Number") + print(f"{error_message} it fails the Lhun check.") return False - print(f"Given number({number}) is Valid") + print(f"{credit_card_number} is a valid credit card number.") return True @@ -101,3 +101,5 @@ def validate_credit_card_number(number: str) -> bool: import doctest doctest.testmod() + validate_credit_card_number("4111111111111111") + validate_credit_card_number("32323")