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 x 2 = 2; Partial-result (PR) : 2
-
8 x 7 = 56; PR : 56 + 2 = 58
-
1 x 9 = 9; PR : 67
-
0 x 1 = 0; PR : 67
-
6 x 4 = 24; PR : 91
-
2 x 6 = 12; PR : 103
-
3 x 3 = 9; PR : 112
-
4 x 5 = 20; PR : 132
-
2 x 8 = 16; PR : 148
-
0 x 2 = 0; PR : 148
-
0 x 7 = 0; PR : 148
-
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)
-
-
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) :
-
ISerializable (switches the the binary serialization from automatic to manual)
-
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 :)