What characters are more common in my MD2 hash?

11

The challenge is simple

Write a script that, when given a string input, will hash the string using the MD2 hashing algorithm, and then return either a positive integer or negative integer output based on which character set below is more common in the resulting hash as a hexadecimal string:

01234567 - (positive)
89abcdef - (negative)
  • The input will always be a string, but may be of any length up to 65535
  • The entire input, whitespace and all, must be hashed
  • For the purposes of this challenge, the integer 0 is considered neither positive nor negative (see tie output)
  • The more common set is the one who's characters are more common within the 32 character hexadecimal hash string
  • Your output may contain trailing whitespace of any kind, as long as the only non-whitespace characters are a valid truthy or falsey output
  • In the event of a tie, where the hexadecimal string contains exactly 16 characters from each set, the program should output a 0

I/O Examples

Input: "" (Empty String)
Hash: 8350e5a3e24c153df2275c9f80692773
Output: 1

Input: "The quick brown fox jumps over the lazy cog" (Without quotes)
Hash: 6b890c9292668cdbbfda00a4ebf31f05
Output: -1

Input: "m" (Without quotes)
Hash: f720d455eab8b92f03ddc7868a934417
Output: 0

Winning Criterion

This is , fewest bytes wins!

Skidsdev

Posted 2017-04-19T15:11:42.273

Reputation: 9 656

1It would be good to link to or ideally explain the MD2 hashing algorithm in the challenge specification to make it self contained. – Martin Ender – 2017-04-19T15:28:02.277

@MartinEnder Will do! – Skidsdev – 2017-04-19T15:29:58.500

I think it would be fair to simply accept three distinct values for win, lose, and tie – math junkie – 2017-04-19T15:45:12.883

@mathjunkie true, probably shouldn't be changing the spec so much, but I guess just having 1, 0 or -1 is the best way – Skidsdev – 2017-04-19T15:47:35.757

Hi there! Since others haven't mentioned it yet, I'll toss out a recommendation to use the Sandbox for future challenges, where you can get meaningful feedback and tighten up the challenge before posting on Main.

– AdmBorkBork – 2017-04-19T15:56:20.497

@AdmBorkBork Ah thanks! Had I know about that I'd've definitely done so before posting it, will keep that in mind for the future, thanks! – Skidsdev – 2017-04-19T15:59:42.417

"are a valid truthy or falsey output" Shouldn't it be a valid integer...? "Your output may contain trailing whitespace of any kind, as long as the only non-whitespace characters..." The first part says trailing whitespace is allowed, but the second part implies leading and trailing whitespace are allowed. Which is it? – jpmc26 – 2017-04-19T22:15:29.133

2

This strikes me as a chameleon challenge. Either your language has a build-in or library to do MD2 and the rest is simple character counting, or it doesn't and you have to implement it yourself.

– xnor – 2017-04-20T00:20:24.460

@xnor A lot of challenges are trivial in some language and difficult in others. Take the reciprocal challenge for instance... Many languages already have a function for this or at least support division. Some answers are in languages that don't and those answers seem to generally get more attention. It all comes down to not upvoting trivial solutions

– Poke – 2017-04-20T13:41:35.590

Answers

1

Octave, 35 bytes

@(s)diff(hist(hash('md2',s),+'78'))

*Requires the latest version of Octave (at least 4.2).

Computes histcounts of the hash string with it's center of bins are 7 and 8 then computes the difference of counts.

rahnema1

Posted 2017-04-19T15:11:42.273

Reputation: 5 435

Given it's been a few days I'll put yours up as the winning answer, if somebody comes along later with a shorter solution I can always change it. Well done! – Skidsdev – 2017-04-21T13:02:55.450

@Mayube Thanks! – rahnema1 – 2017-04-21T16:35:46.337

8

Mathematica, 43 bytes

Tr@Sign[15-2#~Hash~"MD2"~IntegerDigits~16]&

Outputs the number of digits in 01234567 minus the number of digits in 89abcdef.

Martin Ender

Posted 2017-04-19T15:11:42.273

Reputation: 184 808

1Too bad that 3E is between 8 and 9 and not between 7 and 8. :| – Martin Ender – 2017-04-19T15:54:55.097

8

JavaScript (ES6), 731 bytes

This monster is implementing the MD2 algorithm, so it's embarrassingly long. Based on js-md2 by Chen Yi-Cyuan.

let f =

m=>{L=x=s=b=0,n=m.length,M=[],X=[],C=[],S=[...atob`KS5DyaLYfAE9NlSh7PAGE2KnBfPAx3OMmJMr2bxMgsoem1c8/dTgFmdCbxiKF+USvk7E1tqe3kmg+/WOuy/ueqloeZEVsgc/lMIQiQsiXyGAf12aWpAyJzU+zOe/95cD/xkws0iltdHXXpIqrFaqxk+4ONKWpH22dvxr4px0BPFFnXBZZHGHIIZbz2XmLagCG2Alra6wufYcRmFpNEB+D1VHoyPdUa86w1z5zrrF6iYsUw1uhSiECdPfzfRBgU1Satw3yGzBq/ok4XsIDL2xSniIlYvjY+ht6cvV/jsAHTny77cOZljQ5KZ3cvjrdUsKMURQtI/tHxrbmY0znxGDFA`].map(c=>c[O='charCodeAt']());for(l=1;l-2;){for(j=19;j--;)M[j]=M[16+j]||0;for(i=s;i<16;x++)L=(x-n||(b+=i-s,s=i-16,l=2),C[i]^=S[(M[i++]=x<n?m[O](x):16-(b&15))^L]);for(i=0;i<l;i++){for(j=16;j--;)X[32+j]=(X[16+j]=(i?C:M)[j])^X[j];for(t=j=0;j<18;t=t+j++&255)for(k=0;k<48;)t=X[k++]^=S[t]}}for(i=16,n=-i;i--;)n+=!(X[i]&8)+!(X[i]&128);return n}

console.log(f(''))
console.log(f('The quick brown fox jumps over the lazy cog'))
console.log(f('m'))

Arnauld

Posted 2017-04-19T15:11:42.273

Reputation: 111 334

Beat me to it. Really nice effort. – Luke – 2017-04-20T06:13:22.227

Props for, thus far, being the only one to actually implement the full MD2 algorithm rather than using built-in functions. – Skidsdev – 2017-04-20T09:50:46.930

Highest byte answer deserving of more points. – Magic Octopus Urn – 2017-04-20T13:51:24.233

5

Python 2 + Crypto, 108 99 93 91 87 78 bytes

Python doesn't have a native builtin for MD2.

from Crypto.Hash import*
lambda s:sum(x<'8'for x in MD2.new(s).hexdigest())-16

Saved 12 bytes thanks to @ovs.
Saved 9 bytes thanks to @FelipeNardiBatista.

mbomb007

Posted 2017-04-19T15:11:42.273

Reputation: 21 944

lambda s:cmp(sum((int(x,16)<8)-.5for x in MD2.new(s).hexdigest()),0) should reduce the byte count to 93 – ovs – 2017-04-19T17:45:54.510

@ovs Very clever! – mbomb007 – 2017-04-19T20:08:38.927

sum(x<'8'for x ...... – Felipe Nardi Batista – 2017-04-20T12:35:15.893

lambda s:sum(x<'8'for x in MD2.new(s).hexdigest())-16 for 78. the output can be any number, not just -1,0,1 – Felipe Nardi Batista – 2017-04-20T12:45:28.927

4

Java 8, 173 bytes

-4 thanks to dzaima

-128 thanks to Oliver, this is basically his answer now.

a->{String h="";for(byte b:java.security.MessageDigest.getInstance("MD2").digest(a.ge‌​tBytes()))h+=h.forma‌​t("%02x",b);return h.codePoints().filter(c->c>47&&c<56).count()-16;}

Positive for truthy. Negative for falsy. 0 for 0.

Magic Octopus Urn

Posted 2017-04-19T15:11:42.273

Reputation: 19 422

1You can save 4 bytes by removing the enclosing brackets of for and if – dzaima – 2017-04-19T16:48:36.287

1bytes to hex can be golfed: String s="";for(byte b:bytes)h+=h.format("%02x",b);. Also, you don't need to write a full program, but a lambda suffice: a->{... return x;}. Finally the for loop can be replaced by int x=s.codePoints().filter(c->c>47&&c<56).count();. All in all, I get 173 for your algorithm, golfed: a->{String h="";for(byte b:java.security.MessageDigest.getInstance("MD2").digest(a.getBytes()))h+=h.format("%02x",b);return h.codePoints().filter(c->c>47&&c<56).count()-16;}. More golfing is possible, but this is a net improvement in byte count, isn't it? – Olivier Grégoire – 2017-04-20T08:16:31.957

Some things to golf: println -> print and for(char c:s.toCharArray())if("01234567".contains(""+c))x++; -> for(String c:s.split(""))if("01234567".contains(c))x++; – Kevin Cruijssen – 2017-04-20T11:25:07.047

@OlivierGrégoire I don't know much about Java 8, I switched to Groovy/Grails around the same time. – Magic Octopus Urn – 2017-04-20T13:50:09.840

3

PHP, 50 Bytes

prints 1 for truthy and -1 for false and 0 for a tie

<?=preg_match_all("#[0-7]#",hash(md2,$argn))<=>16;

PHP, 58 Bytes

prints 1 for truthy and -1 for false and 0 for a tie

<?=16<=>strlen(preg_filter("#[0-7]#","",hash(md2,$argn)));

Jörg Hülsermann

Posted 2017-04-19T15:11:42.273

Reputation: 13 026

Sorry for all the spec changes, final output requirements are in now. Basically what you currently have but reversed (1 for truthy, -1 for falsey) which should be fairly easy as iirc in PHP -0 === 0 – Skidsdev – 2017-04-19T15:51:06.057

@Mayube this is too long 1 Byte more is enough. The best way is to specified the output by the possibilities of the language and not general – Jörg Hülsermann – 2017-04-19T16:09:55.930

1echo 16<=>strlen(preg_filter("#[0-7]#","",hash(md2,$argn))); should do the trick without additional byte. – Christoph – 2017-04-20T06:57:24.537

1Golfed version: <?=preg_match_all("/[0-7]/",hash(md2,$argn))<=>16; – Christoph – 2017-04-20T07:10:39.003

@Christoph I feel like an idiot that I have not think about preg_match_all – Jörg Hülsermann – 2017-04-20T11:47:29.050

1

Tcl + Trf package, 79

package require Trf
puts [expr [regexp -all \[0-7\] [hex -m e [md2 $argv]]]-16]

Try it online. (Thanks @Dennis for adding Tcl to TIO.)

Digital Trauma

Posted 2017-04-19T15:11:42.273

Reputation: 64 644

1

PHP, 56 bytes

while($i<32)${hash(md2,$argn)[$i++]>'7'}++;echo$$_<=>16;

user63956

Posted 2017-04-19T15:11:42.273

Reputation: 1 571

1

Java 137 130 124 123 bytes

a->{int c=32;for(int b:java.security.MessageDigest.getInstance("MD2").digest(a.getBytes()))c-=(b>>6&2)+(b>>2&2);return c;}

Test it online!

Basically, for each byte, we're asked to check against its 4th and 8th least significant bits. I don't go through the hex representation at all. So it seemed only natural to start playing with bits.

Values <0 are falsey, values >0 are truthy, value 0 is neither truthy or falsey. The usual truthy and falsey can't be applied to Java this time (because it can't be true or false or 0 with the rule if(<truthy>)), so I took the liberty to declare as such.

Saves

  1. 137 -> 130 bytes: golfed by using bit operations, removing 2 everytime I get a "falsy" bit.
  2. 130 -> 124 bytes: more bitwise operations
  3. 124 -> 123 bytes: replaced byte by int in the for loop declaration.

Olivier Grégoire

Posted 2017-04-19T15:11:42.273

Reputation: 10 647