I have a script for counting the paranthese depth of a text. My function counts the depth and checks for open parantheses, and is supposed to return the following tupel: (depth, valid, balanced).
The depth is controlling how many valid parantheses the text contains.
Valid checks if there are to many or any closing paranthese missing their counterpart, of if its a negative value.
Balanced check controlls if the value is 0 or not.
s = '((This teXt)((is)(five deep((, valid))and))balanced)\t'
te = ''.join(s).lower()
par = ''
for ch in te:
if ch not in (' ', '\n', ',', '.', '-', '–', '—', '*',
'«', '»', ':', ';', '’', '?', "'", '"',
'/', '!', '…', '´', '`', '+', '[', ']',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9','a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', 'æ', 'ø', 'å', '\t'):
par += ch
def max_dep():
count = 0
max_num = 0
for i in par:
if i == '(':
count += 1
if max_num < count:
max_num = count
if i == ')':
count -= 1
val = 0
for t in par:
if t == '(':
val += 1
if t == ')':
val -= 1
if val == 0:
val = True
else:
val = False
bal = 0
for x in par:
if x == '(':
bal += 1
if x == ')':
bal -= 1
if bal == 0:
bal = True
else:
bal = False
return max_num, val, bal
print(max_dep())
Since 'val' = 0 and 'bal' = 0, I was hoping on the print (5, True, True), but as I hvae come to understand, 0 is never True. Is there any hope to get this function to print True for 0 or do I have to start over?
In short: The solution was to pull back the check/ statements from the for-loop.
'''s = '((This teXt)((is)(five deep((, valid))and))balanced)\t'
te = ''.join(s).lower()
par = ''
for ch in te:
if ch not in (' ', '\n', ',', '.', '-', '–', '—', '*',
'«', '»', ':', ';', '’', '?', "'", '"',
'/', '!', '…', '´', '`', '+', '[', ']',
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9','a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', 'æ', 'ø', 'å', '\t'):
par += ch
def max_dep():
count = 0
max_num = 0
for i in par:
if i == '(':
count += 1
if max_num < count:
max_num = count
if i == ')':
count -= 1
val = 0
for t in par:
if t == '(':
val += 1
if t == ')':
val -= 1
if val < 0:
val = False
else:
val = True
bal = 0
for x in par:
if x == '(':
bal += 1
if x == ')':
bal -= 1
if bal == 0:
bal = True
else:
bal = False
return max_num, val, bal
print(max_dep())'''
Related
function generateRandomString($minlen = 7, $maxlen = 10, $randomCase = 0) {
$length = rand($minlen, $maxlen);
$symbols = array('A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'R', 'S',
'T', 'U', 'V', 'X', 'Y', 'Z',
'1', '2', '3', '4', '5', '6',
'7', '8', '9', '0');
$string = '';
for ($i = 0; $i < $length; $i++) {
$index = rand(0, strlen($symbols) - 1);
$symbol = $symbols[$index];
if ($randomCase)
$symbol = (rand(0, 1)) ? strtolower($symbol) : $symbol;
$string .= $symbol;
}
return $string;
Where can there be an error in the code?
I use IN a lot in my project and I have lots of these warnings:
[DCC Warning] Unit1.pas(40): W1050 WideChar reduced to byte char in
set expressions. Consider using CharInSet function in SysUtils unit.
I made a quick test and using CharInSet instead of IN is from 65%-100% slower:
if s1[i] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] then
vs
if CharInSet(s1[i], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']) then
Here is code for 2 tests, one works with loop through shorter strings, one loops once through a large string:
Adding 2 buttons on form I tested this for short string:
procedure TForm1.Button1Click(Sender: TObject);
var s1: string;
t1, t2: TStopWatch;
a, i, cnt, vMaxLoop: Integer;
begin
s1 := '[DCC Warning] Unit1.pas(40): W1050 WideChar reduced to byte char in set expressions. Consider using CharInSet function in SysUtils unit.';
vMaxLoop := 10000000;
cnt := 0;
t1 := TStopWatch.Create;
t1.Start;
for a := 1 to vMaxLoop do
for i := 1 to Length(s1) do
if s1[i] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] then
inc(cnt);
t1.Stop;
cnt := 0;
t2 := TStopWatch.Create;
t2.Start;
for a := 1 to vMaxLoop do
for i := 1 to Length(s1) do
if CharInSet(s1[i], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']) then
inc(cnt);
t2.Stop;
Button1.Caption := inttostr(t1.ElapsedMilliseconds) + ' - ' + inttostr(t2.ElapsedMilliseconds);
end;
And this for 1 long string:
procedure TForm1.Button2Click(Sender: TObject);
var s1: string;
t1, t2: TStopWatch;
a, i, cnt, vMaxLoop: Integer;
begin
s1 := '[DCC Warning] Unit1.pas(40): W1050 WideChar reduced to byte char in set expressions. Consider using CharInSet function in SysUtils unit.';
s1 := DupeString(s1, 1000000);
s1 := s1 + s1 + s1 + s1; // DupeString is limited, use this to create longer string
cnt := 0;
t1 := TStopWatch.Create;
t1.Start;
for i := 1 to Length(s1) do
if s1[i] in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] then
inc(cnt);
t1.Stop;
cnt := 0;
t2 := TStopWatch.Create;
t2.Start;
for i := 1 to Length(s1) do
if CharInSet(s1[i], ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']) then
inc(cnt);
t2.Stop;
Button2.Caption := inttostr(t1.ElapsedMilliseconds) + ' - ' + inttostr(t2.ElapsedMilliseconds);
end;
Why do they recommend slower option, or how can I fix this warning without penalty in performance?
The warning is telling you that your code may be defective. Because sets can only be based on types with ordinality of 256 or less, the base type is truncated to that size. Now, Char is an alias for WideChar and has ordinality 65536. So the warning is there to tell you that your program may not behave as you expect. For instance, one might ask what this expression evaluates to:
['A', chr(256)] = ['A']
One might expect it to evaluate false, but in fact it evaluates true. So I think you should certainly take heed of the compiler when it issues this warning.
Now, it so happens that your set, which can and should be written more concisely as ['A'..'Z'], is made up entirely of ASCII characters. And it happens (thanks to commentors Andreas and ventiseis) that in that case the compiler generates correct code for such a set, regardless of the ordinal value of the character to the left of the in operator. So
if s1[i] in ['A'..'Z'] then
will result in correct code, in spite of the warning. And the compiler is able to detect that the set's elements are contiguous and generate efficient code.
Note that this does depend on the set being a literal and so the optimisation can be performed by the compiler. And that is why it can perform so much better than CharInSet. Because CharInSet is a function, and the Delphi optimiser has limited power, CharInSet is not able to take advantage of the contiguous nature of this specific set literal.
The warning is annoying though, and do you really want to rely on remembering the very specific details of when this warning can safely be ignored. Another way to implement the test, and sidestep this warning is to use inequality operators:
if (c >= 'A') and (c <= 'Z') then
....
You'd probably wrap this in an inlined function to make the code even easier to read.
function IsUpperCaseEnglishLetter(c: Char): Boolean; inline;
begin
Result := (c >= 'A') and (c <= 'Z');
end;
You should also ask yourself whether or not this code is a performance bottleneck. You should time your real program rather than such an artificial program. I'll bet that this code isn't a bottleneck and if so you should not treat performance as the key driver.
I'm self-learning Ruby, and one assignment is to make a Caesar cipher.
Using #gsub, I've been able to change my letters to integers ('c' => 2), shift them, then change the new integers to strings (2 => "2").
I've hit a wall, and the Ruby documentation isn't helping. When I try to #gsub the strings back to letters ("2" => 'c') it only recognizes 0-9. Everything after that is just a concatenation of those numbers ("12" => 'bc' instead of => 'l').
Why does Ruby do this, and how can I fix it?
Thanks for your help guys.
code: (I know it's sloppy beginner's code; I will try to edit it after it passes)
def convert_to_integer
puts "What would you like to encode?"
words = gets.chomp
words = words.split("")
words.map { |words| words.gsub!(/[a-z]/, 'a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4, 'f' => 5, 'g' => 6, 'h' => 7, 'i' => 8, 'j' => 9, 'k' => 10, 'l' => 11, 'm' => 12, 'n' => 13, 'o' => 14, 'p' => 15, 'q' => 16, 'r' => 17, 's' => 18, 't' => 19, 'u' => 20, 'v' => 21, 'w' => 22, 'x' => 23, 'y' => 24, 'z' => 25)
}
integer = words.map! { |letter| letter.to_i }
return integer
end
def shift_left(integer, number = 0)
puts "How many letters (to the left) would you like to shift it?"
number = gets.to_i
integer.map! { |n| n - number }
return integer
end
def convert_to_letter(integer)
integer.map! { |integer| integer.to_s }
integer.map! { |n| n.gsub(/[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 , 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]/, '0' => 'a', '1' => 'b', '2' => 'c', '3' => 'd', '4' => 'e', '5' => 'f', '6' => 'g', '7' => 'h', '8' => 'i', '9' => 'j', '10' => 'k', '11' => 'l', '12' => 'm', '13' => 'n', '14' => 'o', '15' => 'p', '16' => 'q', '17' => 'r', '18' => 's', '19' => 't', '20' => 'u', '21' => 'v', '22' => 'w', '23' => 'x', '24' => 'y', '25' => 'z')
}
print integer
end
convert_to_letter(shift_left(convert_to_integer))
You don't need to do a gsub there. gsub is normally used to replace parts of a bigger string. You want to replace the whole thing.
This should do the trick:
def convert_to_letter(integers)
replacements = {0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd', 4 => 'e',
5 => 'f', 6 => 'g', 7 => 'h', 8 => 'i', 9 => 'j', 10 => 'k',
11 => 'l', 12 => 'm', 13 => 'n', 14 => 'o', 15 => 'p', 16 => 'q',
17 => 'r', 18 => 's', 19 => 't', 20 => 'u', 21 => 'v', 22 => 'w',
23 => 'x', 24 => 'y', 25 => 'z'
}
integers.map{|x| replacements[x]}.join
end
Also, be careful with destructive operations (map! here). You may run into undesired side-effects (for example, some arrays will change when you think they shouldn't).
It's easier and faster to use lookups:
#letter_to_number = ('a'..'z').zip(0..25).to_h
#number_to_letter = (0..25).zip('a'..'z').to_h
def convert_to_integers(letters)
letters.map{|l| #letter_to_number[l]}
end
def convert_to_letters(numbers)
numbers.map{|n| #number_to_letter[n]}
end
There's also a shortcut that combines the lookups and combines the methods.
#convert = (('a'..'z').zip(0..25) + (0..25).zip('a'..'z')).to_h
def convert(objects)
objects.map{|o| #convert[o]}
end
That's not how regular expressions work. "12".gsub(/[12]/, '12' => 'm') does not produce "m". That code says to find any occurrence of "1" or "2", and replace it according to the following rule: "12" gets replaced with "m", and, implicitly, anything else gets replaced with nothing. Both the "1" and the "2" are occurrences of "1" or "2", but neither of them are "12", so they both get replaced with nothing. Thus the above results in just the empty string.
In fact gsub and regular expressions are not really ideal for this problem. You could just do this:
def char_to_int(char)
char.ord - 97
end
def int_to_char(int)
(int + 97).chr
end
def caesar(string, shift)
string.split(" ").map do |word|
word.split("").map do |letter|
int_to_char((char_to_int(letter) - shift) % 26)
end.join
end.join(" ")
end
Alright, I know this is going to sound bad, like I'm going to use this for un-ethical things, but you have my word that I am not.
I am writing a paper for my Computer and Information Security course and the topic I chose was hashing methods. One of the points that I go over in my paper is MD5 being only one-way and the only way to crack an MD5 hash is to continuously make strings and use an MD5 function, then compare it with the hash you want to crack.
I would like to build a really simple mock-up program to show alongside my paper (we do a presentation and this would be an awesome thing to have), so I wanted to work out an algorithm that makes a string with every possible character combination up to 8 characters. For example the output will be:
a, b, c, ..., aa, ab, ac, ... ba, bb, bc etc etc etc.
It need to include letters, numbers and symbols if possible.
I got partly through the algorithm for this, but unfortunately my programming skills are not up to the task. If anyone can provide a complete algorithm for this I'd be extremely thankful.
Again, if you think I'm a liar and I'm going to use this for hacking purposes you don't have to leave an answer.
Thank you. :)
In Python, itertools.product does almost all you require -- though it does it for just one "number of repeats", so you'll have to iterate from 1 to 8 (not hard;-). In essence:
import itertools
import string
# whatever you wish as alphabet (lower/upper, digits, punct, &c)
myalphabet = string.ascii_lowercase + string.ascii_digits
def prods(maxlen, alphabet=myalphabet):
for i in range(1, maxlen+1):
for s in itertools.product(alphabet, repeat=i):
yield ''.join(s)
Of course, for an alphabet of length N and K repetitions (8 in your case) this does produce N + N^2 + ... + N^K possibilities (2,901,713,047,668 possibilities for N=36 and K=8), but, what's a few trillion outputs among friends!-)
To implement this i would probably encode integers to base 36 (or more if you wanted symbols).
1 = 1
2 = 2
...
a = 10
b = 12
..
and so on.
then you would have a number, like 38 and do some divisions, ie:
38/36 = 1 remaider 2 = 12 in base 36
then just run a for loop to your max number you want to encode, something very large and output your encoded numbers.
just for fun i wrote this for you: http://pastebin.antiyes.com/index.php?id=327
It is not true that "the only way to crack an MD5 hash" is to generate every possible string and look for collisions. In fact, if you have access to the original it is possible to modify it so that its MD5 matches that of another file you can create. This is described in a paper at infosec.edu.
Even if you cannot modify the original file, rainbow tables of MD5 checksums exist which can be used to generate collisions.
These facts make MD5 unsuitable for passwords or cryptography, and in fact the U.S. government has forbidden its continued use for secure applications.
If you already have access to the hashed version of the password, then MD5 is broken to begin with. That said, when it comes to breaking a hashed value, you'd likely be better off using Rainbow Tables, Dictionary Attacks, and Social Engineering over your brute force method. That said, since you asked for an algorithm to generate all the values, maybe the following will be beneficial (C#):
using System;
using System.Text;
namespace PossibiltyIterator
{
class Program
{
static readonly char[] Symbols = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '!', '#', '#', '$', '%', '^', '&',
'*', '(', ')', '-', '_', '+', '=', '/', '\\', '[', ']', '{', '}', ';', ':', '\'', '"',
',', '.', '<', '>', '?', '`', '~'
};
const int MaxLength = 8;
static void BuildWord(int currentLength, int desiredLength, char[] word)
{
if (currentLength == desiredLength)
{
Console.WriteLine(word);
}
else
{
for (int value = 0; value < Symbols.Length; ++value)
{
word[currentLength] = Symbols[value];
BuildWord(currentLength + 1, desiredLength, word);
}
}
}
static void Main(String[] args)
{
double totalValues = (Math.Pow(Symbols.Length, MaxLength + 1) - Symbols.Length)/(Symbols.Length - 1);
Console.WriteLine("Warning! You are about to print: {0} values", totalValues);
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true /* intercept */);
for (int desiredLength = 1; desiredLength <= MaxLength; ++desiredLength)
{
BuildWord(0 /* currentLength */, desiredLength, new char[MaxLength]);
}
}
}
}
To be completely honest, this can be optimized further. Because it builds all the "words" of length 1, then does that work a second time in building the words of length 2. It would be smarter to build the words of length MaxLength, then truncate one letter to build a word of MaxLength-1.
Here is the optimized version... note that it does NOT return the words in the order originally requested.
using System;
using System.Text;
namespace PossibiltyIterator
{
class Program
{
static readonly char[] Symbols = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '!', '#', '#', '$', '%', '^', '&',
'*', '(', ')', '-', '_', '+', '=', '/', '\\', '[', ']', '{', '}', ';', ':', '\'', '"',
',', '.', '<', '>', '?', '`', '~'
};
const int MaxLength = 8;
static void BuildWord(int currentLength, int desiredLength, char[] word)
{
if (currentLength != desiredLength)
{
for (int value = 0; value < Symbols.Length; ++value)
{
word[currentLength] = Symbols[value];
BuildWord(currentLength + 1, desiredLength, word);
}
word[currentLength] = '\0';
}
Console.WriteLine(word);
}
static void Main(String[] args)
{
double totalValues = (Math.Pow(Symbols.Length, MaxLength + 1) - Symbols.Length)/(Symbols.Length - 1);
char[] word = new char[MaxLength];
Console.WriteLine("Warning! You are about to print: {0} values", totalValues);
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true /* intercept */);
BuildWord(0 /* currentLength */, MaxLength, new char[MaxLength]);
}
}
}
To complete the post with a Java example which will print out the Base64 encoded MD5's of all possible character combinations using only 0-9 and a-z characters:
MessageDigest digest = MessageDigest.getInstance("MD5");
int i = 0;
while (true)
{
String raw = Integer.toString(i, Character.MAX_RADIX);
byte[] md5 = digest.digest(raw.getBytes());
String base64 = new BigInteger(1, md5).toString(16);
System.out.println(raw + " = " + base64);
i++;
}
Can anyone provide an algorithm to validate a Singaporean FIN?
I know with a Singaporean NRIC I can validate it via modulo 11 and then compare the result to a lookup table but cannot find a similar lookup table for the FIN.
I also do not know for sure if the modulo 11 is the correct method to validate.
I am aware the government sells a algorithm for $400 but maybe someone knows a cheaper way.
Bonus points for c# implementation.
And again in PHP
function isNricValid ($theNric) {
$multiples = array( 2, 7, 6, 5, 4, 3, 2 );
if (!$theNric || $theNric == '')
{
return false;
}
if (strlen($theNric) != 9)
{
return false;
}
$total = 0;
$count = 0;
$numericNric = 0;
$first = $theNric[0];
$last = $theNric[strlen($theNric) - 1];
if ($first != 'S' && $first != 'T')
{
return false;
}
$numericNric = substr($theNric, 1, strlen($theNric) - 2);
if (!is_numeric ($numericNric)) {
return false;
}
while ($numericNric != 0)
{
$total += ($numericNric % 10) * $multiples[sizeof($multiples) - (1 + $count++)];
$numericNric /= 10;
$numericNric = floor($numericNric);
}
$outputs = '';
if (strcmp($first, "S") == 0)
{
$outputs = array( 'J', 'Z', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A' );
}
else
{
$outputs = array( 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'J', 'Z', 'I', 'H' );
}
return $last == $outputs[$total % 11];
}
function isFinValid ($fin)
{
$multiples = array( 2, 7, 6, 5, 4, 3, 2 );
if (!$fin || $fin == '')
{
return false;
}
if (strlen($fin) != 9)
{
return false;
}
$total = 0;
$count = 0;
$numericNric = 0;
$first = $fin[0];
$last = $fin[strlen($fin) - 1];
if ($first != 'F' && $first != 'G')
{
return false;
}
$numericNric = substr($fin, 1, strlen($fin) - 2);
if (!is_numeric ($numericNric)) {
return false;
}
while ($numericNric != 0)
{
$total += ($numericNric % 10) * $multiples[sizeof($multiples) - (1 + $count++)];
$numericNric /= 10;
$numericNric = floor($numericNric);
}
$outputs = array();
if (strcmp($first, 'F') == 0)
{
$outputs = array( 'X', 'W', 'U', 'T', 'R', 'Q', 'P', 'N', 'M', 'L', 'K' );
}
else
{
$outputs = array( 'R', 'Q', 'P', 'N', 'M', 'L', 'K', 'X', 'W', 'U', 'T' );
}
return $last == $outputs[$total % 11];
}
Here's similar code written in JavaScript
var nric = [];
nric.multiples = [ 2, 7, 6, 5, 4, 3, 2 ];
nric.isNricValid = function (theNric) {
if (!theNric || theNric == '')
{
return false;
}
if (theNric.length != 9)
{
return false;
}
var total = 0
, count = 0
, numericNric;
var first = theNric[0]
, last = theNric[theNric.length - 1];
if (first != 'S' && first != 'T')
{
return false;
}
numericNric = theNric.substr(1, theNric.length - 2);
if (isNaN(numericNric)) {
return false
}
while (numericNric != 0)
{
total += (numericNric % 10) * nric.multiples[nric.multiples.length - (1 + count++)];
numericNric /= 10;
numericNric = Math.floor(numericNric);
}
var outputs;
if (first == 'S')
{
outputs = [ 'J', 'Z', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A' ];
}
else
{
outputs = [ 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'J', 'Z', 'I', 'H' ];
}
return last == outputs[total % 11];
}
nric.isFinValid = function(fin)
{
if (!fin || fin == '')
{
return false;
}
if (fin.length != 9)
{
return false;
}
var total = 0
, count = 0
, numericNric;
var first = fin[0]
, last = fin[fin.length - 1];
if (first != 'F' && first != 'G')
{
return false;
}
numericNric = fin.substr(1, fin.length - 2);
if (isNaN(numericNric)) {
return false;
}
while (numericNric != 0)
{
total += (numericNric % 10) * nric.multiples[nric.multiples.length - (1 + count++)];
numericNric /= 10;
numericNric = Math.floor(numericNric);
}
var outputs;
if (first == 'F')
{
outputs = [ 'X', 'W', 'U', 'T', 'R', 'Q', 'P', 'N', 'M', 'L', 'K' ];
}
else
{
outputs = [ 'R', 'Q', 'P', 'N', 'M', 'L', 'K', 'X', 'W', 'U', 'T' ];
}
return last == outputs[total % 11];
}
After a little searching around I found a way to validate them. This doesn't neccersarily mean the FIN is valid, just that it falls within a valid range.
I based it on algorithms from http://www.ngiam.net/NRIC/ppframe.htm
I've also included a similar method for checking NRIC because I figure anyone who comes across this and is interested in one is interested in the other as well.
Hope this helps someone!
private static readonly int[] Multiples = { 2, 7, 6, 5, 4, 3, 2 };
public static bool IsNricValid(string nric)
{
if (string.IsNullOrEmpty(nric))
{
return false;
}
// check length
if (nric.Length != 9)
{
return false;
}
int total = 0
, count = 0
, numericNric;
char first = nric[0]
, last = nric[nric.Length - 1];
if (first != 'S' && first != 'T')
{
return false;
}
if (!int.TryParse(nric.Substring(1, nric.Length - 2), out numericNric))
{
return false;
}
while (numericNric != 0)
{
total += numericNric % 10 * Multiples[Multiples.Length - (1 + count++)];
numericNric /= 10;
}
char[] outputs;
if (first == 'S')
{
outputs = new char[] { 'J', 'Z', 'I', 'H', 'G', 'F', 'E', 'D', 'C', 'B', 'A' };
}
else
{
outputs = new char[] { 'G', 'F', 'E', 'D', 'C', 'B', 'A', 'J', 'Z', 'I', 'H' };
}
return last == outputs[total % 11];
}
public static bool IsFinValid(string fin)
{
if (string.IsNullOrEmpty(fin))
{
return false;
}
// check length
if (fin.Length != 9)
{
return false;
}
int total = 0
, count = 0
, numericNric;
char first = fin[0]
, last = fin[fin.Length - 1];
if (first != 'F' && first != 'G')
{
return false;
}
if (!int.TryParse(fin.Substring(1, fin.Length - 2), out numericNric))
{
return false;
}
while (numericNric != 0)
{
total += numericNric % 10 * Multiples[Multiples.Length - (1 + count++)];
numericNric /= 10;
}
char[] outputs;
if (first == 'F')
{
outputs = new char[] { 'X', 'W', 'U', 'T', 'R', 'Q', 'P', 'N', 'M', 'L', 'K' };
}
else
{
outputs = new char[] { 'R', 'Q', 'P', 'N', 'M', 'L', 'K', 'X', 'W', 'U', 'T' };
}
return last == outputs[total % 11];
}
The below is the URL for NRIC & FIN algaritham.
http://www.arjun.com.np/blog/all-about-nric-number-in-singapore/
public class NRIC_FIN {
public static void main(String[] args) {
String idNumber = "T123d121J";
String NRIC = idNumber.toUpperCase();
String number;
int sum = 0;
if (NRIC.length() != 9) {
System.out.println("must be 9 digits..");
return;
}
number = NRIC.substring(1, 8);
if (!isNAN(at(number, 0)))
sum += valueOf(at(number, 0)) * 2;
if (!isNAN(at(number, 1)))
sum += valueOf(at(number, 1)) * 7;
if (!isNAN(at(number, 2)))
sum += valueOf(at(number, 2)) * 6;
if (!isNAN(at(number, 3)))
sum += valueOf(at(number, 3)) * 5;
if (!isNAN(at(number, 4)))
sum += valueOf(at(number, 4)) * 4;
if (!isNAN(at(number, 5)))
sum += valueOf(at(number, 5)) * 3;
if (!isNAN(at(number, 6)))
sum += valueOf(at(number, 6)) * 2;
if (NRIC.charAt(0) == 'T' || NRIC.charAt(0) == 'G') {
sum += 4;
}
char checkChar = NRIC.charAt(8);
double p = 11 - (sum % 11);
if (NRIC.charAt(0) == 'S' || NRIC.charAt(0) == 'T') {
if ((p == 1 && checkChar == 'A') || (p == 2 && checkChar == 'B')
|| (p == 3 && checkChar == 'C')
|| (p == 4 && checkChar == 'D')
|| (p == 5 && checkChar == 'E')
|| (p == 6 && checkChar == 'F')
|| (p == 7 && checkChar == 'G')
|| (p == 8 && checkChar == 'H')
|| (p == 9 && checkChar == 'I')
|| (p == 10 && checkChar == 'Z')
|| (p == 11 && checkChar == 'J')) {
System.out.println("true...NRIC");
}
}
if (NRIC.charAt(0) == 'F' || NRIC.charAt(0) == 'G') {
if ((p == 1 && checkChar == 'K') || (p == 2 && checkChar == 'M')
|| (p == 3 && checkChar == 'L')
|| (p == 4 && checkChar == 'N')
|| (p == 5 && checkChar == 'P')
|| (p == 6 && checkChar == 'Q')
|| (p == 7 && checkChar == 'R')
|| (p == 8 && checkChar == 'T')
|| (p == 9 && checkChar == 'U')
|| (p == 10 && checkChar == 'W')
|| (p == 11 && checkChar == 'X')) {
System.out.println("true ...FIN");
}
}
System.out.println("flase ...");
}
public static boolean isNAN(char c) {
return !Character.isDigit(c);
}
public static int valueOf(char c) {
return (((int) c) - 48);
}
public static char at(String from, int index) {
return from.charAt(index);
}
}