Andrei Rinea

.NET Framework & SQL Server

Creating a reusable class - with all the goodies - part 3 : Final analysys

clock September 7, 2009 23:59 by author Andrei Rinea

I've decided to write a class that would model a SSN-like personal identification number used in Romania. It should be useful both in production code and as a learning material for my readers. In the first part I've shown a preliminary analysys and further analysys in the second part.

Validation logic and what interfaces to implement is what is left for this part.

Let's see the fragments that can be validated so we'll see how to model the validation flow. The first digit (the fragments can be seen in the first part) can have values from 1 to 9 so all possible values are valid. Nothing to validate here. The second fragment is the year. Also all possible values (00 to 99) are valid so nothing to validate here.

The fun starts at the next fragment, the month. Only 01 to 12 values are valid anything outside (00, 13, 14, ..., 99) are invalid as they don't represent a valid year. The dating fun gets even hotter at the day. Although 00, 32, 33 and so on values are obviously invalid, some values are valid only in some months or some years.
29th of february is valid in 2008 but not in 2009. 31 is valid in January but not in April. 30 is valid in April but not in February and so on.

Next, the county has a list of predefined values as shown here, in the specs, so their validations should be piece of cake. The Enum.IsDefined (remember from the previous part that I've decided to use an enum for the county) should be a snap :)

The index value must only not be 000 any other value is valid. This brings us to the final check : the check digit. The algorithm is moderately complex (as in not really 1+1 but something close) as seen in the specs :

You take the PNC without the last (check) digit and put it next to the predefined value 279146358279. Next you multiply the first digit of the PNC to be validated to the first digit of this special validation value. Then you store it. Then you take the 2nd digit of your PNC and the 2nd digit of the special value and multiply it and you add it (the product) to the previously stored number and so on until you finish. In the end you have a sum of products that you will modulo by 11 and have a result : for 0..9 this is the final result. For 10 the result will be 1. This is the check digit. Enough theory, let's test it on my PNC :

My PNC :               1810623420056
The validation value : 279146358279
  1. 1 x 2 = 2; Partial-result (PR) : 2
  2. 8 x 7 = 56; PR : 56 + 2 = 58
  3. 1 x 9 = 9; PR : 67
  4. 0 x 1 = 0; PR : 67
  5. 6 x 4 = 24; PR : 91
  6. 2 x 6 = 12; PR : 103
  7. 3 x 3 = 9; PR : 112
  8. 4 x 5 = 20; PR : 132
  9. 2 x 8 = 16; PR : 148
  10. 0 x 2 = 0; PR : 148
  11. 0 x 7 = 0; PR : 148
  12. 5 x 9 = 45; PR : 193

Now 193 % (modulo) 11 = 6 => my check digit is 6. This verifies my full PNC.

As for interfaces to implement there are already-stated interfaces (as shown in the end of part 1) :

  • IEquatable<T> (enables equality comparison to another instance; type-safe)

    NOTE : Here type safe means the comparand is forced to be of the same type whereas in the type-unsafe versions the comparand is of type System.Object from which all classes inherit in .NET which allows invalid type instances to be passed in - these are required to work with old components and classes.
  • IComparable (enables value comparison to another instance; type-unsafe)
  • IComparable<T> (enables value comparison to another instance; type-safe)
  • IConvertible (enables converting to many basic types)
  • ICloneable; I thought of this later - after the initial analysys (enables deep cloning of the instance)

There are also interfaces necessary to improve the serialization performance (speed and memory) :

In the end, the IsValid and Violations properties should be grouped into a IValidable<T> interface that I'll define right now :

interface IValidable<TFieldEnum> where TFieldEnum : struct
{
    bool IsValid { get; }
    KeyValuePair<TFieldEnum, string>[] Violations { get; }
}

In the next part we'll FINALLY begin writing some code :)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Creating a reusable class - with all the goodies - part 2 : Further analysys

clock September 7, 2009 23:27 by author Andrei Rinea

In the first part we've layed out the basic requirements and had a glance of the data the class would model. In short it would model a SSN-like identificator for romanian citizens.

Let's see what properties and methods the class should expose and what interfaces should be implemented and how.

Basic data :

  • Sex
  • Foreigner or native
  • Resident
  • Birth date
  • Birth county
  • Index
  • Check digit

Extra information :

  • The raw value
  • A list of violations (if any)
  • A boolean to quickly see if the instance is valid or not

Let's make a quick range check and see what data type we could choose for the underlying (raw) value :

The smallest valid PNC could be 1010100010011 (male, January 1st, 1900, born in Alba, first born in that day and the last digit, the check digit I am too lazy to compute it - we'll see later and - its value won't change the calculations much). The largest valid could be 9311299529999 (non-resident foreigner born on December 31st, 1999 and so on..).

The range would be 9311299529999 - 1010100010011 = 8,301,199,519,988

Now this is larger than Int32's (2 billion and something) and UInt32's (4 billion and something) maximum values so Int64 is the smallest numeric data type that could fit the raw value. It also fits the value without using an offset (having the raw value stored as a difference between a minimum value and the actual value) which simplifies the storage logic.

Having it fit in an Int64 is reassuring that at least on 64bits OSs + CPUs the operations on it will be fast.

Let's go back to the public properties now. The sex property could have been a simple boolean and set up a convention to set true for one sex and false for the other (I won't go in deep phylosophical discussion whether men or women are "true" :P ). However value 9 for the first digit screws things up as it does not specify the sex. Sure, a nullable boolean could be in order but I will choose an enum over this one. Let's call it PersonBirthSex and assign three values for it : Male, Female and Unknown (for 9'ers).

The Foreigner property can very well be a bool just like Resident.
The BirthDate will be a DateTime.
For the county of birth an enum will fit the bill.
Index is always positive and has values from 1 to 999 inclusive. So the nicest fit will be the UInt16 (ushort) type.
The check digit is from 1 to 9 inclusive so a byte should do the trick.

Having settled on the public properties, their types and the raw value type we'll see the validation part.

As I said in the beginning the instance of the class should be able to report zero, one or more violations that it may contain. For a quick check it will also expose a bool that we'll call "IsValid". Let's end the analysys in the next episode.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


About me

A passionated .NET Developer working closely with Microsoft technologies. View my public site.

Page List

Sign in