DEV Community

Kevin Mungai
Kevin Mungai

Posted on • Updated on

Custom Card Number Verification System Challenge

caveat

This programming challenge is found at

http://neo.cellulant.com:3000/sw_internship.html

or

Google Web Cache

and it will be presented as is:

the challenge

Cellulant has implemented a custom, card number verification system to ensure detection and blocking of fake cards. A scratch card consists of 4 sets of 5 digits (20 digits in total) e.g. [10006-12342-00081-99993] which are printed separated by a space or a dash.

Each set of 5 digits consists of two parts, the first 4 digits (the number) and a 5th digit (the checksum) The formula dictates that each of the sets must be validated as follows: The first 4 digits, e.g 1234, as decimal numbers be converted into an octal number (base 8) i.e. 23228. This octal number is then processed to generate the checksum, as follows:

  1. Add all digits to each other to get a new number “X”
  2. If “X” is more than 1 digit, repeat step (1) until you have a single digit. “Y”
  3. Append “Y” to the end of the original decimal number e.g. 1234 2

For Example:

Given the set “10006”

Convert 1000 to octal =>1750>
1+7+5+0=15
1+5=68
Valid number is 10006 so;
return (TRUE)

Given the set “99998”

Convert 9999 to octal => 23417
2+3+4+1+7=21
2+1=3
Valid number is 99993 so;
return (FALSE)

Create a Java, Java-Script or PHP script with the function that will receive a string value (scratch card number) and return boolean TRUE or FALSE, on whether the card number is valid, as shown below

You should use as many other functions as you deem necessary within your code.

really neat solution

This solution was contributed by Filipe Ramalho

  1. We get the modulo operation of first-four with 7
  2. Then we subtract the result from the checksum
  3. Next we get the modulo operation of the result above with 7
  4. Then we check if it is equal to 0

Better explained by Filipe Ramalho:

For 9999|8

  1. 9999 % 7 = 3 //First you modulo through 7
  2. 8 - 3 = 5 //Then you subtract the result from the checksum.
  3. 5 % 7 = 5 //Then you modulo that through 7.

The end result isn't 0, so the digit isn't valid

(defn is-valid?
  [number]
  (let [first-four (quot number 10)
        check-sum (rem number 10)]
    (-> first-four
        (mod 7)
        (- check-sum)
        (mod 7)
        (= 0))))


user> (is-valid-modified? 10006)
true
user> (is-valid-modified? 99993)
true
user> (is-valid-modified? 99998)
false
user> (is-valid-modified? 12342)
true
user> (is-valid-modified? 11697)
true


Enter fullscreen mode Exit fullscreen mode
(defn validate-scratch-card [card-number]
  (let [f (comp is-valid? #(Integer/parseInt %))]
    (->> (string/split card-number #"(-|\s)")
         (map f)
         (every? true?))))

user> (validate-scratch-card "10006 12342 00081 99998")
false
user> (validate-scratch-card "10006 12342 00081 99993")
true

Enter fullscreen mode Exit fullscreen mode

It is an elegant solution.

the old solution

Instead of using any of the proposed languages above, I shall use Clojure.

validate-scratch-card is a function which takes a string which may look like:

  1. "10006-12342-00081-99993"
  2. "10006 12342 00081 99993"

It would be nice to split the argument based on the presence of a space or a hyphen so that I could work on each substring:

;; has hyphen
user> (clojure.string/split "10006-12342-00081-99993" #("-|\s)")
;; ["10006" "12342" "00081" "99993"]

;; has spaces
user> (clojure.string/split "10006 12342 00081 99993" #"(-|\s)")
;; ["10006" "12342" "00081" "99993"]
Enter fullscreen mode Exit fullscreen mode

Next item on the agenda is to parse each individual substring to Integer via the Java method Integer/parseInt.

user> (Integer/parseInt "10006")
;; 10006
Enter fullscreen mode Exit fullscreen mode

map can be used to parse each individual substring

user> (map #(Integer/parseInt %) "10006 12342 00081 99993")
;; 10006
Enter fullscreen mode Exit fullscreen mode

Each Integer can be checked for validity based on the rules above.
Each Integer has 2 parts:

  1. first four digits (the number)
  2. the fifth digit (the checksum)
user> (quot 10006 10)
;; 1000   first four digits (the number)

user> (rem 10006 10)
;; 6   fifth digit (checksum)
Enter fullscreen mode Exit fullscreen mode

Next is to convert the first four digits to octal.

This will be achieved by creating a base converter

(defn- to-digits [number base]
  (loop [n number
         digits '()]
    (if (pos? n)
      (recur (quot n base)
             (conj digits (mod n base)))
      digits)))

(defn to-octal [number]
  "returns a list of octal numbers"
  (to-digits number 8))
Enter fullscreen mode Exit fullscreen mode
user> (to-octal 1000)
;; (1 7 5 0)
Enter fullscreen mode Exit fullscreen mode

Subsequent step is to sum up each of the generated numbers.

But before that, a function that takes two octal numbers and sums them up is needed.

(defn add-two-octal [x y]
  "add two octal numbers"
  (let [a (Integer/parseInt (str x) 8)
        b (Integer/parseInt (str y) 8)]
    (-> (+ a b)
        Integer/toOctalString
        Integer/parseInt)))
Enter fullscreen mode Exit fullscreen mode
user> (add-two-octal 3 6)
;; 11
Enter fullscreen mode Exit fullscreen mode

This add-two-octal function can then be used to sum up a list of octal numbers together as follows:

user> (reduce #(add-two-octal %1 %2)
              (to-octal 1000))
;; 15

user> (reduce #(add-two-octal %1 %2)
              '(1 7 5 0))
;; 15
Enter fullscreen mode Exit fullscreen mode

Although the addition works fine it doesn't satisfy the requirement that the result of the sum should be a single digit.
If a single digit is the result of the sum then the single digit should be returned else the summation should be done again.
Also there is need for a function to separate the new summation if it happens to be two digits.

(defn separate [number]
  "a utility function to separate digits"
  (loop [n number
         digits '()]
    (if (zero? n)
      digits
      (recur (quot n 10)
             (conj digits (rem n 10))))))


(defn sum-octal [number]
  (let [sum (reduce #(add-two-octal %1 %2)
                    (to-octal number))]
    (if (zero? (quot sum 10))
      sum
      (apply add-two-octal (separate sum)))))
Enter fullscreen mode Exit fullscreen mode
user> (sum-octal 1000)
;; 6
Enter fullscreen mode Exit fullscreen mode

Next a function that takes a number and checks for validity can be written as follows:

(defn is-valid? [number]
  (let [first-four (quot number 10)
        check-sum (rem number 10)
        verify (sum-octal first-four)]
    (= verify check-sum)))
Enter fullscreen mode Exit fullscreen mode
user> (is-valid? 10006)
;; true

user> (is-valid? 99998)
;; false
Enter fullscreen mode Exit fullscreen mode

The is-valid? function and the #(Integer/parseInt %) can be composed together

(comp is-valid? #(Integer/parseInt %))

user> ((comp is-valid? #(Integer/parseInt %)) "10006")
;; true
Enter fullscreen mode Exit fullscreen mode

Finally, the validate-scratch-card function will shape up as follows

(defn validate-scratch-card [card-number]
  (let [f (comp is-valid? #(Integer/parseInt %))]
    (->> (string/split card-number #"(-|\s)")
         (map f)
         (every? true?))))
Enter fullscreen mode Exit fullscreen mode
user> (validate-scratch-card "10006 12342 00081 99993")
;; true

user> (validate-scratch-card "10006 12342 00081 99998")
;; false
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
filipe_mdsr profile image
Filipe Ramalho • Edited

You can also use modulo. You calculate the iterative digit sum with number % 9. If 0 comes out the sum is 9.
sjsu.edu/faculty/watkins/Digitsum0...

I'm not familiar with Clojure so I can't give you a code example of Clojure. But here in pseudocode

boolean checkdigit(int digit,int checksum):     
    int sum = converttooctal(digit) % 9 //% is the modulo operator
    if(sum == 0):
        sum = 9
    return checksum - sum == 0

You can also consider 0 and 9 equal and spare the conversion from 0 to 9:

boolean checkdigit(int digit,int checksum): 
    int sum = converttooctal(digit) % 9 //% is the modulo operator
    return checksum - sum == 0 || checksum - sum == 9

Or you could modulo the difference through 9. If it's 0 the result is 0, if it's 9 the result is also 0.

boolean checkdigit(int digit,int checksum): 
    int sum = converttooctal(digit) % 9 //% is the modulo operator
    return (checksum - sum) % 9 == 0

So in one line it would look like this

boolean check = (checksum - converttooctal(digit) % 9 ) % 9 == 0
Collapse
 
kevinmungai profile image
Kevin Mungai

Thank you for contribution.

I have really tried my best to understand, but have failed.

Could you use examples?

Collapse
 
filipe_mdsr profile image
Filipe Ramalho • Edited

Actually I had an error. You shouldn't convert the digit. And instead modulo through 7.

For 9999|8

  1. 9999 % 7 = 3 //First you modulo through 7
  2. 8 - 3 = 5 //Then you subtract the result from the checksum.
  3. 5 % 7 = 5 //Then you modulo that through 7.

The end result isn't 0, so the digit isn't valid

For 1234|2

  1. 1234 % 7 = 2 //First you modulo through 7
  2. 2 - 2 = 0 //Then you subtract the result from the checksum.
  3. 0 % 7 = 0 //Then you modulo that through 7

The end result is 0, so the digit is valid

For 1169|7

1.1169 % 7 = 0 //If you modulo through 7, when the digit sum is 7 instead 0 comes out, so you can consider 0=7.

  1. 7 - 0 = 7
  2. 7 % 7 = 0 //If you modulo 0 or 7 through 7, the result is 0.

The end result is 0, so the digit is valid

Updated code

boolean checkdigit(int digit,int checksum):     
    int sum = digit % 7 //% is the modulo operator
    if(sum == 0):
        sum = 7
    return checksum - sum == 0

You can also consider 0 and 7 equal and spare the conversion from 0 to 7:

boolean checkdigit(int digit,int checksum): 
    int sum = digit % 7 //% is the modulo operator
    return checksum - sum == 0 || checksum - sum == 7

Or you could modulo the difference through 7. The difference must be either 7 or 0 and both have the same result for modulo 7.

boolean checkdigit(int digit,int checksum): 
    int sum = digit % 7 //% is the modulo operator
    return (checksum - sum) % 7 == 0

So in one line it would look like this

boolean check = (checksum - digit % 7 ) % 7 == 0
x digitsum(x) x%7
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
6 6 6
7 7 0
8 1 1
9 2 2
10 3 3
11 4 4
12 5 5
13 6 6
14 7 0
Thread Thread
 
kevinmungai profile image
Kevin Mungai • Edited

Thank you for clarifying. I have now understood.

It is actually a really neat solution it basically reduces the code to just two functions.

(defn is-valid-modified?
  [number]
  (let [first-four (quot number 10)
        check-sum (rem number 10)]
    (-> first-four
        (mod 7)
        (- check-sum)
        (mod 7)
        (= 0))))

user> (is-valid-modified? 10006)
true
user> (is-valid-modified? 99993)
true
user> (is-valid-modified? 99998)
false
user> (is-valid-modified? 12342)
true
user> (is-valid-modified? 11697)
true
(defn validate-scratch-card [card-number]
  (let [f (comp is-valid-modified? #(Integer/parseInt %))]
    (->> (string/split card-number #"(-|\s)")
         (map f)
         (every? true?))))


user> (validate-scratch-card "10006 12342 00081 99998")
false
user> (validate-scratch-card "10006 12342 00081 99993")
true

I have learnt something, thank you.