CREATE OR REPLACE FUNCTION @extschema@.luhn_verify(int8) RETURNS boolean AS $$ -- -- >>> SELECT @extschema@.luhn_verify(NULL) IS NULL -- true -- >>> SELECT @extschema@.luhn_verify(18) -- true -- >>> SELECT @extschema@.luhn_verify(50) -- false -- Take the sum of the -- doubled digits and the even-numbered undoubled digits, and see if -- the sum is evenly divisible by zero. SELECT -- Doubled digits might in turn be two digits. In that case, -- we must add each digit individually rather than adding the -- doubled digit value to the sum. Ie if the original digit was -- `6' the doubled result was `12' and we must add `1+2' to the -- sum rather than `12'. MOD(SUM(doubled_digit / INT8 '10' + doubled_digit % INT8 '10'), 10) = 0 FROM -- Double odd-numbered digits (counting left with -- least significant as zero). If the doubled digits end up -- having values > 10 (ie they're two digits), add their digits together. (SELECT -- Extract digit `n' counting left from least significant -- as zero MOD( ( $1::int8 / (10^n)::int8 ), 10::int8) -- Double odd-numbered digits * (MOD(n,2) + 1) AS doubled_digit FROM generate_series(0, floor(log( $1 ))::integer) AS n ) AS doubled_digits; $$ LANGUAGE SQL IMMUTABLE STRICT; COMMENT ON FUNCTION @extschema@.luhn_verify(int8) IS ' Return true if the last digit of the input is a correct check digit for the rest of the input according to Luhn''s algorithm. '; CREATE OR REPLACE FUNCTION @extschema@.luhn_generate_checkdigit(int8) RETURNS int8 AS $$ -- -- >>> SELECT @extschema@.luhn_generate_checkdigit(NULL) IS NULL -- true -- >>> SELECT @extschema@.luhn_generate_checkdigit(1) -- 8 -- >>> SELECT @extschema@.luhn_generate_checkdigit(1354646574456456546) -- 1 -- SELECT -- Add the digits, doubling even-numbered digits (counting left -- with least-significant as zero). Subtract the remainder of -- dividing the sum by 10 from 10, and take the remainder -- of dividing that by 10 in turn. ((INT8 '10' - SUM(doubled_digit / INT8 '10' + doubled_digit % INT8 '10') % INT8 '10') % INT8 '10')::INT8 FROM (SELECT -- Extract digit `n' counting left from least significant\ -- as zero MOD( ($1::int8 / (10^n)::int8), 10::int8 ) -- double even-numbered digits * (2 - MOD(n,2)) AS doubled_digit FROM pg_catalog.generate_series(0, floor(log( $1 ))::integer) AS n ) AS doubled_digits; $$ LANGUAGE SQL IMMUTABLE STRICT; COMMENT ON FUNCTION @extschema@.luhn_generate_checkdigit(int8) IS 'For the input value, generate a check digit according to Luhn''s algorithm'; CREATE OR REPLACE FUNCTION @extschema@.luhn_generate(int8) RETURNS int8 AS $$ -- -- >>> SELECT @extschema@.luhn_generate(NULL) IS NULL -- true -- >>> SELECT @extschema@.luhn_generate(8) -- 83 -- >>> SELECT @extschema@.luhn_generate(9999999999) -- 99999999990 -- SELECT 10 * $1 + @extschema@.luhn_generate_checkdigit($1); $$ LANGUAGE SQL IMMUTABLE STRICT; COMMENT ON FUNCTION @extschema@.luhn_generate(int8) IS 'Append a check digit generated according to Luhn''s algorithm to the input value. The input value must be no greater than (maxbigint/10).'; CREATE OR REPLACE FUNCTION @extschema@.luhn_strip(int8) RETURNS int8 AS $$ -- -- >>> SELECT @extschema@.luhn_strip(NULL) IS NULL -- true -- >>> SELECT @extschema@.luhn_strip(0) -- 0 -- >>> SELECT @extschema@.luhn_strip(12345) -- 1234 -- SELECT $1 / 10; $$ LANGUAGE SQL IMMUTABLE STRICT; COMMENT ON FUNCTION @extschema@.luhn_strip(int8) IS 'Strip the least significant digit from the input value. Intended for use when stripping the check digit from a number including a Luhn''s algorithm check digit.';