void ProximityInfo::initializeG() {
    // TODO: Optimize
    for (int i = 0; i < KEY_COUNT; ++i) {
        const int code = mKeyCodePoints[i];
        const int lowerCode = CharUtils::toLowerCase(code);
        mCenterXsG[i] = mKeyXCoordinates[i] + mKeyWidths[i] / 2;
        mCenterYsG[i] = mKeyYCoordinates[i] + mKeyHeights[i] / 2;
        if (hasTouchPositionCorrectionData()) {
            // Computes sweet spot center points for geometric input.
            const float verticalScale = ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G;
            const float sweetSpotCenterY = static_cast<float>(mSweetSpotCenterYs[i]);
            const float gapY = sweetSpotCenterY - mCenterYsG[i];
            mSweetSpotCenterYsG[i] = static_cast<int>(mCenterYsG[i] + gapY * verticalScale);
        }
        mLowerCodePointToKeyMap[lowerCode] = i;
        mKeyIndexToOriginalCodePoint[i] = code;
        mKeyIndexToLowerCodePointG[i] = lowerCode;
    }
    for (int i = 0; i < KEY_COUNT; i++) {
        mKeyKeyDistancesG[i][i] = 0;
        for (int j = i + 1; j < KEY_COUNT; j++) {
            if (hasTouchPositionCorrectionData()) {
                // Computes distances using sweet spots if they exist.
                // We have two types of Y coordinate sweet spots, for geometric and for the others.
                // The sweet spots for geometric input are used for calculating key-key distances
                // here.
                mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt(
                        mSweetSpotCenterXs[i], mSweetSpotCenterYsG[i],
                        mSweetSpotCenterXs[j], mSweetSpotCenterYsG[j]);
            } else {
                mKeyKeyDistancesG[i][j] = GeometryUtils::getDistanceInt(
                        mCenterXsG[i], mCenterYsG[i], mCenterXsG[j], mCenterYsG[j]);
            }
            mKeyKeyDistancesG[j][i] = mKeyKeyDistancesG[i][j];
        }
    }
}
float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG(
    const int keyId, const int x, const int y, const float verticalScale) const {
    const bool correctTouchPosition = hasTouchPositionCorrectionData();
    const float centerX = static_cast<float>(correctTouchPosition ? getSweetSpotCenterXAt(keyId)
                          : getKeyCenterXOfKeyIdG(keyId));
    const float visualKeyCenterY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId));
    float centerY;
    if (correctTouchPosition) {
        const float sweetSpotCenterY = static_cast<float>(getSweetSpotCenterYAt(keyId));
        const float gapY = sweetSpotCenterY - visualKeyCenterY;
        centerY = visualKeyCenterY + gapY * verticalScale;
    } else {
        centerY = visualKeyCenterY;
    }
    const float touchX = static_cast<float>(x);
    const float touchY = static_cast<float>(y);
    const float keyWidth = static_cast<float>(getMostCommonKeyWidth());
    return ProximityInfoUtils::getSquaredDistanceFloat(centerX, centerY, touchX, touchY)
           / SQUARE_FLOAT(keyWidth);
}
// When the referencePointY is NOT_A_COORDINATE, this method calculates the return value without
// using the line segment.
int ProximityInfo::getKeyCenterYOfKeyIdG(
        const int keyId, const int referencePointY, const bool isGeometric) const {
    // TODO: Remove "isGeometric" and have separate "proximity_info"s for gesture and typing.
    if (keyId < 0) {
        return 0;
    }
    int centerY;
    if (!hasTouchPositionCorrectionData()) {
        centerY = mCenterYsG[keyId];
    } else if (isGeometric) {
        centerY = static_cast<int>(mSweetSpotCenterYsG[keyId]);
    } else {
        centerY = static_cast<int>(mSweetSpotCenterYs[keyId]);
    }
    if (referencePointY != NOT_A_COORDINATE &&
            centerY + mKeyHeights[keyId] > KEYBOARD_HEIGHT && centerY < referencePointY) {
        // When the distance between center point and bottom edge of the keyboard is shorter than
        // the key height, we assume the key is located at the bottom row of the keyboard.
        // The center point is extended to the bottom edge for such keys.
        return referencePointY;
    }
    return centerY;
}
// referencePointX is used only for keys wider than most common key width. When the referencePointX
// is NOT_A_COORDINATE, this method calculates the return value without using the line segment.
// isGeometric is currently not used because we don't have extra X coordinates sweet spots for
// geometric input.
int ProximityInfo::getKeyCenterXOfKeyIdG(
        const int keyId, const int referencePointX, const bool isGeometric) const {
    if (keyId < 0) {
        return 0;
    }
    int centerX = (hasTouchPositionCorrectionData()) ? static_cast<int>(mSweetSpotCenterXs[keyId])
            : mCenterXsG[keyId];
    const int keyWidth = mKeyWidths[keyId];
    if (referencePointX != NOT_A_COORDINATE
            && keyWidth > getMostCommonKeyWidth()) {
        // For keys wider than most common keys, we use a line segment instead of the center point;
        // thus, centerX is adjusted depending on referencePointX.
        const int keyWidthHalfDiff = (keyWidth - getMostCommonKeyWidth()) / 2;
        if (referencePointX < centerX - keyWidthHalfDiff) {
            centerX -= keyWidthHalfDiff;
        } else if (referencePointX > centerX + keyWidthHalfDiff) {
            centerX += keyWidthHalfDiff;
        } else {
            centerX = referencePointX;
        }
    }
    return centerX;
}