This is the performance version of [this kata](https://www.codewars.com/kata/59afff65f1c8274f270020f5).

---

Imagine two rings with numbers on them. The inner ring spins clockwise and the outer ring spins anti-clockwise. We start with both rings aligned on 0 at the top, and on each move we spin each ring by 1. How many moves will it take before both rings show the same number at the top again?

The inner ring has integers from 0 to innerMax and the outer ring has integers from 0 to outerMax, where innerMax and outerMax are integers >= 1.

```
e.g. if innerMax is 2 and outerMax is 3 then after
1 move: inner = 2, outer = 1
2 moves: inner = 1, outer = 2
3 moves: inner = 0, outer = 3
4 moves: inner = 2, outer = 0
5 moves: inner = 1, outer = 1
Therefore it takes 5 moves for the two rings to reach the same number
Therefore spinningRings(2, 3) = 5
```
```
e.g. if innerMax is 3 and outerMax is 2 then after
1 move: inner = 3, outer = 1
2 moves: inner = 2, outer = 2
Therefore it takes 2 moves for the two rings to reach the same number
spinningRings(3, 2) = 2
```

---

Test input range:

- `100` tests with `1 <= innerMax, outerMax <= 10000`
- `400` tests with `1 <= innerMax, outerMax <= 2^48`

Difficulty: interview

Starter code:

def spinning_rings(inner_max, outer_max):
	

Solutions:
def spinning_rings(inner_max, outer_max):
    p = inner_max + 1
    q = outer_max + 1
    move = 1
    while (-move) % p != move % q:
        if (-move) % p >= q:
            move = move // p * p + p - q + 1
        elif move % q >= p:
            move = move // q * q + q
        elif (-move) % p > move % q and ((-move) % p + move % q) % 2 == 0:
            move += ((-move) % p - move % q) // 2
        else:
            move = min((move - 1) // p * p + p, (move - 1) // q * q + q) + 1
    return move

def spinning_rings(inner_max, outer_max):
    
    i = inner_max+1
    j = outer_max+1
    
    x = 1
    while x < i*j:
        a = -x % i
        b = x % j
        if a == b:
            return x
        elif a > b: 
            if a > j:
                x += a-j
            else:                
                x+= max((a-b)//2,1)
        elif b > a:
            if b > i:
                x+= j-b
            else:
                x+= max(min(j-b,a),1)

def spinning_rings(im, om):
    a,b,res,d = im,1,1,abs(om - im)
    while a != b:
        x = 1
        if a - b > 2: x = (a-b)//2
        if a < b: x = min(a + 1, om - b + 1)
        if b == im + 1 or om < im and a == im: x = d
        if a - b == 1: x = min(d,min(a,om-b))
        a,b = (a - x) % (im+1), (b + x) % (om+1)
        res += x
    return res

def spinning_rings(inner_max, outer_max):
    inner = inner_max
    outer = 1
    moves = 1
    while inner != outer:
        if outer > inner_max:
            jump = outer_max + 1 - outer
        elif inner > outer_max:
            jump = inner - outer_max
        elif inner > outer:
            jump = (inner - outer + 1) // 2
        elif inner == (outer_max + 1 - outer):
            jump = inner
        else:
            jump = min(inner + 1, outer_max + 1 - outer)
        outer = (outer + jump) % (outer_max + 1)
        inner = (inner - jump) % (inner_max + 1)
        moves += jump
    return moves

def spinning_rings(inner_max, outer_max):
    if inner_max==outer_max:
        if inner_max%2:
            return (inner_max+1)/2
        return inner_max+1
    if inner_max%2:
        res = (inner_max+1)/2 if outer_max>(inner_max+1)/2 else (inner_max+1+(outer_max+1)*((inner_max+1)//(outer_max+1)-1+(((inner_max+1)//(outer_max+1)+1)*(outer_max+1))%2))/2
        return res    
    if outer_max<inner_max:
        if outer_max%2:
            # outermax odd
            a = 2*(inner_max+1) - (inner_max+1)%(outer_max+1) 
            return a if (inner_max+1)%(outer_max+1)>(outer_max+1)/2 else a - (outer_max+1)/2
        else:
            #a = ceil((inner_max-outer_max)/(outer_max+1))*(outer_max+1)
            a = inner_max + 1 - (inner_max-outer_max)%(outer_max+1)
            b = (inner_max + 1 - a)//2
            #b = ((inner_max-outer_max)%(outer_max+1))//2
            c = (outer_max + 1 + a)%2
            return inner_max - b + 1 if not c else (inner_max - outer_max + a)/2
    if outer_max>inner_max:
        k = (outer_max + 1)//(inner_max + 1)
        a = (k + 1)*(inner_max + 1) + ((outer_max + 1)%(inner_max + 1))/2
        return a if not ((outer_max+1)%(inner_max+1))%2 else 2*(outer_max+1)-(outer_max-inner_max/2)-((outer_max+1)%(inner_max+1)+1)/2


def spinning_rings(inner_max, outer_max):
    i = 1
    while True:
        inner = -i % (inner_max + 1)
        outer = i % (outer_max + 1)
        if inner == outer:
            return int(i)
        elif inner < outer:
            i += inner + 1 if inner_max > outer_max else outer_max - outer + 1
        else:
            if inner > outer_max:
                i += inner - outer_max
            else:
                mid = (inner + outer) / 2
                if mid % 1 == 0:
                    i += mid - outer
                else:
                    i += min(inner, outer_max - outer) + 1

def spinning_rings(inner_max, outer_max):
    i = inner_max
    o = 1
    moves = 1
    while i != o:
        if o > inner_max:
            jump = outer_max + 1 - o
        elif i > outer_max:
            jump = i - outer_max
        elif i > o:
            jump = (i - o + 1) // 2
        elif i == (outer_max + 1 - o):
            jump = i
        else:
            jump = min(i + 1, outer_max + 1 - o)
        o = (o + jump) % (outer_max + 1)
        i = (i - jump) % (inner_max + 1)
        moves += jump
    return moves

def spinning_rings(innermax,outermax):
    if innermax>outermax:
        a = innermax
        b = 1
        c = 1
        if a>outermax and outermax*2<innermax:
            c+=(a-outermax)
            b+=(a-outermax)%(outermax+1)
            if b>outermax:
                b=0
            a=outermax

        while a != b:
            if a>outermax and c!=b:  # check if a> outermax than a become in outer max range.
                temp=c
                c+=(a-outermax)
                b=(b+(c-temp))%(outermax+1)
                a=outermax
            if (a%2)==(b%2):
                return c+(a-b)//2
            elif a<b:
                c+=a+1
                b+=(a+1)%(outermax+1)
                a=innermax
            elif a>b:
                c+=(outermax-b+1)
                a=a-(outermax-b+1)
                b=0
            if a == 0:
                a = innermax
            else:
                a -= 1
            if b == outermax:
                b = 0
            else:
                b += 1
            c += 1
        else:
            return c

    else:
        a = innermax
        b = 1
        c = 1
        if a % 2 != 0:
            return a // 2 + 1
        while a != b:

            if a % 2 == 1 and b % 2 == 1:
                return c + (a // 2 + 1)
            elif a > b and a % 2 == 0 and b % 2 == 0:
                return c + (a - b) // 2
            else:

                c = c + a
                b = b + a
                a = 0
            if b > innermax:
                a = abs((a - (outermax - b)) % (innermax + 1))
                c += (outermax - b)
                b = outermax
            if a == 0:
                a = innermax
            else:
                a -= 1
            if b == outermax:
                b = 0
            else:
                b += 1
            c += 1
        else:
            return c

def spinning_rings(inner_max, outer_max):

    outer_ring = 0
    count = 1
    if inner_max > outer_max:
        while True:
            count += (inner_max - outer_max)
            inner_ring = outer_max
            outer_ring += ((count) % (outer_max+1))

            if (inner_ring-outer_ring) % 2 == 0:
                count += (inner_ring-outer_ring)//2
                return count
            else:
                # parity change happens when one of the rings reaches 0
                # it happens if inner reaches 0 or outer reaches outer_max
                # since inner is at outer max it is certain that outer will reach zero sooner
                count += (outer_max-outer_ring)+1
                inner_ring -= (outer_ring)
                if inner_ring % 2 == 0:
                    count += (inner_ring//2)
                    return count
                else:
                    dist = (inner_max + 1) // (outer_max + 1)
                    steps = dist * (outer_max + 1)
                    if inner_max%2 == outer_max%2:
                        inner_ring = (inner_max-steps)
                        count = steps + (inner_ring)//2
                        return count+1
                    elif(inner_max-steps-1)%2==0:
                        inner_ring = (inner_max - steps)
                        count = steps + inner_ring // 2
                        return count + 1
                    else:

                        count = inner_max + (inner_max-outer_max) + 1
                        outer_ring = count%(outer_max+1)
                        count += (outer_max-outer_ring)//2
                return count+1

    elif outer_max > inner_max:
        inner_ring = inner_max
        count = 1
        outer_ring = 1
        while True:
            if (inner_ring-outer_ring) % 2 == 0:
                count += (inner_ring-outer_ring)//2
                return count
            else:
                # check parity change
                count = (outer_max) # larger ring reaches 0
                outer_ring = 0
                inner_ring -= count%(inner_max+1)
                count += 1
                if inner_ring%2 == 0:
                    count += inner_ring//2
                    return count
                else:
                        count += inner_ring + 1 # smaller reaches inner max
                        outer_ring += inner_ring+1
                        inner_ring = inner_max
                        count += (inner_ring-outer_ring)//2
                return count
    else:
        if(outer_max-1) %2 == 0:
            return count + outer_max//2