Thursday, January 29, 2009

C# .NET 3.5 Credit card validation using Luhn formula

The following function in C# .NET 3.5 checks whether a given credit card is valid using the Mod10 or Luhn formula.

For more complete information and explanation of how the formula works, please check out this website. I based this function largely from the information I got there.

In summary the formula is as follow:
1) Double each alternative digit, starting from the second digit from the *RIGHT
2) If any digit is equal to 10 or greater, then add up the digits. e.g. "10" would be equal to "1 + 0" or "1"
3) Sum up all the digits.
4) If result is divisible by 10, then we probably have got a valid card number, otherwise it's fake.

This is a necessary, but insufficient check to verify credit card numbers generated by most financial institution. For a more complete check, you'll also need to check the first digits, to make sure they match the credit card company, eg. Master Card, may start with 51. However this is an easy check and is not a subject of this topic.

Function to check if credit card number is valid, or otherwise using Mod 10, or Luhn Formula:




public bool Mod10Check(string CreditCardNumber)
{
char[] CreditCardNumberArray = CreditCardNumber.ToCharArray();


var CreditCardDigits = new short[CreditCardNumberArray.Length];


for (int i = 0; i < CreditCardNumberArray.Length; i++)
{
CreditCardDigits[i] = short.Parse(CreditCardNumberArray[i].ToString());
}


CreditCardDigits = CreditCardDigits.Reverse().ToArray();


for (int i = 0; i < CreditCardDigits.Length; i++)
{
if (i%2 == 1)
{
CreditCardDigits[i] = (short)
(CreditCardDigits[i]*2);


if (CreditCardDigits[i]
>= 10)
{
char[] BigDigit =
CreditCardDigits[i].ToString().ToCharArray();


CreditCardDigits[i] = (short)
(short.Parse(BigDigit[0].ToString())
+ short.Parse(BigDigit[1].ToString()));
}
}
}


int SumOfDigits = CreditCardDigits.Sum(o => (int) o);


return SumOfDigits%10 == 0;
}

Wednesday, January 14, 2009

DirectoryInfo Move (UNC Compatible)

Ever wanted to move a whole directory, but .NET would not allow it? The only possible way was to create directory, at target, and copy each files to the new folder and delete the old one.

To make matter worse, you can't copy or move directory to a shared location, with a given UNC path.

The following code is an extension method to allow the built-in .NET DirectoryInfo class, to move an entire folder, even across servers.





///////////////////////////Directory Info Extension///////////////////////////
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace YourNameSpace
{
public static class DirectoryInfoExtension
{
///
/// Copy directory from one location to another, recursively.
///
///
///
///
///
public static DirectoryInfo CopyTo(this DirectoryInfo Source, DirectoryInfo Target, bool Overwrite)
{
string TargetFullPath = Target.FullName;

if (Overwrite && Target.Exists)
{
Target.Delete(true);

}
else if (!Overwrite && Target.Exists)
{
Target.MoveTo(Target.Parent.FullName + "\\" + Target.Name + "." + Guid.NewGuid().ToString());
}

//Restores target back, such that it's not pointing to the renamed, obsolete directory.
Target = new DirectoryInfo(TargetFullPath);

Target.Create();

CopyRecurse(Source, Target);

return Target;
}
///
/// Copy source recursively to target.
/// NOTE: This will create target subdirectories, but NOT target itself.
///
///
///
private static void CopyRecurse(DirectoryInfo Source, DirectoryInfo Target)
{
foreach (DirectoryInfo ChildSource in Source.GetDirectories())
{
DirectoryInfo ChildTarget = Target.CreateSubdirectory(ChildSource.Name);
CopyRecurse(ChildSource, ChildTarget);
}
foreach (FileInfo File in Source.GetFiles())
{
File.CopyTo(Target.FullName + "
\\" + File.Name);
}
}

///
/// This extension allows directory to be moved accross servers.
///
///
///
///
///
public static DirectoryInfo MoveTo(this DirectoryInfo Source, DirectoryInfo Target, bool Overwrite)
{
Source.CopyTo(Target, Overwrite);
Source.Delete(true);
return Target;
}
}
}

Case Insensitive String Replace

This post is a modification from Rick Strahl's Web Log, to replace string. ignoring case. The code has been modified to suit C# .NET 3.5 coding style - with extension methods.

Recently in one of the project I was working on, I came across a problem where I needed to replace parts of the string, while preserving the case, otherwise. That last part is the problem. While it's possible to simply call string.ToLower(), it'd result in the whole string converted to lowercase, regardles of whether they match the replacement criteria or otherwise.

The code below is designed to address the issue. It's almost copy and paste, except you need to rename the namespace to that of your solution. Reference the namespace and class, and string.Replace() will have 2 extra overloads.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YourNameSpace.SubNameSpace
{
public static class StringExtension
{
/// <summary>
/// String replace function that support
/// </summary>
/// <param name="OrigString">Original input string</param>
/// <param name="FindString">The string that is to be replaced</param>
/// <param name="ReplaceWith">The replacement string</param>
/// <param name="Instance">Instance of the FindString that is to be found. if Instance = -1 all are replaced</param>
/// <param name="CaseInsensitive">Case insensitivity flag</param>
/// <returns>updated string or original string if no matches</returns>
public static string Replace(this string OrigString, string FindString,
string ReplaceWith, int Instance,
bool CaseInsensitive)
{
if (Instance == -1)
return OrigString.Replace(FindString, ReplaceWith, CaseInsensitive);

int at1 = 0;
for (int x = 0; x < Instance; x++)
{

if (CaseInsensitive)
at1 = OrigString.IndexOf(FindString, at1, OrigString.Length - at1, StringComparison.OrdinalIgnoreCase);
else
at1 = OrigString.IndexOf(FindString, at1);

if (at1 == -1)
return OrigString;

if (x < Instance - 1)
at1 += FindString.Length;
}

return OrigString.Substring(0, at1) + ReplaceWith + OrigString.Substring(at1 + FindString.Length);
}


/// <summary>
/// Replaces a substring within a string with another substring with optional case sensitivity turned off.
/// </summary>
/// <param name="OrigString">String to do replacements on</param>
/// <param name="FindString">The string to find</param>
/// <param name="ReplaceString">The string to replace found string wiht</param>
/// <param name="CaseInsensitive">If true case insensitive search is performed</param>
/// <returns>updated string or original string if no matches</returns>
public static string Replace(this string OrigString, string FindString,
string ReplaceString, bool CaseInsensitive)
{
int at1 = 0;
while (true)
{
if (CaseInsensitive)
at1 = OrigString.IndexOf(FindString, at1, OrigString.Length - at1, StringComparison.OrdinalIgnoreCase);
else
at1 = OrigString.IndexOf(FindString, at1);

if (at1 == -1)
return OrigString;

OrigString = OrigString.Substring(0, at1) + ReplaceString + OrigString.Substring(at1 + FindString.Length);

at1 += ReplaceString.Length;
}

return OrigString;
}
}
}

Tuesday, January 6, 2009

Creating a gaussian random number in SQL Server 2005

I've been looking into this for a while, but so far I couldn't find anyone having posted a working sample of Gaussian random number generator ( and I need it for my probject)

As a bonus I also show how to modify the box-muller algorithm so that the resulting random numbers will conform to a given mean and standard deviation. It's very simple although it wasn't as intuitive as I wished, personally.



-------------------------CODE BELOW---------------------------




-- =============================================
-- Author: Alwyn Aswin
-- Create date: 01/02/2009
-- Description: Generate a normally distributed random number.
-- NOTE: Please leave the author's attribution, if you copy this code.
-- =============================================
CREATE PROCEDURE BoxMullerRandom
@Mean float = 0
,@StdDev float = 1
,@BMRand float out
AS
BEGIN
--@choice is the variable used to store the random number to return
declare @choice float, @store float, @choiceid uniqueidentifier

--checks to see if a box muller random number was already cached from previous call.
select top 1 @choiceid = randomid, @choice = random from boxmullercache
if(@choice is not null) -- if we do, delete that entry, since it's useable only once.
begin
print 'loading from cache'
delete from boxmullercache
where randomid = @choiceid
end
else --otherwise, generate a pair of box muller random number.
begin
print 'generate new ones'
declare @MethodChoiceRand float
set @MethodChoiceRand = rand()

--We re-roll if we get a 0, and use 0.5 as the cutoff point.
while @MethodChoiceRand = 0
begin
set @MethodChoiceRand= rand()
end

-- Reroll if @MethodChoiceRand = 0, this will ensure that the interval, may be divided into 2 groups with equal number of members.
-- AND it has the advantage of removing the problematic ln(0) error from the Box-Muller equation.
declare @rand1 float, @rand2 float
select @rand1 = rand(), @rand2 = rand()
while @rand1 = 0 or @rand2 = 0
begin
select @rand1 = rand(), @rand2 = rand()
end

declare @normalRand1 float, @normalRand2 float
SELECT @normalRand1 = sqrt(-2 * log(@rand1)) * cos(2*pi()*@rand2)
,@normalRand2 = sqrt(-2 * log(@rand1)) * sin(2*pi()*@rand2)

print 'box muller no 1:' + convert(varchar,@normalRand1) + ', box muller no 2:' + convert(varchar,@normalRand2)
--RandomlySelects which one to store, which one to save.

if @MethodChoiceRand <= 0.5
begin
print 'choice 1'
select @choice = @normalRand1, @store = @normalRand2
end
else if @MethodChoiceRand > 0.5
begin
print 'choice 2'
select @choice = @normalRand2, @store = @normalRand1
end

--stores the other pair into the cache to be retrieved during subsequent call to this method.
insert into boxmullercache (randomid, random)
values (newid(),@store)
end

--fix up the random number, so that it should have the correct mean and standard deviation.
set @BMRand = @choice * @stddev + @mean
END
GO



------------------------------------------------------------

I leave out the creation of the cachetable to the reader. You need to create a table to hold the other of the 2 values created via the Box-Muller algorithm! It should be fairly straight forward, it just needs an ID and a float column, which can be deduced from the code above.

Please feel free to comment or suggest ways on improving the code.