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? Jul 20, 2016 at 17:09
• Could you show how the first one evaluates to be false? Jul 20, 2016 at 17:11
• @LeakyNun: Look at the date :) Jul 20, 2016 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? Jul 20, 2016 at 17:23
• I'm guessing that the symbol 0 represents the digit with decimal value 10, but that could be clearer. Jul 20, 2016 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+[32]+[0]*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("-")[0] and also by removing the space before the for. Jul 20, 2016 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. Jul 20, 2016 at 21:52
• Move the last line after the line starting with try using a semicolon. Jul 20, 2016 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) Jul 20, 2016 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. Jul 20, 2016 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",$_)? Jul 21, 2016 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. Jul 21, 2016 at 21:38

Mathematica, 130 bytes

DateObject[a={10#5+#6+4,10#3+#4,10#+#2}][[1]]==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 Jul 21, 2016 at 21:40
• @KevinLau-notKenny I think that's up to Daniel, hence the note. Jul 21, 2016 at 21:44
• FWIW this still returns true or false, even when the date is invalid. It doesn't raise an exception. Jul 21, 2016 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... Jul 21, 2016 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! Jul 21, 2016 at 22:47