/**
  * Given (i, j) coordinates that may be out of bounds, normalize them by
  * returning the corresponding neighbor cell on an adjacent face.
  */
 private static function fromFaceIJWrap($face, $i, $j)
 {
     // Convert i and j to the coordinates of a leaf cell just beyond the
     // boundary of this face. This prevents 32-bit overflow in the case
     // of finding the neighbors of a face cell, and also means that we
     // don't need to worry about the distinction between (s,t) and (u,v).
     $i = max(-1, min(self::MAX_SIZE, $i));
     $j = max(-1, min(self::MAX_SIZE, $j));
     // Find the (s,t) coordinates corresponding to (i,j). At least one
     // of these coordinates will be just outside the range [0, 1].
     $kScale = 1.0 / self::MAX_SIZE;
     $s = $kScale * (($i << 1) + 1 - self::MAX_SIZE);
     $t = $kScale * (($j << 1) + 1 - self::MAX_SIZE);
     // Find the leaf cell coordinates on the adjacent face, and convert
     // them to a cell id at the appropriate level.
     $p = S2Projections::faceUvToXyz($face, $s, $t);
     $face = S2Projections::xyzToFace($p);
     $st = S2Projections::validFaceXyzToUv($face, $p);
     return self::fromFaceIJ($face, self::stToIJ($st->x()), self::stToIJ($st->y()));
 }