How hard can I crush my array? Lets define the process of crushing an array of numbers.  In a crush we read the array left to right.  If at a point we encounter two of the same element in a row we remove the first one and double the second one.  For example here is the process of crushing the following array

[5,2,2,3]
 ^
[5,2,2,3]
   ^
[5,2,2,3]
     ^
[5,4,3]
   ^
[5,4,3]
     ^


The same element can be collapsed multiple times, for example [1,1,2] becomes [4] when crushed.

We will call an array uncrushable when the process of crushing that array does not change it.  For example [1,2,3] is still [1,2,3] after being crushed.

Your task is to take an array and determine the number of crushes required to make it uncrushable.  You need only support integers on the range of 0 to 232-1

This is code-golf so answers will be scored in bytes with less bytes being better.

Test Cases

[1] -&gt; 0
[1,1] -&gt; 1
[2,1,1] -&gt; 2
[4,2,1,1] -&gt; 3
[2,2,2,1,1] -&gt; 3
[0,0,0,0] -&gt; 1
[4,0,0,0,4] -&gt; 1
[4,0,0,0,0,4] -&gt; 1
[] -&gt; 0
x86 assembly (64-bit), 66 65 bytes

31 c0 57 59 56 51 56 5f 4d 31 c0 48 83 c6 08 48
83 e9 01 76 1b fc f2 48 a7 75 15 48 d1 67 f8 51
56 57 f3 48 a5 5f 5e 59 fd 48 a7 49 ff c0 eb e5
59 5e 4c 29 c1 48 ff c2 4d 85 c0 75 c7 48 ff c8
c3


String instructions were helpful. Having to correct for off-by-one errors in a 64-bit environment was not.

Fully commentated source code:

.globl crush
crush:
/* return value */
xor %eax, %eax
/* save our length in rcx */
push %rdi
pop %rcx
pass:
/* save the start of the string and the length */
push %rsi
push %rcx
/* this is the loop */
/* first copy source to dest */
push %rsi
pop %rdi
/* and zero a variable to record the number of squashes we make this pass */
xor %r8, %r8
/* increment source, and decrement ecx */
add $8,%rsi
sub $1,%rcx
/* if ecx is zero or -1, we're done (we can't depend on the code to take care of this
automatically since dec will leave the zero flag set and cmpsq won't change it) */
jbe endpass
compare:
/* make sure we're going forward */
cld
/* compare our two values until we find two that are the same */
repne cmpsq
/* if we reach here, we either found the end of the string, or
we found two values that are the same. check the zero flag to
find out which */
jne endpass
/* okay, so we found two values that are the same. what we need
to do is double the previous value of the destination, and then
shift everything leftwards once */
shlq $1, -8(%rdi)
/* easiest way to shift leftwards is rep movsq, especially since
our ecx is already right. we just need to save it and the rsi/rdi */
push %rcx
push %rsi
push %rdi
rep movsq
pop %rdi
pop %rsi
pop %rcx
/* problem: edi and esi are now one farther than they should be,
since we can squash this dest with a different source. consequently
we need to put them back where they were. */
std
cmpsq
/* we don't need to put ecx back since the list is now one shorter
than it was. */
/* finally, mark that we made a squash */
inc %r8
/* okay, once we've reached this point, we should have:
 edi and esi: next two values to compare
 ecx: number of comparisons left
so we just jump back to our comparison operation */
jmp compare
endpass:
/* we reached the end of the string. retrieve our old ecx and esi */
pop %rcx
pop %rsi
/* rsi is accurate, but rcx is not. we need to subtract the number of squashes
that we made this pass. */
sub %r8, %rcx
/* record that we performed a pass */
inc %rax
/* if we did make any squashes, we need to perform another pass */
test %r8, %r8
jnz pass
/* we reached the end; we've made as many passes as we can.
decrement our pass counter since we counted one too many */
dec %rax
/* and finally return it */
ret


I may try doing this in 32-bit, if only for fun, since those REX prefixes really killed me.

Edit: shaved one byte off by replacing lodsq with add, %rdx with %rax, and collapsing two cld's into one.