library Math {
    function compound(uint256 rewardRateX96, uint256 nCompounds)
        internal
        pure
        returns (uint256 compoundedX96)
    {
        if (nCompounds == 0) {
            compoundedX96 = 2**96;
        } else if (nCompounds == 1) {
            compoundedX96 = rewardRateX96;
        } else {
            compoundedX96 = compound(rewardRateX96, nCompounds / 2);
            compoundedX96 = mulX96(compoundedX96, compoundedX96);

            if (nCompounds % 2 == 1) {
                compoundedX96 = mulX96(compoundedX96, rewardRateX96);
            }
        }
    }

    // ref: https://blogs.sas.com/content/iml/2016/05/16/babylonian-square-roots.html
    function sqrt(uint256 x) internal pure returns (uint256 y) {
        uint256 z = (x + 1) / 2;
        y = x;
        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
    }

    function mulX96(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = (x * y) >> 96;
    }

    function divX96(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = (x << 96) / y;
    }

    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }
}
function sqrt(uint256 x) internal pure returns (uint256 y) {
    uint256 z = (x + 1) / 2;
    y = x;
    while (z < y) {
        y = z;
        z = (x / z + z) / 2;
    }
}

// ref: https://blogs.sas.com/content/iml/2016/05/16/babylonian-square-roots.html