1. # Mystery Square
  2. import codejam as gcj
  3. def bn(n):
  4. return bin(n)[2:] # Python annoyingly prepends '0b'
  5. def int_sqrt(n): # Find int(n**0.5) - works for very large numbers
  6. x, y = n, (n + 1) // 2
  7. while y < x:
  8. x, y = y, (y + n // y) // 2
  9. return x
  10. def replace_gaps(X, gaps, digits): # (?00??1?, 1011) -> (1)00(0)(1)1(1)
  11. digits = bn(digits).zfill(len(gaps))
  12. for i, j in enumerate(gaps):
  13. X[j] = digits[i]
  14. def find_square(S):
  15. if len(S) > 1:
  16. top, bottom = S[:len(S) // 2], S[len(S) // 2:]
  17. return try_find_odd(top[:], bottom[:]) or 4 * find_square(top + bottom[:-2])
  18. return 1
  19. def try_find_odd(top, bottom):
  20. if bottom[-1] == '1' or bottom[-1] == '?':
  21. bottom[-1] = '1'
  22. if bottom.count('?') >= top.count('?'):
  23. gaps = [i for i, c in enumerate(top) if c == '?']
  24. for digits in range(2**len(gaps)):
  25. replace_gaps(top, gaps, digits)
  26. r = int_sqrt(int(''.join(top) + '1' * len(bottom), 2))
  27. if all(a in (b, '?') for a, b in zip(top + bottom, bn(r**2))):
  28. return r**2
  29. else:
  30. gaps = [i for i, c in enumerate(bottom) if c == '?']
  31. for digits in range(2**len(gaps)):
  32. replace_gaps(bottom, gaps, digits)
  33. b = int(''.join(bottom), 2)
  34. for second_digit in 0, 1:
  35. r = 2 * second_digit + 1
  36. for k in range(3, len(bottom) + 1):
  37. if r**2 % 2**(k + 1) != b % 2**(k + 1):
  38. r += 2**(k - 1)
  39. r |= 2**(k - 1)
  40. if all(a in (b, '?') for a, b in zip(top + bottom, bn(r**2))):
  41. return r**2
  42. T = gcj.read_input('i')
  43. for _case in range(1, T + 1):
  44. S = gcj.read_input('s{}')
  45. print('Case #%d:' % _case, bn(find_square(S)))