[EMAIL PROTECTED] wrote:
> 
> One glitch is a sub-script I borrowed from elsewhere that validates the
> format of credit card numbers. I don't actually understand what it
> does, but it works. ("The secret is to bang the rocks together, guys!"
> - Adams)
> 

SHORT VERSION:

A credit card number is validated by

a) verifying that the length is correct (right number of digits), and
b) verifying that the check digit is correct (all digits have a valid
   sanity-check releationship).

I've actually been planning to code and post a REBOL version of a
"universal check digit" algorithm I developed a few years ago.
(I'm sure I'm not the only one to think of the concept behind this
algorithm, but all the published descriptions of check digits I've
seen overlook the idea.)

I'll post that (or at least a translation of the Perl routine you
asked about) as soon as I put out the current collection of brush
fires.  (Of course, that's not meant to pre-empt anyone else who wants
to tackle the task.)


BORING DETAILS:

The arithmetic of check-digit calculation is a cool idea that
deserves to be in every programmer's tool kit of concepts.  For
those who aren't familiar with them, I give a few comments of
explanation below.

The fact that you found the original code unnecessarily difficult to
read and understand is testimony to your good taste!  (I hope that
the fact that I understand it is only evidence that I'm already
familiar with the algorithms involved, and that I have 12 years'
experience reading badly-written student programs, and NOT evidence
that I have bad taste!  ;-)


On check digits:

For those who aren't familiar with them, check digits are simply an
extension of the parity-bit concept to decimal numbers.  The old
"casting out nines" technique for validating arithmentic (I don't
think they teach it now that calculators are so cheap) is another
use of the same idea in disguise.

Check digits provide a simple validity test that doesn't require
access to a complete database of legitimate ID numbers.  The last
digit is calculated from the other digits of the number (by a
variety of formulas, depending on the relevant standard) and can
be recalculated as a quick test to weed out obviously bogus or
counterfeit IDs.

The simplest kind just does arithmetic on the entire original
number; an example is a "mod 7" check digit, calculated as the
remainder of dividing the original by 7.  An ID of 030159 would
get a check digit of 3 (because remainder 30'159 7 is 3), and
would be written as 0301597 (often with hyphens added for human
convenience).  If someone offered an ID of 0426015, we could
calculate 

    >> remainder 42'601 7
    == 6

to prove that the ID was invalid, without even having to hit an
ID database.

More complex check digits (such as the ones used for credit cards)
have a slightly more complicated scheme which is essentially
always a variation on the following procedure:

1)  break the number into its digits;
2)  multiply each digit by a "weight" factor, based on its position;
3)  sum the weighted products;
4)  take the remainder, based on some modulus (divisor); and
5)  use that remainder (or its complement) as the check digit.

The reason (perhaps I should say "flimsy excuse" -- see below) for
the three routines called ver13, ver15, and ver16 in the Perl code
is that credit card check digits start weighting at the right-hand
digit of the basic number, with even-position and odd-position
digits weighted differently.

For example, a complemented mod 9 check digit with left-to-right
weights of [1 2 5] could be calculated according to the following
paper-and-pencil scheme:

     0      3      0      1      5      9
   * 1    * 2    * 5    * 1    * 2    * 5
   ---    ---    ---    ---    ---    ---
     0  +   6  +   0  +   1  +  10  +  45  =  62

   9 - remainder 62 9 = 1  (this is the "complemented" part)

   Final ID = 0301591

The check digit scheme in the Perl code is complemented mod 10 with
right-to-left weights of [2 1].


On how NOT to write code:

The original Perl code is VERY poorly-written, which certainly gets
in the way of understanding what's going on.  It's almost a text-
book example of what happens when one ignores everything we've
learned over the years about program design and programming style.
All of this bogosity has NOTHING to do with the fact that it's
written in Perl, by the way!

I'm offering these criticisms only partly because the original code
hacked me off; I hope that the commments below will help remind us
REBOLers of ways to avoid creating such a mess.

1) Different cards have slightly different rules for (a) and/or (b).
   Instead of writing a separate validity checker for each type of
card and then just invoking the correct one based on card type, the
code smooshes all checks into a single function.  The author
apparently never heard of code factoring.

2) The code uses redundant if's all over the place, and uses goto's
   (hidden in a sub-function, for cryin' out loud!) for error exits.
The author apparently never heard of structured programming.

3) The code does the calculation partly with string operators and
   partly with (clumsy -- see below) arithmetic, which obscures the
actual nature of the calculation unnecessarily.  The author
apparently never learned that source code is for people to read.
The author apparently never learned that if you code something you
don't understand yourself, then your code will not be understandable.

3) The code uses of individual variables to hold the digits of the
   number being checked, instead of an array.  The author apparently
never heard of data structures.

4) The code uses lots of redundant variables of the define-it-and-
   use-it-once-and-throw-it-away flavor.  The author apparently never
learned to write readable expressions with more than one operator.

5) The code uses lots of repeated snippets, of the cut-and-paste
   variety.  (I like the saying "Cut-and-paste is to code as
search-and-destroy is to ideas.")  The author apparently never
heard of generalizing repeated actions into functions.

6) The code uses explicit decision logic and string-wise manipulation
   of a number's digits instead of the modulus operator (written as %
in Perl or c, and 'remainder in REBOL).  The author apparently never
learned enough arithmetic to be writing code for this application,
and never learned to use built-in language features instead of
rewriting them himself (wasting both programming time and run time).

I could say more, but everybody's probably tired of this by now.

-jn-

Reply via email to