Compare four integers, return word based on maximum

9

This function should take four integer inputs (a,b,c,d) and return a binary word based on which values equal the maximum of the four.

The return value will be between 1 and 0xF.

For example:

a = 6, b = 77, c = 1, d = 4

returns 2 (binary 0010; only 2nd-least significant bit is set corresponding to b being sole max value)

a = 4, b = 5, c = 10, d = 10

returns 0xC (binary 1100; 3rd- and 4th-least significant bits set corresponding to c and d equaling max value)

a = 1, b = 1, c = 1, d = 1

returns 0xF (binary 1111; all four bits set because all values equal the max)

Here is a simple implementation:

int getWord(int a, int b, int c, int d)
{
    int max = a;
    int word = 1;
    if (b > max)
    {
        max = b;
        word = 2;
    }
    else if (b == max)
    {
        word |= 2;
    }
    if (c > max)
    {
        max = c;
        word = 4;
    }
    else if (c == max)
    {
        word |= 4;
    }
    if (d > max)
    {
        word = 8;
    }
    else if (d == max)
    {
        word |= 8;
    }
    return word;
}

return value can be string of 0's and 1's, bool / bit vector, or integer

Mr Anderson

Posted 2019-03-04T06:50:43.143

Reputation: 193

What kind of branching can we use? – ASCII-only – 2019-03-04T06:53:14.033

@ASCII-only I updated my question, does that clarify? – Mr Anderson – 2019-03-04T06:57:52.663

Also, what would inequality/equality return? 1/0? – ASCII-only – 2019-03-04T06:59:02.447

@ASCII-only yes. – Mr Anderson – 2019-03-04T07:01:31.607

2I have a solution in a golfing language, which uses the builtins Reverse, Maximum, Equality-check, Join, Convert from binary to integer, Convert from integer to hexadecimal. Does this means my score is 1 due to the Equality-check? I have the feeling this is too much focused on regular languages, and even for those it's not 100% clear what the scoring for let's say a Maximum-builtin.. :S – Kevin Cruijssen – 2019-03-04T07:39:55.740

1I would suggest you try to: 1. change this question to code-golf, which only care about the number of bytes. 2. or, restrict to some certain language (certain version of compiler/interpreter please), and list all statements and operators allowed, and how to score them. – tsh – 2019-03-04T07:43:57.937

51 is a better option, IMO. I think this makes a perfectly good code-golf question and I can't see any benefit that would come from restricting the languages available for answers – senox13 – 2019-03-04T07:54:40.930

2I updated my question to remove the criteria. Let me know it it's still unclear – Mr Anderson – 2019-03-04T08:09:20.807

5Should I output a decimal number? Or may I output 4 binary digits instead? – tsh – 2019-03-04T08:51:11.593

I somehow managed to submit an answer after this was closed – Embodiment of Ignorance – 2019-03-04T16:35:54.240

1I have voted to reopen after the (somewhat strange) rules were revised. Should the output be binary, decimal or hex? It's still not clear to me. Maybe I am missing something? – ElPedro – 2019-03-04T17:55:06.660

1I have updated my question regarding return value. Hopefully it's no longer unclear. – Mr Anderson – 2019-03-04T19:06:11.083

Answers

8

Jelly, 2 bytes

Takes input as [d,c,b,a]. Returns a list of Booleans.

Ṁ=

Try it online!

Maximum

= equal to (implies the other argument is the original argument; vectorises)

Adám

Posted 2019-03-04T06:50:43.143

Reputation: 37 779

5

R, 17 bytes

max(a<-scan())==a

Try it online!

Returns a vector of booleans. Since this output has been confirmed, this is preferable over numerical output, as that one is almost twice longer:

R, 33 bytes

sum(2^which(max(a<-scan())==a)/2)

Try it online!

Kirill L.

Posted 2019-03-04T06:50:43.143

Reputation: 6 693

4

APL (Dyalog Unicode), 4 bytesSBCS

Anonymous tacit prefix function. Takes [a,b,c,d] as argument. Returns a bit-Boolean array.*

⌈/=⌽

Try it online!

⌈/ Does the maximum of the argument

= equal (vectorises)

 the reverse of the argument?

* Note that APL stores arrays of Booleans using one bit per value, so this does indeed return a 4-bit word, despite the display form being 0 0 1 0.

Adám

Posted 2019-03-04T06:50:43.143

Reputation: 37 779

3

Haskell, 20 18 bytes

2 bytes saved thanks to proud haskeller

map=<<(==).maximum

Try it online!

Post Rock Garf Hunter

Posted 2019-03-04T06:50:43.143

Reputation: 55 382

You can output a string. – Adám – 2019-03-05T05:44:32.277

1Writing map instead of (<$>) would be two bytes shorter! – proud haskeller – 2019-03-07T06:23:12.130

@proudhaskeller Good catch. Can't believe that I didn't see that one. – Post Rock Garf Hunter – 2019-03-07T13:40:38.933

2

Perl 6, 12 bytes

{$_ X==.max}

Try it online!

Anonymous code block that takes a list of integers and returns a list of booleans. If we need to return as a number, it's +4 bytes to wrap the inside of the code block with 2:[...].

Explanation:

{          }  # Anonymous code block
 $_           # With the input
    X==       # Which values are equal
       .max   # To the maximum element

Jo King

Posted 2019-03-04T06:50:43.143

Reputation: 38 234

OP now says you don't need to wrap. – Adám – 2019-03-05T05:42:44.170

2

Japt, 5

m¶Urw

Try it!

-4 bytes thanks to @Oliver!
-2 bytes thanks to @Shaggy!

Input is a 4-element array in the following format:

[d, c, b, a]

Output is an array of bits.

dana

Posted 2019-03-04T06:50:43.143

Reputation: 2 541

Of course there is ;) There are apparently a lot of shortcuts to learn. – dana – 2019-03-04T15:52:33.473

If a boolean array is an acceptable output, this can be 7 bytes

– Oliver – 2019-03-04T15:56:48.623

@Oliver, 5 bytes ;)

– Shaggy – 2019-03-04T20:29:19.500

You guys are pretty good :) That's interesting how rw converts to r("w") does a reduce by repeatedly getting the max. Same with getting converted to U.m("===", ...). In any case, thanks for the tips! – dana – 2019-03-04T23:54:57.543

2

x86 machine code (MMX/SSE1), 26 bytes (4x int16_t)

x86 machine code (SSE4.1), 28 bytes (4x int32_t or uint32_t)

x86 machine code (SSE2), 24 bytes (4x float32) or 27B to cvt int32

(The last version that converts int32 to float isn't perfectly accurate for large integers that round to the same float. With float input, rounding is the caller's problem and this function works correctly if there are no NaNs, identifying floats that compare == to the max. The integer versions work for all inputs, treating them as signed 2's complement.)

All of these work in 16/32/64-bit mode with the same machine code.

A stack-args calling convention would make it possible to loop over the args twice (finding max and then comparing), possibly giving us a smaller implementation, but I haven't tried that approach.

x86 SIMD has vector->integer bitmap as a single instruction (pmovmskb or movmskps or pd), so it was natural for this even though MMX/SSE instructions are at least 3 bytes long. SSSE3 and later instructions are longer than SSE2, and MMX/SSE1 instructions are the shortest. Different versions of pmax* (packed-integer vertical max) were introduced at different times, with SSE1 (for mmx regs) and SSE2 (for xmm regs) only having signed word (16-bit) and unsigned byte.

(pshufw and pmaxsw on MMX registers are new with Katmai Pentium III, so really they require SSE1, not just the MMX CPU feature bit.)

This is callable from C as unsigned max4_mmx(__m64) with the i386 System V ABI, which passes an __m64 arg in mm0. (Not x86-64 System V, which passes __m64 in xmm0!)

   line         code bytes
    num  addr   
     1                         global max4_mmx
     2                             ;; Input 4x int16_t in mm0
     3                             ;; output: bitmap in EAX
     4                             ;; clobbers: mm1, mm2
     5                         max4_mmx:
     6 00000000 0F70C8B1           pshufw    mm1, mm0, 0b10110001   ; swap adjacent pairs
     7 00000004 0FEEC8             pmaxsw    mm1, mm0
     8                         
     9 00000007 0F70D14E           pshufw    mm2, mm1, 0b01001110   ; swap high/low halves
    10 0000000B 0FEECA             pmaxsw    mm1, mm2
    11                         
    12 0000000E 0F75C8             pcmpeqw   mm1, mm0               ; 0 / -1
    13 00000011 0F63C9             packsswb  mm1, mm1               ; squish word elements to bytes, preserving sign bit
    14                         
    15 00000014 0FD7C1             pmovmskb  eax, mm1          ; extract the high bit of each byte
    16 00000017 240F               and       al, 0x0F          ; zero out the 2nd copy of the bitmap in the high nibble
    17 00000019 C3                 ret

size = 0x1A = 26 bytes

If there was a pmovmskw, what would have saved the packsswb and the and (3+2 bytes). We don't need and eax, 0x0f because pmovmskb on an MMX register already zeros the upper bytes. MMX registers are only 8 bytes wide, so 8-bit AL covers all the possible non-zero bits.

If we knew our inputs were non-negative, we could packsswb mm1, mm0 to produce non-negative signed bytes in the upper 4 bytes of mm1, avoiding the need for and after pmovmskb. Thus 24 bytes.

x86 pack with signed saturation treats the input and output as signed, so it always preserves the sign bit. (https://www.felixcloutier.com/x86/packsswb:packssdw). Fun fact: x86 pack with unsigned saturation still treats the input as signed. This might be why PACKUSDW wasn't introduced until SSE4.1, while the other 3 combinations of size and signedness existed since MMX/SSE2.


Or with 32-bit integers in an XMM register (and pshufd instead of pshufw), every instruction would need one more prefix byte, except for movmskps replacing the pack/and. But pmaxsd / pmaxud need an extra extra byte...

callable from C as unsigned max4_sse4(__m128i); with x86-64 System V, or MSVC vectorcall (-Gv), both of which pass __m128i/__m128d/__m128 args in XMM regs starting with xmm0.

    20                         global max4_sse4
    21                             ;; Input 4x int32_t in xmm0
    22                             ;; output: bitmap in EAX
    23                             ;; clobbers: xmm1, xmm2
    24                         max4_sse4:
    25 00000020 660F70C8B1         pshufd    xmm1, xmm0, 0b10110001   ; swap adjacent pairs
    26 00000025 660F383DC8         pmaxsd    xmm1, xmm0
    27                         
    28 0000002A 660F70D14E         pshufd    xmm2, xmm1, 0b01001110   ; swap high/low halves
    29 0000002F 660F383DCA         pmaxsd    xmm1, xmm2
    30                         
    31 00000034 660F76C8           pcmpeqd   xmm1, xmm0               ; 0 / -1
    32                         
    33 00000038 0F50C1             movmskps  eax, xmm1          ; extract the high bit of each dword
    34 0000003B C3                 ret

size = 0x3C - 0x20 = 28 bytes

Or if we accept input as float, we can use SSE1 instructions. The float format can represent a wide range of integer values...

Or if you think that's bending the rules too far, start with a 3-byte 0F 5B C0 cvtdq2ps xmm0, xmm0 to convert, making a 27-byte function that works for all integers that are exactly representable as IEEE binary32 float, and many combinations of inputs where some of the inputs get rounded to a multiple of 2, 4, 8, or whatever during conversion. (So it's 1 byte smaller than the SSE4.1 version, and works on any x86-64 with just SSE2.)

If any of the float inputs are NaN, note that maxps a,b exactly implements (a<b) ? a : b, keeping the element from the 2nd operand on unordered. So it might be possible for this to return with a non-zero bitmap even if the input contains some NaN, depending on where they are.

unsigned max4_sse2(__m128);

    37                         global max4_sse2
    38                             ;; Input 4x float32 in xmm0
    39                             ;; output: bitmap in EAX
    40                             ;; clobbers: xmm1, xmm2
    41                         max4_sse2:
    42                         ;    cvtdq2ps  xmm0, xmm0
    43 00000040 660F70C8B1         pshufd    xmm1, xmm0, 0b10110001   ; swap adjacent pairs
    44 00000045 0F5FC8             maxps     xmm1, xmm0
    45                         
    46 00000048 660F70D14E         pshufd    xmm2, xmm1, 0b01001110   ; swap high/low halves
    47 0000004D 0F5FCA             maxps     xmm1, xmm2
    48                         
    49 00000050 0FC2C800           cmpeqps   xmm1, xmm0               ; 0 / -1
    50                         
    51 00000054 0F50C1             movmskps  eax, xmm1          ; extract the high bit of each dword
    52 00000057 C3                 ret

size = 0x58 - 0x40 = 24 bytes

copy-and-shuffle with pshufd is still our best bet: shufps dst,src,imm8 reads the input for the low half of dst from dst. And we need a non-destructive copy-and-shuffle both times, so 3-byte movhlps and unpckhps/pd are both out. If we were narrowing to a scalar max, we could use those, but it costs another instruction to broadcast before compare if we don't have the max in all elements already.


Related: SSE4.1 phminposuw can find the position and value of the minimum uint16_t in an XMM register. I don't think it's a win to subtract from 65535 to use it for max, but see an SO answer about using it for max of bytes or signed integers.

Peter Cordes

Posted 2019-03-04T06:50:43.143

Reputation: 2 810

1

JavaScript (ES6), 30 bytes

Takes input as ([d,c,b,a]). Returns 4 Boolean values.

a=>a.map(x=>x==Math.max(...a))

Try it online!

Arnauld

Posted 2019-03-04T06:50:43.143

Reputation: 111 334

1OP has clarified that you can indeed return 4 Boolean values. – Adám – 2019-03-05T05:41:34.993

1

05AB1E, 3 2 bytes

ZQ

Input as a list of [d,c,b,a], output as a list of boolean.

Try it online or verify all test cases.

Explanation:

Z    # Take the maximum of the implicit input-list
 Q   # Check for each in the (implicit) input-list if it equals this value
     # (and output implicitly as result)

Kevin Cruijssen

Posted 2019-03-04T06:50:43.143

Reputation: 67 575

OP has been updated — you don't need to convert to hex. – Adám – 2019-03-05T05:42:14.730

1

Java (JDK), 78 bytes

a->{int m=a[0],w=1,p=1;for(int t:a){w=t<m?w:p|(t>m?(m=t)^m:w);p*=2;}return w;}

Try it online!

  • Takes the input as an array of [a,b,c,d].

Olivier Grégoire

Posted 2019-03-04T06:50:43.143

Reputation: 10 647

1

C# (Visual C# Interactive Compiler), 26 bytes

n=>n.Select(a=>a==n.Max())

Try it online!

Takes input in format [d,c,b,a]. All the others down below take input as [a,b,c,d]

C# (Visual C# Interactive Compiler), 35 bytes

n=>n.Select((a,b)=>n[3-b]==n.Max())

Returns an IEnumerable<bool>.

Try it online!

C# (Visual C# Interactive Compiler), 39 bytes

n=>n.Select((a,b)=>n[3-b]==n.Max()?1:0)

Returns an IEnumerable<int>, which represent bits.

Try it online!

C# (Visual C# Interactive Compiler), 49 bytes

n=>{for(int i=4;i-->0;Write(n[i]==n.Max()?1:0));}

Prints a binary string to STDOUT.

Try it online!

Embodiment of Ignorance

Posted 2019-03-04T06:50:43.143

Reputation: 7 014

IEnumerable<bool> is acceptable. – Adám – 2019-03-05T05:45:04.230

1

Python 3.8 (pre-release), 67 bytes

Lambda function that takes in 4 integers, bit shifts the boolean result of their comparison to the maximum value with some help from Python 3.8's new assignment operator, and returns the bitwise OR of the results

lambda a,b,c,d:((m:=max(a,b,c,d))==d)<<3|(m==c)<<2|(m==b)<<2|(a==m)

Try it online!

senox13

Posted 2019-03-04T06:50:43.143

Reputation: 171

:= reminds me of the old days where that was the assignment operator with Lotus Notes formula. Guess I'll have to take a look at 3.8 for old times sake :) – ElPedro – 2019-03-04T23:54:28.427

1

Ruby, 34 22 bytes

Takes input as an array [d, c, b, a] and returns an array of 1s and 0s.

->r{r.map{|e|e/r.max}}

Try it online!

Reinstate Monica -- notmaynard

Posted 2019-03-04T06:50:43.143

Reputation: 1 053

1

Python 3, 59 bytes 66 bytes

def f(l):
 n=max(a)
 for i in 0,1,2,3:a[i]=a[i]==n
 return a[::-1]

Try it online!

Takes input as [a,b,c,d] and outputs a list of booleans.

Edited to be a proper function, then saved 2 bytes by removing brackets around the conditional.

Bsoned

Posted 2019-03-04T06:50:43.143

Reputation: 31

1

Hello and welcome to PPCG. As it stands, your answer has the form of a snippet, which is disallowed. Please fix your answer to conform with our I/O consensus, i.e. make it a function or full program.

– Jonathan Frech – 2019-03-05T09:23:21.360

1Edited. First time around. Appreciate the heads up! – Bsoned – 2019-03-05T17:53:59.243

You can reduce it to 37 bytes using this list comprehension within a lambda. Welcome to PPCG, and enjoy your stay!

– Value Ink – 2019-03-05T23:27:29.367

@ValueInk Removing the superfluous whitespace saves yet another byte. – Jonathan Frech – 2019-03-06T16:32:56.973

1

1. Python 3.5, 90 bytes

Takes sequence of numbers as parameters. Returns "binary" string

import sys;v=[*map(int,sys.argv[1:])];m=max(v);s=""
for e in v:s=str(int(e==m))+s
print(s)

example:

$ ./script.py 6 77 1 4 77
10010

Explanation

import sys
# convert list of string parameters to list of integers
v=[*map(int,sys.argv[1:])]
# get max
m=max(v)
# init outstring
s=""
# walk through list
for e in v:
    # prepend to outstring: int(True)=>1, int(False)=>0
    s=str(int(e==m))+s
# print out result
print(s)

Rene

Posted 2019-03-04T06:50:43.143

Reputation: 141

0

C# (Visual C# Interactive Compiler), 51 bytes

x=>{for(int m=x.Max(),i=4;i-->0;)x[i]=x[i]==m?1:0;}

Try it online!

Above is an anonymous function that outputs by modifying an argument. Output is an array of 1's and 0's.

Below is a recursive function that outputs an integer.

C# (Visual C# Interactive Compiler), 60 bytes

int f(int[]x,int i=3)=>i<0?0:2*f(x,i-1)|(x[i]==x.Max()?1:0);

Try it online!

Both functions take input as a 4-element array.

[d, c, b, a]

dana

Posted 2019-03-04T06:50:43.143

Reputation: 2 541

You don't need to output an integer. – Adám – 2019-03-05T05:43:34.510

@Adam - thanks :) I realized this after posting while I was working on my other answer. Before I had a chance to change, there was another C# answer that used a lot of the good tricks. – dana – 2019-03-05T19:11:08.720

0

Here's a JS version that outputs as binary

update: Shorter with join, and without the lookup:

JavaScript (Node.js), 42 bytes

a=>a.map(x=>+(x==Math.max(...a))).join('')

Try it online!

Previous, with lookup, 49 bytes

a=>a.map(x=>[0,1][+(x==Math.max(...a))]).join('')

Try it online!

Previous, with reduce, 52 bytes:

a=>a.reduce((y,x)=>y+[0,1][+(x==Math.max(...a))],'')

Try it online!

fa=>a.map(x=>+(x==Math.max(...a))).join('')
console.log(f([ 4, 1,77, 6])) // 0010
console.log(f([10,10, 5, 4])) // 1100
console.log(f([ 1, 1, 1, 1])) // 1111

Pureferret

Posted 2019-03-04T06:50:43.143

Reputation: 960

1You can safely remove [0,1][...] since you're using an index that already is either $0$ or $1$. – Arnauld – 2019-03-04T16:38:46.120

@Arnauld seems obvious now. Thanks! – Pureferret – 2019-03-04T16:54:43.957

0

PHP, 54 bytes

while($i<4)$argv[++$i]<max($argv)||$r+=1<<$i-1;echo$r;

or

while($i<4)$argv[++$i]<max($argv)||$r+=1<<$i;echo$r/2;

take input from command line arguments. Run with -nr or try them online.

Titus

Posted 2019-03-04T06:50:43.143

Reputation: 13 814

0

Python 2, 35 bytes

lambda i:[int(x==max(i))for x in i]

Try it online!

Takes input in the format [d,c,b,a] as with the accepted answer from Adám so I guess it is OK.

Alternative for 41 if it's not...

Python 2, 41 bytes

lambda i:[int(x==max(i))for x in i][::-1]

Try it online!

ElPedro

Posted 2019-03-04T06:50:43.143

Reputation: 5 301

0

Python 3, 42 bytes

f=lambda a:list(map(lambda x:x==max(a),a))

Simply returns a list of whether the element is the max for each element in the input. -2 bytes if you don't count the f= assignment.

Try it online!

ThePlasmaRailgun

Posted 2019-03-04T06:50:43.143

Reputation: 383

f= doesn't count except in recursive functions – ASCII-only – 2019-03-13T03:47:01.577

0

Batch, 92 bytes

@set m=%1
@set f=@for %%i in (%*)do @
%f%set/a"m=m+(m-=%%i)*(m>>31)
%f%cmd/cset/a!(m-%%i)

Takes arguments as command-line parameters in reverse order. Works by arithmetically calculating the maximum of the parameters by reducing over them and adding on only positive differences from the running maximum, then mapping over each parameter again this time comparing it to the maximum. Conveniently cmd/cset/a doesn't output a newline, so the results are automatically concatenated together. The %f% simply saves 5 bytes on what would be a repeated construct.

Neil

Posted 2019-03-04T06:50:43.143

Reputation: 95 035