# Validating a CPR number

A Danish CPR number (Person Identification Number) is date of birth followed by 4 digits (the last one being a control digit): DDMMYY-XXXX

The final digit validates the CPR number using Modulo 11 by satisfying the following equation: where the are the ten digits of the complete ID number, and the coefficients (4, 3, 2, 7, …) are all nonzero in the finite field of order 11.

# Input

A CPR number formatted like this DDMMYY-XXXX where YY indicates a year within the last 100 years (between 1917 and 2016).

# Output

Indication whether CPR number is valid (1 or true) or invalid (0 or false)

# Examples

  290215-2353 --> false
140585-2981 --> true
311217-6852 --> true
121200-0004 --> false
140322-5166 --> false
111111-1118 --> true


# Scoring

This is - fewest bytes win!

# Date validation

All years refer to a period within the last 100 years. A valid date is a date that exists within it's calendar year.

290215 is not a valid date because 29/2 wasn't a date in 2015.

301216 is a valid date because 30/12 is a date in 2016.

• Can I receive the input as an array of the ten digits? – Leaky Nun Jul 20 '16 at 17:09
• Could you show how the first one evaluates to be false? – Leaky Nun Jul 20 '16 at 17:11
• @LeakyNun: Look at the date :) – Daniel Jul 20 '16 at 17:13
• What kind of date validation needs to be done? Do we have to check for leap years? Calendar changes? Check if the birthday is more recent than today? – Nathan Merrill Jul 20 '16 at 17:23
• I'm guessing that the symbol 0 represents the digit with decimal value 10, but that could be clearer. – Peter Taylor Jul 20 '16 at 21:33

# Python (2), 167156155142 141 bytes

from time import*
def f(s):
try:return 1>sum(int(i)*(ord(j)-48)for i,j in zip("43276504321",s))%11;strptime(s[:6],"%d%m%y")
except:return 0


tried to do the date validation without the library, but it's 168 bytes:

lambda s:sum(int(i)*(ord(j)-48)for i,j in zip("43276504321",s))%11==0 and int(s[0:2])<([0,32,(29,30)[int(s[4:6])%4==0],32]+[31,32]*2+[32,31]*2++*99)[int(s[2:4])]


-12 bytes thanks to @GáborFekete

-14 bytes thanks to @RootTwo

• You can golf some bytes by using s[:6] instead of s.split("-") and also by removing the space before the for. – Gábor Fekete Jul 20 '16 at 21:37
• thank you, now i just gotta save 1 byte somewhere to beat the php solution despite the horrendous date validation in Python. – KarlKastor Jul 20 '16 at 21:52
• Move the last line after the line starting with try using a semicolon. – Gábor Fekete Jul 20 '16 at 21:53
• Does that save characters? (you lose a " " but add a ";"). However, I had a similar idea before I read your comment (see above) – KarlKastor Jul 20 '16 at 22:01
• Great answer! When golfing, it is a good idea to keep your old byte counts by surrounding them with <s> and <\s> in the markdown editor, which adds a strikethrough. – TheBikingViking Jul 20 '16 at 22:07

# Ruby, 145113112 103 + 8 (-nrdate flags) = 111 bytes

Returns a date object (truthy) if it's a valid CPR, false if it fails the modulo test, and nil (falsy) if it fails the date test. Input is STDIN.

-33 bytes from @Jordan

s=0
$_.size.times{|i|s+=12340567234/10**i%10*$_[i].to_i}
p s%11<1&&Date.strptime($_,"%d%m%y")rescue p p  Version that returns literal true on valid CPR for +1 byte: s=0$_.size.times{|i|s+=12340567234/10**i%10*$_[i].to_i} p Date.strptime($_,"%d%m%y")&&s%11<1 rescue p p


Version that returns an error on invalid date instead of nil, for -10 bytes but probably invalid:

s=0
$_.size.times{|i|s+=12340567234/10**i%10*$_[i].to_i}
p Date.strptime($_,"%d%m%y")&&s%11<1  • Might I suggest Date.strptime("%d%m%y",$_)? – Jordan Jul 21 '16 at 21:08
• @Jordan I was about to say that it would cause errors for "121118" giving the year as 2018, but then I remembered that the leap years are all the same (except for 1900 which is out of the year scope anyways). Also, the arguments are in the reverse order so I fixed that. – Value Ink Jul 21 '16 at 21:38

# Mathematica, 130 bytes

DateObject[a={10#5+#6+4,10#3+#4,10#+#2}][]==a&&11∣Tr[{4,3,2,7,6,5,4,3,2,1}{##}]&@@IntegerDigits@FromDigits[#~StringDrop~{7}]&


Anonymous function. Takes a string as input and returns True or False as output. The Unicode character is U+2223 DIVIDES, representing \[Divides].

# Ruby + GNU date, 112111 103 bytes

-8 bytes from Kevin Lau.

->n{s=0
n.size.times{|i|s+=12340567234/10**i%10*n[i].to_i}
n=~/#{"(..)"*3}/
s%11<1&&date -d#$3#$2#$1>""}  Note that this prints e.g. "date: invalid date ‘150229’" to stderr if the date portion is invalid, but it still returns true or false (it doesn't raise an exception). If that's a disqualifier, though, I can add ⎵2>/dev/null for a 12-byte penalty. ## Ungolfed v = ->(cpr) { sum = 0 cpr.size.times do |idx| sum += 12340567234 / 10**idx % 10 * cpr[idx].to_i end cpr =~ /#{"(..)" * 3}/ sum % 11 < 1 && date -d#$3#$2#$1 > ""
}

• Errors are allowed? If so that cuts a lot of bytes off of my own Ruby solution :o – Value Ink Jul 21 '16 at 21:40
• @KevinLau-notKenny I think that's up to Daniel, hence the note. – Jordan Jul 21 '16 at 21:44
• FWIW this still returns true or false, even when the date is invalid. It doesn't raise an exception. – Jordan Jul 21 '16 at 21:46
• Hmm, that's true. I'm tempted to use your zip trick with the block but it feels like cheating and I'd probably outpace yours by using it... – Value Ink Jul 21 '16 at 21:54
• It's OK, I managed to tie you with an alternate solution (turns out the zip trick wasn't enough bytes saved). And now you can use my technique and make yours even shorter! – Value Ink Jul 21 '16 at 22:47