I have found a PHP script github ISBN-Calc Routine to perform the ISBN-10 checksum calculation:
<?php
/**
* Calculate ISBN checksum
*
* #param string $isbn
* #return integer
*/
function isbn_checksum($isbn) {
$sum = 0; $isbn = str_split(preg_replace('/[^\d]/', '', $isbn));
foreach($isbn as $key => $z) {
if($key >= 12) break;
$sum += ($key % 2) ? $z * 3 : $z;
}
$checksum = (10 - $sum % 10);
return ($checksum == 10) ? 0 : $checksum;
}
But f.e for my ISBN-10: 0470173424 I get Checksum: 0with this github script.
Accoring to ISBN online checker the checksum should be 4 as is it in the ISBN. Can anyone here provide me with the correct PHP routine, please?
Thanks
That function is for calculating an ISBN-13 check digit, not ISBN-10 - that's why it breaks the loop after the 12th character.
The algorithm for ISBN-10 is different, and requires multiplying the first 9 digits of the number by 10 down to 2. The difference between that sum and the next multiple of 11 is the check-digit. For your example, this would be:
(10 * 0) +
(9 * 4) +
(8 * 7) +
(7 * 0) +
(6 * 1) +
(5 * 7) +
(4 * 3) +
(3 * 4) +
(2 * 2) = 161.
The next multiple of 11 is 165, so the check-digit should be 4 (as you say). In the case where the check-digit would be 10, X is used. We can model this in PHP like this:
function isbn10($isbn) {
$isbn = preg_replace('/[^\d]/', '', $isbn);
$digits = str_split(substr($isbn, 0, 9));
$sum = 0;
foreach ($digits as $index => $digit) {
$sum += (10 - $index) * $digit;
}
$check = 11 - ($sum % 11);
return ($check === 10) ? 'X' : $check;
}
echo isbn10('047017342');
4
You can see this working here: https://eval.in/1039654
The previously marked answer is close but incomplete.
Specifically this portion:
$check = 11 - ($sum % 11); // This can output 1,2,3,4,5,6,7,8,9,10,11 not 0
return ($check === 10) ? 'X' : $check; // This is incomplete does not address 11
The code does not deal with the situation where 11 - 0 = 11. I have tried to clarify it below.
function isbn10($isbn)
{
$isbn = preg_replace('/[^\d]/', '', $isbn);
$digits = str_split(substr($isbn, 0, 9));
$sum = 0;
foreach ($digits as $index => $digit)
{
$sum += (10 - $index) * $digit;
}
$check = 11 - ($sum % 11);
// $check may hold either 10 or 11, but not 0
// 10 becomes X, 11 becomes 0 -- output is 1 character only
if ($check == 10)
{
$check = 'X';
}
elseif ($check == 11)
{
$check = '0';
}
return $check;
}
An example ISBN where the earlier answer fails is 0134093410
There is an library from GitHub: https://github.com/Fale/isbn
There is a function called "Check":
Initialization:
$isbn = new Isbn\Isbn();
Check values: (Example)
$isbn->check->is10('888183718'); // Will return false
$isbn->check->is13('9788889527191'); // Will return true
$isbn->check->is13('978888952719'); // Will return false
You can download the library from the given link.
Maybe that helps a bit.
Have a nice weekend!
If you want to check if the ISBN-10 is correct
Validate ISBN-10
<?php
function isValidIsbn10($isbn) {
$check = 0;
for ($i = 0; $i < 10; $i++) {
if ('x' === strtolower($isbn[$i])) {
$check += 10 * (10 - $i);
} elseif (is_numeric($isbn[$i])) {
$check += (int)$isbn[$i] * (10 - $i);
} else {
return false;
}
}
return (0 === ($check % 11)) ? 1 : false;
}
var_dump( isValidIsbn10('0470173424') );
Source: https://stackoverflow.com/a/14096142/5201919
Will show
1 for true
Demo
https://eval.in/1053913
Related
I have some cases as follows.
1? 2?
?2? ??3
? ?
?5 ?0
Now what I am supposed to do is to find some values in place of question marks, that would give produce the minimum possible difference between the 2 numbers.
Answers Should be like
19 20
023 023
0 0
05 00
Note : the number which will be produced after the minimum absolute difference between the 2 values must be smallest. As in, the last case could be 15 and 10 with the absolute difference to be 5 but it is invalid.
I tried some permutation combination ideas for replacing the question marks for both numbers individually and then find out the number but the length of the number could go up to 18 digits per number. Hence I believe it wouldn't be a good idea.
Then I tried to search for similar questions but that didn't help.
I still think that regex could be helpful to solve this question but am stuck with how to do it.
Any help is welcome!! Thanx!
The language shall be Php.. I am working with Php.
Okay, I got a solution.
Explanation:
Uses regex to grab the two numbers, then compares them in pairs from left to right, starting with the assumption they're equal. Meaning they both resolve to the same number wherever possible, or 0 if they are both ?.
After there is a pair of numbers that aren't equal, it starts setting the lower ones ?'s to 9, and the higher ones ?'s to 0, to make them as close as possible.
Here is an example of it in action.
function minDiff($str) {
preg_match("/([\d\?]+) ([\d\?]+)/", $str, $matches);
$first = $matches[1];
$second = $matches[2];
$biggest = 0; // -1 = first, 0 = none, 1 = second
$firstResult = 0;
$secondResult = 0;
for ($i = 0; $i < strlen($first); $i++) {
$powerValue = strlen($first) - $i - 1;
if ($biggest != 0) { // not equal
if (!strcmp($first[$i], '?') && !strcmp($second[$i], '?')) {
if ($biggest > 0) { // second is biggest
$firstResult += 9 * pow(10, $powerValue);
} else { // first is biggest
$secondResult += 9 * pow(10, $powerValue);
}
} elseif (!strcmp($first[$i], '?')) {
if ($biggest > 0) { // second is biggest
$firstResult += 9 * pow(10, $powerValue);
}
$secondResult += $second[$i] * pow(10, $powerValue);
} elseif (!strcmp($second[$i], '?')) {
if ($biggest < 0) { // first is biggest
$secondResult += 9 * pow(10, $powerValue);
}
$firstResult += $first[$i] * pow(10, $powerValue);
} else {
$firstResult += $first[$i] * pow(10, $powerValue);
$secondResult += $second[$i] * pow(10, $powerValue);
}
} else { // both equal (so far)
if (!strcmp($first[$i], '?')) {
$firstResult += $second[$i] * pow(10, $powerValue);
$secondResult += $second[$i] * pow(10, $powerValue);
} elseif (!strcmp($second[$i], '?')) {
$firstResult += $first[$i] * pow(10, $powerValue);
$secondResult += $first[$i] * pow(10, $powerValue);
} else {
if (intval($first[$i]) > intval($second[$i])) {
$biggest = -1;
} elseif (intval($first[$i]) < intval($second[$i])) {
$biggest = 1;
}
$firstResult += $first[$i] * pow(10, $powerValue);
$secondResult += $second[$i] * pow(10, $powerValue);
}
// Find if next number will change
if (($i + 1) < strlen($first) && strcmp($first[$i + 1], '?') && strcmp($second[$i + 1], '?')) {
$diff = preg_replace('/\?/', '0', substr($first, $i + 1)) - preg_replace('/\?/', '0', substr($second, $i + 1));
echo "$diff\n";
// Check to see if you need to add 1 to the value for this loop
if ($diff > pow(10, $powerValue) / 2) {
$secondResult += pow(10, $powerValue);
$biggest = 1;
} elseif ($diff < pow(10, $powerValue) / -2) {
$firstResult += pow(10, $powerValue);
$biggest = -1;
}
}
}
}
echo "first: ".str_pad($firstResult, strlen($first), "0", STR_PAD_LEFT)."\n";
echo "second: ".str_pad($secondResult, strlen($second), "0", STR_PAD_LEFT)."\n\n";
}
Since i'm more of a "noob" at PHP than i'd hope, Is it possible to inverse the following function for example:
public function baseID($resourceid){
$rid = $resourceid;
$version = 0;
while ($rid > 16777216){
$version++;
if ($version == 1){
//the constant applied to all items
$rid -= 1342177280;
}elseif ($version == 2){
//the value added to the first updated version
$rid -= 50331648;
}else{
//the value added on all subsequent versions
$rid -= 16777216;
}
}
//$returnable = array('baseID'=>$rid,'version'=>$version);
return $rid;
}
Would it be possible to input the "baseID" and return a "resourceID" instead of the current way ?
I apologize if this is the wrong place to be asking such question
Not really. Your function returns the same value for all rids that are of the form:
5 * 2^28 + (3 + n) * 2^24
Where n is a positive integer.
>>> baseID(5 * 2**28 + (3 + 1) * 2**24)
16777216
>>> baseID(5 * 2**28 + (3 + 2) * 2**24)
16777216
>>> baseID(5 * 2**28 + (3 + 3) * 2**24)
16777216
So given just 16777216, you won't be able to determine what went into your function.
This function only works to a limited range, however at a certain point the resource IDs will start returning the same results.
public function resourceID($baseid){
$bid = $baseid;
$version = 0;
while ($bid <= 16777216){
++$version;
if ($version === 1){
$bid += 1342177280;
} elseif ($version === 2){
$bid += 50331648;
} else {
$bid += 16777216;
}
}
return $bid;
}
Does anybody know a PHP function for IMEI validation?
Short solution
You can use this (witchcraft!) solution, and simply check the string length:
function is_luhn($n) {
$str = '';
foreach (str_split(strrev((string) $n)) as $i => $d) {
$str .= $i %2 !== 0 ? $d * 2 : $d;
}
return array_sum(str_split($str)) % 10 === 0;
}
function is_imei($n){
return is_luhn($n) && strlen($n) == 15;
}
Detailed solution
Here's my original function that explains each step:
function is_imei($imei){
// Should be 15 digits
if(strlen($imei) != 15 || !ctype_digit($imei))
return false;
// Get digits
$digits = str_split($imei);
// Remove last digit, and store it
$imei_last = array_pop($digits);
// Create log
$log = array();
// Loop through digits
foreach($digits as $key => $n){
// If key is odd, then count is even
if($key & 1){
// Get double digits
$double = str_split($n * 2);
// Sum double digits
$n = array_sum($double);
}
// Append log
$log[] = $n;
}
// Sum log & multiply by 9
$sum = array_sum($log) * 9;
// Compare the last digit with $imei_last
return substr($sum, -1) == $imei_last;
}
Maybe can help you :
This IMEI number is something like this: ABCDEF-GH-IJKLMNO-X (without “-” characters)
For example: 350077523237513
In our example ABCDEF-GH-IJKLMNO-X:
AB is Reporting Body Identifier such as 35 = “British Approvals Board of Telecommunications (BABT)”
ABCDEF is Type Approval Code
GH is Final Assembly Code
IJKLMNO is Serial Number
X is Check Digit
Also this can help you : http://en.wikipedia.org/wiki/IMEI#Check_digit_computation
If i don't misunderstood, IMEI numbers using Luhn algorithm . So you can google this :) Or you can search IMEI algorithm
Maybe your good with the imei validator in the comments here:
http://www.php.net/manual/en/function.ctype-digit.php#77718
But I haven't tested it
Check this solution
<?php
function validate_imei($imei)
{
if (!preg_match('/^[0-9]{15}$/', $imei)) return false;
$sum = 0;
for ($i = 0; $i < 14; $i++)
{
$num = $imei[$i];
if (($i % 2) != 0)
{
$num = $imei[$i] * 2;
if ($num > 9)
{
$num = (string) $num;
$num = $num[0] + $num[1];
}
}
$sum += $num;
}
if ((($sum + $imei[14]) % 10) != 0) return false;
return true;
}
$imei = '868932036356090';
var_dump(validate_imei($imei));
?>
IMEI validation uses Luhn check algorithm. I found a link to a page where you can validate your IMEI. Furthermore, at the bottom of this page is a piece of code written in JavaScript to show how to calculate the 15th digit of IMEI and to valid IMEI. I might give you some ideas. You can check it out here http://imei.sms.eu.sk/index.html
Here is a jQuery solution which may be of use: https://github.com/madeinstefano/imei-validator
good fun from kasperhartwich
function validateImei($imei, $use_checksum = true) {
if (is_string($imei)) {
if (ereg('^[0-9]{15}$', $imei)) {
if (!$use_checksum) return true;
for ($i = 0, $sum = 0; $i < 14; $i++) {
$tmp = $imei[$i] * (($i%2) + 1 );
$sum += ($tmp%10) + intval($tmp/10);
}
return (((10 - ($sum%10)) %10) == $imei[14]);
}
}
return false;
}
Does anyone know how to convert a number such as 1, 2, or 3 to their text version (one, two, three) in PHP? I only need to convert from 1 to 99. I know I could write a huge switch statement but that would be ridiculous.
pear has a package Numbers_Words:
$numberToWord = new Numbers_Words();
echo $numberToWords->toWords(200);
Not really ideal, but atleast better than a 'huge switch statement':
$numbermappings = array("zero", "one","two","three", "four" .... "ninetynine");
echo $numbermappings[4]; // four
You still have to write that huge array though..
Here's one I wrote way back in college. It includes support for negative numbers, as well. I know there's some ways it could be shortened and/or cleaned up, but hey, it works well for any integer!
/**
Converts an integer to its textual representation.
#param num the number to convert to a textual representation
#param depth the number of times this has been recursed
*/
function readNumber($num, $depth=0)
{
$num = (int)$num;
$retval ="";
if ($num < 0) // if it's any other negative, just flip it and call again
return "negative " + readNumber(-$num, 0);
if ($num > 99) // 100 and above
{
if ($num > 999) // 1000 and higher
$retval .= readNumber($num/1000, $depth+3);
$num %= 1000; // now we just need the last three digits
if ($num > 99) // as long as the first digit is not zero
$retval .= readNumber($num/100, 2)." hundred\n";
$retval .=readNumber($num%100, 1); // our last two digits
}
else // from 0 to 99
{
$mod = floor($num / 10);
if ($mod == 0) // ones place
{
if ($num == 1) $retval.="one";
else if ($num == 2) $retval.="two";
else if ($num == 3) $retval.="three";
else if ($num == 4) $retval.="four";
else if ($num == 5) $retval.="five";
else if ($num == 6) $retval.="six";
else if ($num == 7) $retval.="seven";
else if ($num == 8) $retval.="eight";
else if ($num == 9) $retval.="nine";
}
else if ($mod == 1) // if there's a one in the ten's place
{
if ($num == 10) $retval.="ten";
else if ($num == 11) $retval.="eleven";
else if ($num == 12) $retval.="twelve";
else if ($num == 13) $retval.="thirteen";
else if ($num == 14) $retval.="fourteen";
else if ($num == 15) $retval.="fifteen";
else if ($num == 16) $retval.="sixteen";
else if ($num == 17) $retval.="seventeen";
else if ($num == 18) $retval.="eighteen";
else if ($num == 19) $retval.="nineteen";
}
else // if there's a different number in the ten's place
{
if ($mod == 2) $retval.="twenty ";
else if ($mod == 3) $retval.="thirty ";
else if ($mod == 4) $retval.="forty ";
else if ($mod == 5) $retval.="fifty ";
else if ($mod == 6) $retval.="sixty ";
else if ($mod == 7) $retval.="seventy ";
else if ($mod == 8) $retval.="eighty ";
else if ($mod == 9) $retval.="ninety ";
if (($num % 10) != 0)
{
$retval = rtrim($retval); //get rid of space at end
$retval .= "-";
}
$retval.=readNumber($num % 10, 0);
}
}
if ($num != 0)
{
if ($depth == 3)
$retval.=" thousand\n";
else if ($depth == 6)
$retval.=" million\n";
if ($depth == 9)
$retval.=" billion\n";
}
return $retval;
}
****See this function in action:****
function N2L($number)
{
$result = array();
$tens = floor($number / 10);
$units = $number % 10;
$words = array
(
'units' => array('', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen'),
'tens' => array('', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety')
);
if ($tens < 2)
{
$result[] = $words['units'][$tens * 10 + $units];
}
else
{
$result[] = $words['tens'][$tens];
if ($units > 0)
{
$result[count($result) - 1] .= '-' . $words['units'][$units];
}
}
if (empty($result[0]))
{
$result[0] = 'Zero';
}
return trim(implode(' ', $result));
}
If you have the NumberFormatter class from intl at hand (which you'll surely do in PHP>5.3):
$nf = new NumberFormatter("en", NumberFormatter::SPELLOUT);
for ($i=0;$i<100;$i++) echo $nf->format($i)."\n";
Of course, that also works over 99, and for any language you need (just change the first parameter from "en" to whatever)
There's a PEAR package that does this. It does number WAY higher than 99 and is multi-language, so it might be more heavyweight than you need, but still worth checking out:
http://pear.php.net/package/Numbers_Words
I ended up having to write this for a coding test during an interview process. You can see my final code at Github here: https://github.com/mangs/integers2words
For convenience, here is the DemoLibrary class that implements this int2str() functionality (all class members are there only to support the int2str() functionality):
<?php
/**
* Demo library class intended to be added to in the future
*/
class DemoLibrary {
/***** NOTE: a const cannot be an array in PHP, so making these arrays static is the next best thing *****/
/**
* #var array $_numbersUnder20 Array containing the word associated with the index's number value
*/
private static $_numbersUnder20 = [
'zero', 'one', 'two', 'three', 'four',
'five', 'six', 'seven', 'eight', 'nine',
'ten', 'eleven', 'twelve', 'thirteen', 'fourteen',
'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'
];
/**
* #var array $_tensDigits Array containing all tens digit values except 10
*/
private static $_tensDigits = [
'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'
];
/**
* #var array $_orderOfMagnitude Array containing the higher-order digit values; can also be
* thought of as the order of magnitude of the target digit
*/
private static $_orderOfMagnitude = [
// Stopped at "quintillion" because the maximum PHP int value on 64-bit Linux is
// 9,223,372,036,854,775,807 (a.k.a. 2^63 - 1 because PHP doesn't support unsigned ints)
'thousand', 'million', 'billion', 'trillion', 'quadrillion', 'quintillion'
];
/**
* Recursively calculates the string-, word-based equivalent of the target integer
*
* #param integer $num Integer whose value will be converted to a word-based string
* #param boolean $recursive Determines if the currently-executing code is being called
* recursively; allows for non-recursive 0 to be converted to "zero"
* otherwise use an empty string
*
* #throws InvalidArgumentException if the first argument is not of type int
*
* #return string Partially- or fully-built word-based representation of the target integer
*/
private function _integerToWords($num, $recursive=false) {
// Ensure a valid integer
if(!is_int($num)) {
throw new InvalidArgumentException(
__FUNCTION__ . ' expects parameter 1 to be of type integer; actual type: ' .
gettype($num)
);
}
/***** Perform the int to string conversion based on the size of $num *****/
// Negative
if($num < 0) {
return 'negative ' . $this->_integerToWords(-1 * $num, true);
}
// 0 or no value in the lowest digits
if($num === 0) {
return $recursive ? '' : 'zero';
}
// 1-19
if($num < 20) {
return self::$_numbersUnder20[$num];
}
// 20 - 99
if($num < 100) {
$highDigitValue = intval(floor($num / 10) - 2); // Value of the highest-order digit
$remainingValue = $num % 10; // Value of the remaining digits
return
self::$_tensDigits[$highDigitValue] .
'-' .
$this->_integerToWords($remainingValue, true);
}
// 100 - 999
if($num < 1000) {
$highDigitValue = intval(floor($num / 100)); // Value of the highest-order digit
$remainingValue = $num % 100; // Value of the remaining digits
return
$this->_integerToWords($highDigitValue, true) .
'-hundred ' .
$this->_integerToWords($remainingValue, true);
}
// 1,000+
$quotient = $num;
$divideCount = 0;
while($quotient >= 1000) {
$quotient /= 1000;
++$divideCount;
}
$highDigitValue = intval(floor($quotient)); // Value of the highest-order digit
$remainingValue = $num - ($highDigitValue * pow(1000, $divideCount)); // Value of the remaining digits
return
$this->_integerToWords($highDigitValue, true) .
'-' .
self::$_orderOfMagnitude[$divideCount - 1] .
' ' .
$this->_integerToWords($remainingValue, true);
}
/**
* #api
*
* Calculates the string-, word-based equivalent of the target integer
*
* #param integer $num Integer whose value will be converted to a word-based string
*
* #return string Fully-built word-based representation of the target integer
*/
public function int2str($num) {
return trim($this->_integerToWords($num), "- \t\n\r\0\x0B");
}
}
There are lots of implementations for validating Luhn checksums but very few for generating them. I've come across this one however in my tests it has revealed to be buggy and I don't understand the logic behind the delta variable.
I've made this function that supposedly should generated Luhn checksums but for some reason that I haven't yet understood the generated checksums are invalid half of the time.
function Luhn($number, $iterations = 1)
{
while ($iterations-- >= 1)
{
$stack = 0;
$parity = strlen($number) % 2;
$number = str_split($number, 1);
foreach ($number as $key => $value)
{
if ($key % 2 == $parity)
{
$value *= 2;
if ($value > 9)
{
$value -= 9;
}
}
$stack += $value;
}
$stack = 10 - $stack % 10;
if ($stack == 10)
{
$stack = 0;
}
$number[] = $stack;
}
return implode('', $number);
}
Some examples:
Luhn(3); // 37, invalid
Luhn(37); // 372, valid
Luhn(372); // 3728, invalid
Luhn(3728); // 37283, valid
Luhn(37283); // 372837, invalid
Luhn(372837); // 3728375, valid
I'm validating the generated checksums against this page, what am I doing wrong here?
For future reference, here is the working function.
function Luhn($number, $iterations = 1)
{
while ($iterations-- >= 1)
{
$stack = 0;
$number = str_split(strrev($number), 1);
foreach ($number as $key => $value)
{
if ($key % 2 == 0)
{
$value = array_sum(str_split($value * 2, 1));
}
$stack += $value;
}
$stack %= 10;
if ($stack != 0)
{
$stack -= 10;
}
$number = implode('', array_reverse($number)) . abs($stack);
}
return $number;
}
I dropped the $parity variable since we don't need it for this purpose, and to verify:
function Luhn_Verify($number, $iterations = 1)
{
$result = substr($number, 0, - $iterations);
if (Luhn($result, $iterations) == $number)
{
return $result;
}
return false;
}
Edit: Sorry, I realize now that you had almost my entire answer already, you had just incorrectly determined which factor to use for which digit.
My entire answer now can be summed up with this single sentence:
You have the factor reversed, you're multiplying the wrong digits by 2 depending on the length of the number.
Take a look at the Wikipedia article on the Luhn algorithm.
The reason your checksum is invalid half the time is that with your checks, half the time your number has an odd number of digits, and then you double the wrong digit.
For 37283, when counting from the right, you get this sequence of numbers:
3 * 1 = 3 3
8 * 2 = 16 --> 1 + 6 = 7
2 * 1 = 2 2
7 * 2 = 14 --> 1 + 4 = 5
+ 3 * 1 = 3 3
= 20
The algorithm requires you to sum the individual digits from the original number, and the individual digits of the product of those "every two digits from the right".
So from the right, you sum 3 + (1 + 6) + 2 + (1 + 4) + 3, which gives you 20.
If the number you end up with ends with a zero, which 20 does, the number is valid.
Now, your question hints at you wanting to know how to generate the checksum, well, that's easy, do the following:
Tack on an extra zero, so your number goes from xyxyxyxy to xyxyxyxy0
Calculate the luhn checksum sum for the new number
Take the sum, modulus 10, so you get a single digit from 0 to 10
If the digit is 0, then congratulations, your checksum digit was a zero
Otherwise, calculate 10-digit to get what you need for the last digit, instead of that zero
Example: Number is 12345
Tack on a zero: 123450
Calculate the luhn checksum for 123450, which results in
0 5 4 3 2 1
1 2 1 2 1 2 <-- factor
0 10 4 6 2 2 <-- product
0 1 0 4 6 2 2 <-- sum these to: 0+1+0+4+6+2+2=15
Take the sum (15), modulus 10, which gives you 5
Digit (5), is not zero
Calculate 10-5, which gives you 5, the last digit should be 5.
So the result is 123455.
your php is buggy, it leads into an infinite loop.
This is the working version that I'm using, modified from your code
function Luhn($number) {
$stack = 0;
$number = str_split(strrev($number));
foreach ($number as $key => $value)
{
if ($key % 2 == 0)
{
$value = array_sum(str_split($value * 2));
}
$stack += $value;
}
$stack %= 10;
if ($stack != 0)
{
$stack -= 10; $stack = abs($stack);
}
$number = implode('', array_reverse($number));
$number = $number . strval($stack);
return $number;
}
Create a php and run in your localhost Luhn(xxxxxxxx) to confirm.
BAD
I literally cannot believe how many crummy implementations there are out there.
IDAutomation has a .NET assembly with a MOD10() function to create but it just doesn't seem to work. In Reflector the code is way too long for what it's supposed to be doing anyway.
BAD
This mess of a page which is actually currently linked to from Wikipedia(!) for Javascript has several verification implementations that don't even return the same value when I call each one.
GOOD
The page linked to from Wikipedia's Luhn page has a Javascript encoder which seems to work :
// Javascript
String.prototype.luhnGet = function()
{
var luhnArr = [[0,1,2,3,4,5,6,7,8,9],[0,2,4,6,8,1,3,5,7,9]], sum = 0;
this.replace(/\D+/g,"").replace(/[\d]/g, function(c, p, o){
sum += luhnArr[ (o.length-p)&1 ][ parseInt(c,10) ]
});
return this + ((10 - sum%10)%10);
};
alert("54511187504546384725".luhnGet());
GOOD
This very useful EE4253 page verifies the check-digit and also shows the full calculation and explanation.
GOOD
I needed C# code and ended up using this code project code:
// C#
public static int GetMod10Digit(string data)
{
int sum = 0;
bool odd = true;
for (int i = data.Length - 1; i >= 0; i--)
{
if (odd == true)
{
int tSum = Convert.ToInt32(data[i].ToString()) * 2;
if (tSum >= 10)
{
string tData = tSum.ToString();
tSum = Convert.ToInt32(tData[0].ToString()) + Convert.ToInt32(tData[1].ToString());
}
sum += tSum;
}
else
sum += Convert.ToInt32(data[i].ToString());
odd = !odd;
}
int result = (((sum / 10) + 1) * 10) - sum;
return result % 10;
}
GOOD
This validation code in C# seems to work, if a little unwieldy. I just used it to check the above was correct.
There's now a github repo based on the original question/answer. See
https://github.com/xi-project/xi-algorithm
It's also available at packagist
This is a function that could help you, it's short and it works just fine.
function isLuhnValid($number)
{
if (empty($number))
return false;
$_j = 0;
$_base = str_split($number);
$_sum = array_pop($_base);
while (($_actual = array_pop($_base)) !== null) {
if ($_j % 2 == 0) {
$_actual *= 2;
if ($_actual > 9)
$_actual -= 9;
}
$_j++;
$_sum += $_actual;
}
return $_sum % 10 === 0;
}
Since the other answers that displayed or linked to C# weren't working, I've added a tested and more explanatory C# version:
/// <summary>
/// Calculates Luhn Check Digit based on
/// https://en.wikipedia.org/wiki/Luhn_algorithm
/// </summary>
/// <param name="digits">The digits EXCLUDING the check digit on the end.
/// The check digit should be compared against the result of this method.
/// </param>
/// <returns>The correct checkDigit</returns>
public static int CalculateLuhnCheckDigit(int[] digits)
{
int sum = 0;
bool isMultiplyByTwo = false;
//Start the summing going right to left
for (int index = digits.Length-1; index >= 0; --index)
{
int digit = digits[index];
//Every other digit should be multipled by two.
if (isMultiplyByTwo)
digit *= 2;
//When the digit becomes 2 digits (due to digit*2),
//we add the two digits together.
if (digit > 9)
digit = digit.ToString()
.Sum(character => (int)char.GetNumericValue(character));
sum += digit;
isMultiplyByTwo = !isMultiplyByTwo;
}
int remainder = sum % 10;
//If theres no remainder, the checkDigit is 0.
int checkDigit = 0;
//Otherwise, the checkDigit is the number that gets to the next 10
if (remainder != 0)
checkDigit = 10 - (sum % 10);
return checkDigit;
}
An example of its use:
public static bool IsValid(string userValue)
{
//Get the check digit from the end of the value
int checkDigit = (int)char.GetNumericValue(userValue[userValue.Length - 1]);
//Remove the checkDigit for the luhn calculation
userValue = userValue.Substring(0, userValue.Length - 1);
int[] userValueDigits = userValue.Select(ch => (int)char.GetNumericValue(ch))
.ToArray();
int originalLuhnDigit = CalculateLuhnCheckDigit(userValueDigits);
//If the user entered check digit matches the calcuated one,
//the number is valid.
return checkDigit == originalLuhnDigit;
}
The parity check must start from the right.
Try this:
<?php
function Luhn($digits) {
$sum = 0;
foreach (str_split(strrev($digits)) as $i => $digit) {
$sum += ($i % 2 == 0) ? array_sum(str_split($digit * 2)) : $digit;
}
return $digits . (10 - ($sum % 10)) % 10;
}
Add Luhn checksum to $input
$digits = Luhn($input);
Verify a number with Luhn checksum in it:
if ($digits == Luhn(substr($digits, 0, -1))) {
// ...
}
Get the checksum number:
$luhn_digit = substr(Luhn($digits), -1);
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
int *LONT, n, TARF;
int SEGVT = 0;
int SEGVT2 = 0;
string TARJETA;
double VA;
cout << "cuantos digitos tiene la tarjeta: " << endl;
cin >> n;
LONT = new int[n];
do {
cout << "ingrese el # de la tarjeta: " << endl;
cin >> TARJETA;
VA = stod(TARJETA);
} while (VA < 0);
for (int POS = 0; POS < TARJETA.size(); POS++) {
LONT[POS] = TARJETA[POS] - '0';
}
for (int i = 0; i < n; i++) {
if (i % 2 == 0) {
LONT[i] = TARJETA[i] - '0';
LONT[i] = LONT[i] * 2;
if (LONT[i] >= 10) {
LONT[i] = LONT[i] - 9;
}
SEGVT2 = SEGVT2 + LONT[i];
}
else
{
LONT[i] = TARJETA[i] - '0';
SEGVT = SEGVT + LONT[i];
}
}
TARF = SEGVT + SEGVT2;
if (TARF % 10 == 0) {
cout << SEGVT2 << SEGVT;
cout << "El numero de tarjeta " << TARJETA << "; Es de una tarjeta valida (YA QUE SU MOD10 ES " << TARF << endl;
}
else
{
cout << SEGVT2 << SEGVT;
cout << "El numero de tarjeta" << TARJETA << "; No es de una tarjeta valida (YA QUE SU MOD10 ES " << TARF << endl;
}
delete[] LONT;
}