def _reshape_mask(_mask:Optional[tuple[tuple[sint, sint], ...]], old_shape:tuple[sint, ...], new_shape:tuple[sint, ...]) \
  -> Optional[tuple[tuple[sint, sint], ...]]:
  """Returns the new mask if reshape is possible, and None if not possible."""
  if _mask is None: return tuple((0, s) for s in new_shape)
  if not all_int(flatten(_mask)): return None

  new_mask: list[tuple[int, int]] = []
  # _mask is all int here
  r_masks, r_shape, r_new_shape = reversed(cast(tuple[tuple[int, int], ...], _mask)), reversed(old_shape), reversed(new_shape)
  curr_stride, old_dim, new_dim, mask = 1, next(r_shape, 1), next(r_new_shape, 1), next(r_masks, (0,1))

  while len(new_mask) < len(new_shape):
    (l, r), next_stride = mask, ssimplify(new_dim * curr_stride)

    # need to split mask
    if old_dim == next_stride: # simply copy the mask and get next batch for merging
      new_mask.append((l // curr_stride, (r - 1) // curr_stride + 1))
      curr_stride, old_dim, new_dim, mask = 1, next(r_shape, 1), next(r_new_shape, 1), next(r_masks, (0,1))
    elif old_dim > next_stride: # mask can only be splitted if reshape doesn't cut across the mask.
      if old_dim % next_stride != 0: return None
      if (l % next_stride != 0 or r % next_stride != 0) and l // next_stride != (r - 1) // next_stride: return None
      new_mask.append((l % next_stride // curr_stride, (r - 1) % next_stride // curr_stride + 1))
      curr_stride, new_dim = next_stride,  next(r_new_shape, 1) # need to get mask for next dimension
    else:
      next_mask = next(r_masks, (0, 1))
      # combine if the mask can unfold continuously
      if mask != (0, old_dim) and l != r and next_mask[1] - next_mask[0] != 1: return None
      mask, old_dim = (next_mask[0] * old_dim + l, (next_mask[1] - 1) * old_dim + r), ssimplify(old_dim * next(r_shape, 1))

  return tuple(reversed(new_mask))
Returns the new mask if reshape is possible, and None if not possible.
tinygrad/shape/view.py