library UniswapV2Library {
    using SafeMath for uint256;

    // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
    function getAmountOut(
        uint256 amountIn,
        uint256 reserveIn,
        uint256 reserveOut
    ) internal pure returns (uint256 amountOut) {
        require(amountIn > 0, "UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT");
        require(reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY");
        uint256 amountInWithFee = amountIn.mul(997);
        uint256 numerator = amountInWithFee.mul(reserveOut);
        uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
        amountOut = numerator / denominator;
    }

    /**
     * @author allemanfredi
     * @notice given an input amount, returns the output
     *         amount with slippage and with fees. This fx is
     *         to check approximately onchain the slippage
     *         during a swap
     */
    function calculateSlippageAmountWithFees(
        uint256 _amountIn,
        uint256 _allowedSlippage,
        uint256 _rateIn,
        uint256 _rateOut
    ) internal pure returns (uint256) {
        require(_amountIn > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
        uint256 slippageAmount = _amountIn
            .mul(_rateOut)
            .div(_rateIn)
            .mul(10000 - _allowedSlippage)
            .div(10000);
        // NOTE: remove fees
        return slippageAmount.mul(997).div(1000);
    }
}
// taken from here: https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/libraries/UniswapV2Library.sol
function calculateSlippageAmountWithFees(
    uint256 _amountIn,
    uint256 _allowedSlippage,
    uint256 _rateIn,
    uint256 _rateOut
) internal pure returns (uint256) {
    require(_amountIn > 0, "UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT");
    uint256 slippageAmount = _amountIn
        .mul(_rateOut)
        .div(_rateIn)
        .mul(10000 - _allowedSlippage)
        .div(10000);
    // NOTE: remove fees
    return slippageAmount.mul(997).div(1000);
}

/**
 * @author allemanfredi
 * @notice given an input amount, returns the output
 *         amount with slippage and with fees. This fx is
 *         to check approximately onchain the slippage
 *         during a swap
 */