5
\$\begingroup\$

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:

mod11

where the enter image description here 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.

\$\endgroup\$
  • 1
    \$\begingroup\$ Can I receive the input as an array of the ten digits? \$\endgroup\$ – Leaky Nun Jul 20 '16 at 17:09
  • 1
    \$\begingroup\$ Could you show how the first one evaluates to be false? \$\endgroup\$ – Leaky Nun Jul 20 '16 at 17:11
  • 1
    \$\begingroup\$ @LeakyNun: Look at the date :) \$\endgroup\$ – Daniel Jul 20 '16 at 17:13
  • 4
    \$\begingroup\$ 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? \$\endgroup\$ – Nathan Merrill Jul 20 '16 at 17:23
  • 1
    \$\begingroup\$ I'm guessing that the symbol 0 represents the digit with decimal value 10, but that could be clearer. \$\endgroup\$ – Peter Taylor Jul 20 '16 at 21:33
2
\$\begingroup\$

Python (2), 167 156 155 142 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

\$\endgroup\$
  • 2
    \$\begingroup\$ You can golf some bytes by using s[:6] instead of s.split("-")[0] and also by removing the space before the for. \$\endgroup\$ – Gábor Fekete Jul 20 '16 at 21:37
  • \$\begingroup\$ thank you, now i just gotta save 1 byte somewhere to beat the php solution despite the horrendous date validation in Python. \$\endgroup\$ – KarlKastor Jul 20 '16 at 21:52
  • \$\begingroup\$ Move the last line after the line starting with try using a semicolon. \$\endgroup\$ – Gábor Fekete Jul 20 '16 at 21:53
  • \$\begingroup\$ Does that save characters? (you lose a " " but add a ";"). However, I had a similar idea before I read your comment (see above) \$\endgroup\$ – KarlKastor Jul 20 '16 at 22:01
  • \$\begingroup\$ 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. \$\endgroup\$ – TheBikingViking Jul 20 '16 at 22:07
2
\$\begingroup\$

Ruby, 145 113 112 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
\$\endgroup\$
  • \$\begingroup\$ Might I suggest Date.strptime("%d%m%y", $_)? \$\endgroup\$ – Jordan Jul 21 '16 at 21:08
  • \$\begingroup\$ @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. \$\endgroup\$ – Value Ink Jul 21 '16 at 21:38
1
\$\begingroup\$

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].

\$\endgroup\$
1
\$\begingroup\$

Ruby + GNU date, 112 111 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` > ""
}
\$\endgroup\$
  • \$\begingroup\$ Errors are allowed? If so that cuts a lot of bytes off of my own Ruby solution :o \$\endgroup\$ – Value Ink Jul 21 '16 at 21:40
  • \$\begingroup\$ @KevinLau-notKenny I think that's up to Daniel, hence the note. \$\endgroup\$ – Jordan Jul 21 '16 at 21:44
  • \$\begingroup\$ FWIW this still returns true or false, even when the date is invalid. It doesn't raise an exception. \$\endgroup\$ – Jordan Jul 21 '16 at 21:46
  • \$\begingroup\$ 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... \$\endgroup\$ – Value Ink Jul 21 '16 at 21:54
  • 1
    \$\begingroup\$ 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! \$\endgroup\$ – Value Ink Jul 21 '16 at 22:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.