Coin counter adding double values for each exact coin - c#

Im doing a C# program that is suppose to take an amount from 0-99 (amount of change given back) and from that the program should put the change in the correct positions. A example is if I enter:
Amount of change given back: 68
it should read as:
Quarters 2
Dimes 1
Nickles 1
Pennies 3
I am using a Windows form application in visual studios 2015.
Whenever i enter the amount of 1, it should be 1 penny but it gives it 2, same for dimes nickles and quarters. anything else it works just fine.
This is what my code looks like:
private void calcButton_Click(object sender, EventArgs e){
//declare variables
const int QUARTERS = 25;
const int DIMES = 10;
const int NICKELS = 5;
const int PENNIES = 1;
int changeVal = 0;
//validations
if (int.TryParse(changeTextBox.Text, out changeVal)){
if (changeVal > 0 && changeVal < 100){
//calcs each value with the remainder operator divided by each change amount
int quarterVal = changeVal / QUARTERS;
int dimeVal = ((changeVal % QUARTERS) / DIMES);
int nickelVal = (((changeVal % QUARTERS) % DIMES) / NICKELS);
int pennyVal = ((((changeVal % QUARTERS) % DIMES) % NICKELS) / PENNIES);
//if statements to put each coin in each specific value variable
if (changeVal == QUARTERS){
quarterVal++;
changeVal = changeVal - QUARTERS;
}
else if (changeVal == DIMES){
dimeVal++;
changeVal = changeVal - DIMES;
}
else if (changeVal == NICKELS){
nickelVal++;
changeVal = changeVal - NICKELS;
}
else if (changeVal == PENNIES){
pennyVal++;
changeVal -= changeVal - PENNIES;
}
//display
quartersLabel.Text = quarterVal.ToString();
dimesLabel.Text = dimeVal.ToString();
nickelsLabel.Text = nickelVal.ToString();
penniesLabel.Text = pennyVal.ToString();
}
//error message
else{
MessageBox.Show("You have to enter a numeric value greater than 0 and less than 100");
}
}
//error message
else{
MessageBox.Show("Please enter a numeric value");
}
}
If you can tell me what I'm doing wrong that would be very helpful!

You are comparing your ChangeVal to the face value of the coins. and then Incrementing it when they are equal,
This Section only runs when your changeVal is equal to the facevalue of the coin
hence 1, 5, 10, 25 all produce a double count. Just remove the section below.
if (changeVal == QUARTERS)
{
quarterVal++;
changeVal = changeVal - QUARTERS;
}
else if (changeVal == DIMES)
{
dimeVal++;
changeVal = changeVal - DIMES;
}
else if (changeVal == NICKELS)
{
nickelVal++;
changeVal = changeVal - NICKELS;
}
else if (changeVal == PENNIES)
{
pennyVal++;
changeVal -= changeVal - PENNIES;
}

You are already calculating the number of coins required with the code below:
int quarterVal = changeVal / QUARTERS;
etc....
But you are then incrementing this by 1 if the changeVal is equal to one of your predefined constants.
Basically, you don't need the 4 if statements that increment the counts when the value entered is an exact match for one of your defined constants.

Related

How to write out odd numbers in an interval using for function

So my homework is I have to take two numbers from the user then I have to write out the odd numbers in that interval.But the code under doesn't work. It writes out "TrueFalseTrueFalse".
int szam;
int szam2=0;
int szam3=0;
int szam4=0;
Console.Write("Please give a number:");
szam = Convert.ToInt32(Console.ReadLine());
Console.Write("Please give another number:");
szam2 = Convert.ToInt32(Console.ReadLine());
if (szam>szam2)
{
for (szam3=szam, szam4 = szam2; szam4 < szam3; szam4++)
{
Console.Write(szam2 % 2==1);
}
}
else
{
for (szam3 = szam, szam4 = szam2; szam3 < szam4; szam3++)
{
Console.Write(szam3 % 2 ==1);
}
}
So if the two numbers would be 0 and 10, the program has to write out 1, 3, 5, 7, and 9
I would be careful when naming your variables yes its a small piece of code but it gets confusing to people trying to read it.
Based on the requirement, I would guess you want all the odd numbers given a certain range.
const string comma = ",";
static void Main(string[] args)
{
int start = getNumber();
int end = getNumber();
if(start > end)
{
int placeHolder = end;
end = start;
start = placeHolder;
}
string delimiter = string.Empty;
for(int i = start; i < end; i++)
{
if(i % 2 == 1)
{
Console.Write(string.Concat(delimiter,i.ToString()));
delimiter = comma;
}
}
Console.ReadLine();//otherwise you wont see the result
}
static int getNumber()
{
Console.Write("Please enter a number:");
string placeHolder = Console.ReadLine();
int toReturn = -1;
if (int.TryParse(placeHolder, out toReturn))
return toReturn;
return getNumber();
}
as Juharr mentioned in the comments, you need to check the result to print the actual number.
Width Linq you can write:
int szam = 20;
int szam2= 30;
var odds = Enumerable.Range(szam2 > szam ? szam : szam2, Math.Abs(szam-szam2))
.Where(x=>x % 2 != 0);
outputs:
21
23
25
27
29
// so we create a range from low to high (Enumerable.Range(..)
// take only the odd values (x % 2 != 0)
simply wrap it in string.Join to make a single string:
string text = String.Join(",",Enumerable.Range(szam2 > szam ? szam :
szam2,Math.Abs(szam-szam2))
.Where(x=>x % 2 != 0));

How to distribute items evenly, without random numbers

I have a situation where I need to evenly distribute N items across M slots. Each item has its own distribution %. For discussion purposes say there are three items (a,b,c) with respective percentages of (50,25,25) to be distributed evenly across 20 slots. Hence 10 X a,5 X b & 5 X c need to be distributed. The outcome would be as follows:
1. a
2. a
3. c
4. b
5. a
6. a
7. c
8. b
9. a
10. a
11. c
12. b
13. a
14. a
15. c
16. b
17. a
18. a
19. c
20. b
The part that I am struggling with is that the number of slots, number of items and percentages can all vary, of course the percentage would always total up to 100%. The code that I wrote resulted in following output, which is always back weighted in favour of item with highest percentage. Any ideas would be great.
1. a
2. b
3. c
4. a
5. b
6. c
7. a
8. b
9. c
10. a
11. c
12. b
13. a
14. b
15. c
16. a
17. a
18. a
19. a
20. a
Edit
This is what my code currently looks like. Results in back weighted distribution as I mentioned earlier. For a little context, I am trying to evenly assign commercials across programs. Hence every run with same inputs has to result in exactly the same output. This is what rules out the use of random numbers.
foreach (ListRecord spl in lstRecords){
string key = spl.AdvertiserName + spl.ContractNumber + spl.AgencyAssignmentCode;
if (!dictCodesheets.ContainsKey(key)){
int maxAssignmentForCurrentContract = weeklyList.Count(c => (c.AdvertiserName == spl.AdvertiserName) && (c.AgencyAssignmentCode == spl.AgencyAssignmentCode)
&& (c.ContractNumber == spl.ContractNumber) && (c.WeekOf == spl.WeekOf));
int tmpAssignmentCount = 0;
for (int i = 0; i < tmpLstGridData.Count; i++)
{
GridData gData = tmpLstGridData[i];
RotationCalculation commIDRotationCalc = new RotationCalculation();
commIDRotationCalc.commercialID = gData.commercialID;
commIDRotationCalc.maxAllowed = (int)Math.Round(((double)(maxAssignmentForCurrentContract * gData.rotationPercentage) / 100), MidpointRounding.AwayFromZero);
tmpAssignmentCount += commIDRotationCalc.maxAllowed;
if (tmpAssignmentCount > maxAssignmentForCurrentContract)
{
commIDRotationCalc.maxAllowed -= 1;
}
if (i == 0)
{
commIDRotationCalc.maxAllowed -= 1;
gridData = gData;
}
commIDRotationCalc.frequency = (int)Math.Round((double)(100/gData.rotationPercentage));
if (i == 1)
{
commIDRotationCalc.isNextToBeAssigned = true;
}
lstCommIDRotCalc.Add(commIDRotationCalc);
}
dictCodesheets.Add(key, lstCommIDRotCalc);
}else{
List<RotationCalculation> lstRotCalc = dictCodesheets[key];
for (int i = 0; i < lstRotCalc.Count; i++)
{
if (lstRotCalc[i].isNextToBeAssigned)
{
gridData = tmpLstGridData.Where(c => c.commercialID == lstRotCalc[i].commercialID).FirstOrDefault();
lstRotCalc[i].maxAllowed -= 1;
if (lstRotCalc.Count != 1)
{
if (i == lstRotCalc.Count - 1 && lstRotCalc[0].maxAllowed > 0)
{
//Debug.Print("In IF");
lstRotCalc[0].isNextToBeAssigned = true;
lstRotCalc[i].isNextToBeAssigned = false;
if (lstRotCalc[i].maxAllowed == 0)
{
lstRotCalc.RemoveAt(i);
}
break;
}
else
{
if (lstRotCalc[i + 1].maxAllowed > 0)
{
//Debug.Print("In ELSE");
lstRotCalc[i + 1].isNextToBeAssigned = true;
lstRotCalc[i].isNextToBeAssigned = false;
if (lstRotCalc[i].maxAllowed == 0)
{
lstRotCalc.RemoveAt(i);
}
break;
}
}
}
}
}
}
}
Edit 2
Trying to clear up my requirement here. Currently, because item 'a' is to be assigned 10 times which is the highest among all three items, towards the end of distribution, items 16 - 20 all have been assigned only 'a'. As has been asked in comments, I am trying to achieve a distribution that "looks" more even.
One way to look at this problem is as a multi-dimensional line drawing problem. So I used Bresenham's line algorithm to create the distribution:
public static IEnumerable<T> GetDistribution<T>( IEnumerable<Tuple<T, int>> itemCounts )
{
var groupCounts = itemCounts.GroupBy( pair => pair.Item1 )
.Select( g => new { Item = g.Key, Count = g.Sum( pair => pair.Item2 ) } )
.OrderByDescending( g => g.Count )
.ToList();
int maxCount = groupCounts[0].Count;
var errorValues = new int[groupCounts.Count];
for( int i = 1; i < errorValues.Length; ++i )
{
var item = groupCounts[i];
errorValues[i] = 2 * groupCounts[i].Count - maxCount;
}
for( int i = 0; i < maxCount; ++i )
{
yield return groupCounts[0].Item;
for( int j = 1; j < errorValues.Length; ++j )
{
if( errorValues[j] > 0 )
{
yield return groupCounts[j].Item;
errorValues[j] -= 2 * maxCount;
}
errorValues[j] += 2 * groupCounts[j].Count;
}
}
}
The input is the actual number of each item you want. This has a couple advantages. First it can use integer arithmetic, which avoids any rounding issues. Also it gets rid of any ambiguity if you ask for 10 items and want 3 items evenly distributed (which is basically just the rounding issue again).
Here's one with no random number that gives the required output.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// name, percentage
Dictionary<string, double> distribution = new Dictionary<string,double>();
// name, amount if one more were to be distributed
Dictionary<string, int> dishedOut = new Dictionary<string, int>();
//Initialize
int numToGive = 20;
distribution.Add("a", 0.50);
distribution.Add("b", 0.25);
distribution.Add("c", 0.25);
foreach (string name in distribution.Keys)
dishedOut.Add(name, 1);
for (int i = 0; i < numToGive; i++)
{
//find the type with the lowest weighted distribution
string nextUp = null;
double lowestRatio = double.MaxValue;
foreach (string name in distribution.Keys)
if (dishedOut[name] / distribution[name] < lowestRatio)
{
lowestRatio = dishedOut[name] / distribution[name];
nextUp = name;
}
//distribute it
dishedOut[nextUp] += 1;
Console.WriteLine(nextUp);
}
Console.ReadLine();
}
}
Instead of a truly random number generator, use a fixed seed, so that the program has the same output every time you run it (for the same input). In the code below, the '0' is the seed, which means the 'random' numbers generated will always be the same each time the program is run.
Random r = new Random(0);
//AABC AABC…
int totalA = 10
int totalB = 5
int totalC = 5
int totalItems = 20 //A+B+C
double frequencyA = totalA / totalItems; //0.5
double frequencyB = totalB / totalItems; //0.25
double frequencyC = totalC / totalItems; //0.25
double filledA = frequencyA;
double filledB = frequencyB;
double filledC = frequencyC;
string output = String.Empty;
while(output.Length < totalItems)
{
filledA += frequencyA;
filledB += frequencyB;
filledC += frequencyC;
if(filledA >= 1)
{
filledA -= 1;
output += "A";
if(output.Length == totalItems){break;}
}
if(filledB >= 1)
{
filledB -= 1
output += "B";
if(output.Length == totalItems){break;}
}
if(filledC >= 1)
{
filledC -= 1
output += "C";
if(output.Length == totalItems){break;}
}
}
This answer was mostly stolen and lightly adapted for your use from here
My idea is that you distribute your items in the simplest way possible without care of order, then shuffle the list.
public static void ShuffleTheSameWay<T>(this IList<T> list)
{
Random rng = new Random(0);
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Fiddle here

Defining boolean local variables

Note: This is my first time using "Stack Overflow" and I am relatively new to C#
(please excuse my poor programming skills at the moment)
My Code:
static void Main(string[] args)
{
Challenge(5, 12);
}
static void Challenge(int num1, int num2)
{
//finds the sum of the two variables
int sum = num1 + num2;
Console.WriteLine("The sum of {0} and {1} is...\n{2}", num1, num2, sum);
bool isDivisible = true;
//checks if divisible by 5 and sets a value for 'isDivisible'
if ((sum % 10 == 5) || (sum % 10 == 0))
{
Console.WriteLine("\nThe sum is divisible by 5!");
isDivisible = true;
}
else if ((sum % 10 != 5) || (sum % 10 != 0))
{
Console.WriteLine("\nThe sum is not divisible by 5!");
isDivisible = false;
}
//depending on value of 'isDivisible', returns certain functions
if (isDivisible == true)
{
Console.WriteLine("This value is usable.");
Console.WriteLine("\n\nThe remaining usable values are: ");
for (int newVal = sum + 1; newVal <= 55; newVal++) // '+ 1' added to make sure 'sum' is not printed again
{
if ((newVal % 10 == 5) || (newVal % 10 == 0))
{
Console.WriteLine(newVal);
}
}
}
else if (isDivisible == false)
{
Console.WriteLine("This value is not usable.");
Console.WriteLine("\n\nThese values are considered usable: ");
for (int newVal = 0; newVal <= 55; newVal++)
{
if ((newVal % 10 == 5) || (newVal % 10 == 0))
{
Console.WriteLine(newVal);
}
}
}
Console.ReadLine();
}
I viewed some articles online, as well as the "Stack Overflow" post: Why compile error "Use of unassigned local variable"? . After learning that local variables are not initialized (and must be given a value), I set my bool value for "isDivisible" equal to true by default.
Question:
Is there a better way to define a local variable that is of Boolean value (at least in the case of the program I am trying to run here)?
Thanks!
Your condition in the else if is wrong, it's not the complement of the first condition, so it doesn't make sense. The correct complement would be:
else if ((sum % 10 != 5) && (sum % 10 != 0))
However, you don't need to use an else if at all, you can just use an else as you want to catch every case that is not caught by the first condition. That also means that you don't have to initialise the boolean variable, as the compiler can see that it always will be set by one of the code blocks:
bool isDivisible;
//checks if divisible by 5 and sets a value for 'isDivisible'
if ((sum % 10 == 5) || (sum % 10 == 0))
{
Console.WriteLine("\nThe sum is divisible by 5!");
isDivisible = true;
}
else
{
Console.WriteLine("\nThe sum is not divisible by 5!");
isDivisible = false;
}
Side note: Instead of (sum % 10 == 5) || (sum % 10 == 0) you could just use sum % 5 == 0.
Side note 2: You don't need to compare a boolean variable to true, you can just use it as a condition. Also, you don't need the else if there either. Instead of this:
if (isDivisible == true)
{
...
}
else if (isDivisible == false)
{
...
}
you can use:
if (isDivisible)
{
...
}
else
{
...
}
Comparison expressions in C# will return a boolean value indicating whether or not they are true. So you could simplify your initial assignment to:
bool isDivisible = ((sum % 10 == 5) || (sum % 10 == 0));
Instead of explicitly setting it to true or false. Then your variable would always be set.
This doesn't work for all cases. Sometimes, it is difficult to reduce the comparison operation to a simple expression. But, it is often a handy way to initialize bools.

C# console app: How can I continue returning an error message until user enters valid data?

I'm building a method to calculate shipping cost. I've already validated the data type to an integer. As long as the integer entered is greater than 0, the logic works correctly. When the number entered is less than one, an error message is generated and repeats the request for a larger whole integer. Okay so far.
However, after the error message asks for a valid integer, the data entered is ignored and the calculation is incorrect. How can I repeat the request until the user enters a number greater than 0 and then perform the desired calculation with it? Thanks!
static double CalculateShipping(int items, double shippingCharge)
{
if (items == 1)
shippingCharge = 2.99;
else if (items > 1 && items < 6)
shippingCharge = 2.99 + 1.99 * (items - 1);
else if (items > 5 && items < 15)
shippingCharge = 10.95 + 1.49 * (items - 5);
else if (items > 14)
shippingCharge = 24.36 + 0.99 * (items - 14);
else
{
Console.WriteLine("You must order at least 1 item.");
Console.WriteLine();
Console.Write("Please enter a whole number greater than zero: ");
items = Console.Read();
}
return shippingCharge;
}
static int ReadItemsCountFromInput()
{
while(true)
{
Console.WriteLine("enter items count: ");
string s = Console.ReadLine();
int r;
if(int.TryParse(s, out r) && r > 0)
{
return r;
}
else
{
Console.WriteLine("you should enter number greater than zero");
}
}
}
static double CalculateShipping(int items, double shippingCharge)
{
if (items == 1)
shippingCharge = 2.99;
else if (items > 1 && items < 6)
shippingCharge = 2.99 + 1.99 * (items - 1);
else if (items > 5 && items < 15)
shippingCharge = 10.95 + 1.49 * (items - 5);
else if (items > 14)
shippingCharge = 24.36 + 0.99 * (items - 14);
return shippingCharge;
}
static void Main()
{
int items = ReadItemsCountFromInput();
double result = CalculateShipping(items, 0);
Console.WriteLine("Shipping: {0}", result);
}
Wrap your Console statements in a While loop. Something like...
bool MyValidationFlag = false;
While MyValidationFlag == False
{
// Prompt User
If (UserInput is an integer > 1)
MyValidationFlag = True
else
MyValidationFlag = False
}
(You might also want to throw in some kind of check for an "escape" value in case the user wants to quit.)

How do I check the validity of the Canadian Social Insurance Number in C#?

I've been given the assignment to write an algorithm in C# that checks the validity of a Canadian Social Insurance Number (SIN). Here are the steps to validate a SIN.
Given an example Number: 123 456 782
Remove the check digit (the last digit): 123456782
Extract the even digits (2,4,6,8th digith): 12345678
Double them:
2 4 6 8
| | | |
v v v v
4 8 12 16
Add the digits together: 4+8+1+2+1+6 = 22
Add the Odd placed digits: 1+3+5+7 = 16
Total : 38
Validity Algorithm
If the total is a multiple of 10, the check digit should be zero.
Otherwise, Subtract the Total from the next highest multiple of 10 (40 in this case)
The check digit for this SIN must be equal to the difference of the number and the totals from earlier (in this case, 40-38 = 2; check digit is 2, so the number is valid)
I'm lost on how to actually implement this in C#, how do I do this?
This is a nice problem to solve. This should be more efficient than converting to string and parsing back to integer. This solution will work on .NET 3.5 and later.
public static IEnumerable<int> ToDigitEnumerable(this int number)
{
IList<int> digits = new List<int>();
while(number > 0)
{
digits.Add(number%10);
number = number/10;
}
//digits are currently backwards, reverse the order
return digits.Reverse();
}
public static bool IsCanadianSocialInsuranceNumber(int number)
{
var digits = number.ToDigitEnumerable();
if (digits.Count() != 9) return false;
//The left side of the addition is adding all even indexes (except the last digit).
//We are adding even indexes since .NET uses base 0 for indexes
//The right side of the addition, multiplies the odd index's value by 2, then breaks each result into
//individual digits, then adds them together
var total = digits.Where((value, index) => index%2 == 0 && index != 8).Sum()
+ digits.Where((value, index) => index%2 != 0).Select(v => v*2)
.SelectMany(v => v.ToDigitEnumerable()).Sum();
//The final modulous 10 operator is to handle the scenarios where the total
//is divisble by 10, in those cases, the check sum should be 0, not 10
var checkDigit = (10 - (total%10)) % 10;
return digits.Last() == checkDigit;
}
One problem with this solution is that it assumes that number, represented as an integer, is 9 digits (can't start with a 0). If the number can start with a 0, then it has to be represented as a string (or converted to a string and padding with zeros). The logic to test will remain mostly intact, but the parts that assume integers will need to be swapped out with strings, and then you'll have to do parsing.
The specification you were given makes things a bit more complicated than they need to be: it's actually equivalent and simpler to just add the last digit into the checksum and make sure the checksum's last digit is 0.
The usual trouble new programmers have is "how do I get each digit?" Here's how:
In integer types, % 10 will delete everything but the last digit of the number: 123 % 10 == 3, and / 10 will delete the last digit of the number: 123 / 10 == 12.
In strings, str[i] - '0' will give you the digit at index i. The characters for digits are stored as special numbers: '0' is stored as 48 and '9' is stored as 57. If you subtract 48, you'll have the actual digit as a number. You don't really need to memorize "subtract 48", of course: if you just subtract '0', it will do the same thing: '8' - '0' == 8
Here are two efficient methods. One takes an int and checks the checksum of the SIN. One takes a string and checks both the format (must be "ddd ddd ddd") and the checksum of the SIN; though it's pretty efficient, it is a bit ugly and repetitive.
// Checks that the given int is a valid Canadian Social Insurance Number
// according to both range (000 000 000 to 999 999 998) and checksum.
public static bool IsValidSIN(int sin) {
if (sin < 0 || sin > 999999998) return false;
int checksum = 0;
for (int i = 4; i != 0; i--) {
checksum += sin % 10;
sin /= 10;
int addend = 2*(sin % 10); if (addend >= 10) addend -= 9;
checksum += addend;
sin /= 10;
}
return (checksum + sin) % 10 == 0;
}
// Checks that the given string is a valid Canadian Social Insurance Number
// according to both format ("ddd ddd ddd") and checksum.
// Implementation note: uses an admittedly ugly and repetitive parser.
public static bool IsValidSIN(string sin) {
if (sin.Length != 11) return false;
int checksum, addend;
checksum = sin[0] - '0';
if (checksum < 0 || checksum > 9) return false;
addend = 2*(sin[1] - '0'); if (addend >= 10) addend -= 9;
if (addend < 0 || addend > 9) return false;
checksum += addend;
addend = sin[2] - '0';
if (addend < 0 || addend > 9) return false;
checksum += addend;
if (sin[3] != ' ') return false;
addend = 2*(sin[4] - '0'); if (addend >= 10) addend -= 9;
if (addend < 0 || addend > 9) return false;
checksum += addend;
addend = sin[5] - '0';
if (addend < 0 || addend > 9) return false;
checksum += addend;
addend = 2*(sin[6] - '0'); if (addend >= 10) addend -= 9;
if (addend < 0 || addend > 9) return false;
checksum += addend;
if (sin[7] != ' ') return false;
addend = sin[8] - '0';
if (addend < 0 || addend > 9) return false;
checksum += addend;
addend = 2*(sin[9] - '0'); if (addend >= 10) addend -= 9;
if (addend < 0 || addend > 9) return false;
checksum += addend;
addend = sin[10] - '0';
if (addend < 0 || addend > 9) return false;
return (checksum + addend) % 10 == 0;
}
I don't know C# but here is a solution in Python. Maybe you can learn from it the method of how to implement it in C#.
def check(SIN):
SIN = ''.join(SIN.split(' '))
if len(SIN) != 9:
raise ValueError("A Canadian SIN must be 9 digits long")
check_digit = int(SIN[-1])
even_digits = [int(SIN[i]) for i in range(1,8,2)]
odd_digits = [int(SIN[i]) for i in range(0,8,2)]
total = sum(i/10 + i%10 for i in map(lambda x: 2*x, even_digits)) + sum(odd_digits)
if total%10 == 0:
return check_digit == 0
else:
return ((total/10)+1)*10 - total == check_digit
if __name__ == '__main__':
for SIN in ['123 456 782',
'123 456 789',
'046 454 286']:
print '%s is %sa valid Canadian SIN' % (SIN, '' if check(SIN) else 'NOT ')
which outputs:
123 456 782 is a valid Canadian SIN
123 456 789 is NOT a valid Canadian SIN
046 454 286 is a valid Canadian SIN
Search on the internet for "Luhn algorithm". You'll find a lot of examples.
The crux of the program is that you need to have some way of iterating over each integer in the SIN.
Since the easiest way is to convert the integer to a string for manipulation operations and back to an integer for addition/multiplication operations, I used the following approach:
The Program:
public class Program
{
static void Main(string[] args)
{
int sn = 123456782;
int[] Digits;
int AddedResult = 0;
string s = sn.ToString();
string sa = s.Substring(s.Length - 1, 1);
int checkDigit = Convert.ToInt32(sn.ToString().Substring(s.Length - 1, 1));
//get the last digit.
if (IsValidLength(sn))
{
sn = RemoveLastDigit(sn);
Digits = ExtractEvenDigits(sn);
Digits = DoubleDigits(Digits);
AddedResult = AddedEvenDigits(Digits);
AddedResult += AddOddDigits(sn);
if (IsValidSN(AddedResult, checkDigit))
{
Console.WriteLine("The number is valid");
}
else
{
Console.WriteLine("The Number is not valid");
}
}
else
{
Console.WriteLine("NotValidLength");
}
Console.Read();
}
public static bool IsValidSN(int AddedResult, int checkDigit)
{
return ((AddedResult % 10 == 0 && checkDigit == 0) || IsValidDifference(AddedResult, checkDigit));
}
public static bool IsValidDifference(int AddedResult, int checkDigit)
{
int nextHighestTens = AddedResult;
while (nextHighestTens % 10 != 0)
{
nextHighestTens++;
}
return ((nextHighestTens - AddedResult) == checkDigit);
}
public static int AddOddDigits(int sn)
{
string s = sn.ToString();
int i = 1;
int addedResult = 0;
foreach (char c in s)
{
if (i % 2 != 0)
{
addedResult += Convert.ToInt32(c.ToString());
}
i++;
}
return addedResult;
}
public static int AddedEvenDigits(int[] Digits)
{
int addedEvenDigits = 0;
string s = "";
for (int i = 0; i < Digits.Length; i++) //extract each digit. For example 12 is extracted as 1 and 2
{
s += Digits[i].ToString();
}
for (int i = 0; i < s.Length; i++) //now add all extracted digits
{
addedEvenDigits += Convert.ToInt32(s[i].ToString());
}
return addedEvenDigits;
}
public static int[] DoubleDigits(int[] Digits)
{
int[] doubledDigits = new int[Digits.Count()];
for (int i = 0; i < Digits.Length; i++)
{
doubledDigits[i] = Digits[i] * 2;
}
return doubledDigits;
}
public static int[] ExtractEvenDigits(int sn)
{
int[] EvenDigits = new int[4];
string s = sn.ToString(); //12345678
int j = 0;
for (int i = 1; i < s.Length; i += 2)
{
EvenDigits[j] = Convert.ToInt32(s[i].ToString());
j++;
}
return EvenDigits;
}
public static int RemoveLastDigit(int sn)
{
string s = sn.ToString();
return Convert.ToInt32(s.Substring(0, s.Count() - 1));
}
public static bool IsValidLength(int sn)
{
return (sn > 9999999 && sn < 1000000000);
}
}
I wrote this in about 20 minutes, so it's not really worthy to turn in. I plan on improving it as an exercise, and I wrote some unit tests for it (that I plan on making better).
[TestFixture]
public class SINTests
{
private int SinNumber = 123456782;
[Test]
public void TestValidNumber()
{
Assert.IsTrue(Program.IsValidLength(SinNumber));
}
[Test]
public void TestRemoveLastDigit()
{
Assert.AreEqual(12345678, Program.RemoveLastDigit(SinNumber));
}
[Test]
public void TestExtractEvenDigit()
{
int sn = 12345678;
int[] array = new int[] { 2,4,6,8 };
Assert.AreEqual(array, Program.ExtractEvenDigits(sn));
}
[Test]
public void TestAddOddDigits()
{
int sn = 12345678;
int result = 1 + 3 + 5 + 7;
Assert.AreEqual(result, Program.AddOddDigits(sn));
}
[Test]
public void TestDoubleEvenDigits()
{
int sn = 12345678;
int[] original = new int[] { 2, 4, 6, 8 };
int[] array = new int[] { 4, 8, 12, 16 };
Assert.AreEqual(array, Program.DoubleDigits(original));
}
[Test]
public void TestOddDigits()
{
int sn = 12345678;
Assert.AreEqual(16, Program.AddOddDigits(sn));
}
}
Since a string can be construed as an Array of Characters1, the operations that work on a string also need to be aware of the fact that Converting a character to an integer is different than converting a string to an integer. For instance:
Char c = '2';
int cInt = Convert.ToInt32(c); // returns 50
string s = c.ToString();
int sInt = Convert.ToInt32(s) //returns 2;
1Technically, a string is not an array of Characters in C# (though it is in C and C++), but because you can access the components of a string through an indexer, it can be treated like an array of characters.
I also recently coded this into an application. Before calling this, the string sSIN has already been checked through regex for being a 9-digit number.
public static bool IsCanadianSocialInsuranceNumber(string sSIN)
{
int iChecksum = 0;
int iDigit = 0;
for (int i = 0; i < sSIN.Length; i++)
{
// even number else odd
if (((i+1) % 2) == 0)
{
iDigit = int.Parse(sSIN.Substring(i, 1))*2;
iChecksum += (iDigit < 10) ? iDigit : iDigit - 9;
}
else
{
iChecksum += int.Parse(sSIN.Substring(i, 1));
}
}
return ((iChecksum % 10) == 0) ? true : false;
}
Here's a very simple way:
int test = 123456782;
if(test > 100000000 && test < 999999999)
{
int check = test % 10;
string temp = "";
foreach(char c in test.ToString().Substring(0, 8))
{
//The character codes for digits follow the same odd/even pattern as the digits.
//This code puts each digit or its value times 2, into a string and sums the digits
//after instead of keeping 2 separate totals
if(c % 2 == 1)
{
temp += c;
}
else
{
temp += (int.Parse(c.ToString()) * 2).ToString();
}
}
int temp2 = temp.Sum((x => int.Parse(x.ToString())));
//no need to compare the sum to the next 10, the modulus of 10 will work for this
int temp2mod = temp2 % 10;
if((temp2mod == 0 && temp2mod == check) || (10 - temp2mod == check))
return true;
}
return false;
public bool ValidateSIN(string sin)
{
if ((int)Char.GetNumericValue(sin[0]) == 0)
{
return false;
}
else
{
string evenString = "";
int totalOfEvens = 0;
int totalOfOdds = 0;
int total, nextMultipleOfTen, remainder;
int checkDigit = (int)Char.GetNumericValue(sin[8]);
// multiply each even number of the input string by 2
// get the resulting numbers into a string so the chars
// can be manipulated as individual digits
for (int i = 1; i <= 7; i += 2)
{
evenString += (Char.GetNumericValue(sin[i]) * 2);
}
// add the individual digits of the products from the above loop
foreach (char c in evenString)
{
totalOfEvens += (int)Char.GetNumericValue(c);
}
// get the odd numbers of the input string, minus the last number,
// and add them together
for (int i = 0; i <= 6; i += 2)
{
totalOfOdds += (int)Char.GetNumericValue(sin[i]);
}
total = totalOfEvens + totalOfOdds;
// take the quotient of total divided by 10 and add 1 to get the next multiple of ten
nextMultipleOfTen = (Math.DivRem(total, 10, out remainder) + 1) * 10;
if ((total % 10 == 0 && checkDigit == 0) || (checkDigit == nextMultipleOfTen - total))
{
return true;
}
else
{
return false;
}
}
}
Fastest method I've tried so far. No LINQ, no if/else, no odd/even checks, only 1 loop to get an array of integers from the string.
Caveat: no guards - input is assumed to be a string of 9 numbers.
public static bool IsValidSin(string input)
{
int[] luhnMap = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
int[] ints = new int[9];
for (int i = 0; i < 9; i++)
{
ints[i] = int.Parse(input[i].ToString());
}
int check = ints[0] + luhnMap[ints[1]] + ints[2] + luhnMap[ints[3]] + ints[4] + luhnMap[ints[5]] + ints[6] + luhnMap[ints[7]] + ints[8];
return (check % 10) == 0;
}

Categories