Address Details
contract
token

0xC2cCCd430E27C7890Be19e7B1475F186d10612c8

Token
Continous Token (TOK)
Creator
0xe91286–9209d6 at 0xa3e60c–20bee6
Balance
0 CELO ( )
Locked CELO Balance
0.00 CELO
Voting CELO Balance
0.00 CELO
Pending Unlocked Gold
0.00 CELO
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
7808190
This contract has been verified via Sourcify. View contract in Sourcify repository
Contract name:
ReputationToken




Optimization enabled
true
Compiler version
v0.8.0+commit.c7dfd78e




Optimization runs
200
EVM Version
istanbul




Verified at
2023-02-06T10:30:14.926230Z

contracts/reputation.sol

// SPDX-License-Identifier: MIT

//Deployed on Ethereum Goerli testnet: 0x97DB500303C219FB11fFded8EADaA8945663D2A5
//Deployed on Near testner: 97db500303c219fb11ffded8eadaa8945663d2a5.factory.goerli.testnet
//Deployed on Aurora testnet: 0x7fc383a117f64b29ab012231ae91dab6dfb95043

pragma solidity ^0.8.0;

import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import "https://github.com/slim12kg/protofire-continous-token-task/blob/master/contracts/curves/BancorFormula.sol";

contract ReputationToken is BancorFormula, ERC20 {
    using SafeMath for uint256;

    uint256 public scale = 10**18;
    uint256 public reserveBalance = 10 * scale;
    uint256 public reserveRatio;
    address public reserveTokenAddress;

    /**
     * @dev Fired when TOK is exchanged for Dai
     */
    event ContinuousBurn(
        address _address,
        uint256 continuousTokenAmount,
        uint256 reserveTokenAmount
    );

    /**
     * @dev Fired when Dai us exchanged for TOK
     */
    event ContinuousMint(
        address _address,
        uint256 reserveTokenAmount,
        uint256 continuousTokenAmount
    );

    /**
     * @param _reserveRatio(RR) to determine the bonding curve to be used. 50% RR = Linear Bonding Curve, 10% RR = Exponential Bonding Curve
     * @param _reserveTokenAddress Contract address of ERC20 Token to use as reserve/exchange of value e.g DAI
     */
    constructor(uint256 _reserveRatio, address _reserveTokenAddress)
        ERC20("Continous Token", "TOK")
    {
        reserveRatio = _reserveRatio;
        reserveTokenAddress = _reserveTokenAddress;
        _mint(msg.sender, 1 * scale);
    }

    /**
     * @dev Mint some TOK token by allowing contract to spend an amount of caller reserve tokens
     * @param _amount Number of reserve token approved for this contract to convert to TOK tokens
     */
    function mint(uint256 _amount) public returns (uint256 _amountMinted) {
        uint256 allowance = IERC20(reserveTokenAddress).allowance(
            msg.sender,
            address(this)
        );

        require(allowance > 0, "Must approve DAI to buy tokens.");
        require(allowance >= _amount, "Must approve enough DAI.");

        bool success = IERC20(reserveTokenAddress).transferFrom(
            msg.sender,
            address(this),
            allowance
        );

        if (success) {
            return _continuousMint(allowance);
        } else {
            require(allowance > 0, "Failed to transfer Dai tokens");
        }
    }

    /**
     * @dev Burn some TOK token and return reserve token based on current curve price
     * @param _amount Number of TOK token to convert to reserve tokens
     */
    function burn(uint256 _amount) public {
        uint256 returnAmount = _continuousBurn(_amount);
        IERC20(reserveTokenAddress).transfer(msg.sender, returnAmount);
    }

    function calculateContinuousMintReturn(uint256 _amount)
        public
        view
        returns (uint256 mintAmount)
    {
        return
            purchaseTargetAmount(
                totalSupply(),
                reserveBalance,
                uint32(reserveRatio),
                _amount
            );
    }

    function calculateContinuousBurnReturn(uint256 _amount)
        public
        view
        returns (uint256 burnAmount)
    {
        return
            saleTargetAmount(
                totalSupply(),
                reserveBalance,
                uint32(reserveRatio),
                _amount
            );
    }

    function _continuousMint(uint256 _deposit) internal returns (uint256) {
        require(_deposit > 0, "Deposit must be non-zero.");

        uint256 amount = calculateContinuousMintReturn(_deposit);
        _mint(msg.sender, amount);
        reserveBalance = reserveBalance.add(_deposit);
        emit ContinuousMint(msg.sender, _deposit, amount);
        return amount;
    }

    function _continuousBurn(uint256 _amount) internal returns (uint256) {
        require(_amount > 0, "Amount must be non-zero.");
        require(
            balanceOf(msg.sender) >= _amount,
            "Insufficient tokens to burn."
        );

        uint256 reimburseAmount = calculateContinuousBurnReturn(_amount);
        _burn(msg.sender, _amount);
        reserveBalance = reserveBalance.sub(reimburseAmount);
        emit ContinuousBurn(msg.sender, _amount, reimburseAmount);
        return reimburseAmount;
    }
}
        

/_openzeppelin/contracts/utils/math/SafeMath.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}
          

/https_/github.com/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * The default value of {decimals} is 18. To select a different value for
     * {decimals} you should overload it.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the value {ERC20} uses, unless this function is
     * overridden;
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(_msgSender(), recipient, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        unchecked {
            _approve(sender, _msgSender(), currentAllowance - amount);
        }

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        uint256 currentAllowance = _allowances[_msgSender()][spender];
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(_msgSender(), spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `sender` to `recipient`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);

        _afterTokenTransfer(sender, recipient, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
        }
        _totalSupply -= amount;

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(
        address owner,
        address spender,
        uint256 amount
    ) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 amount
    ) internal virtual {}
}
          

/https_/github.com/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}
          

/https_/github.com/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
          

/https_/github.com/OpenZeppelin/openzeppelin-contracts/contracts/utils/Context.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}
          

/https_/github.com/slim12kg/protofire-continous-token-task/blob/master/contracts/curves/BancorFormula.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "../interfaces/IBancorFormula.sol";
import "../utils/Power.sol";

contract BancorFormula is IBancorFormula, Power {
    using SafeMath for uint256;

    uint256 private constant ONE = 1;
    uint32 private constant MAX_WEIGHT = 1000000;
    uint8 private constant MIN_PRECISION = 32;
    uint8 private constant MAX_PRECISION = 127;

    // Auto-generated via 'PrintMaxExpArray.py'
    uint256[128] private maxExpArray;

    /**
     * @dev should be executed after construction (too large for the constructor)
     */
    function init() public {
        initMaxExpArray();
        initLambertArray();
    }

    /**
     * @dev given a token supply, reserve balance, weight and a deposit amount (in the reserve token),
     * calculates the target amount for a given conversion (in the main token)
     *
     * Formula:
     * return = _supply * ((1 + _amount / _reserveBalance) ^ (_reserveWeight / 1000000) - 1)
     *
     * @param _supply          liquid token supply
     * @param _reserveBalance  reserve balance
     * @param _reserveWeight   reserve weight, represented in ppm (1-1000000)
     * @param _amount          amount of reserve tokens to get the target amount for
     *
     * @return target
     */
    function purchaseTargetAmount(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveWeight,
        uint256 _amount
    ) public view override returns (uint256) {
        // validate input
        require(_supply > 0, "ERR_INVALID_SUPPLY");
        require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
        require(
            _reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT,
            "ERR_INVALID_RESERVE_WEIGHT"
        );

        // special case for 0 deposit amount
        if (_amount == 0) return 0;

        // special case if the weight = 100%
        if (_reserveWeight == MAX_WEIGHT)
            return _supply.mul(_amount) / _reserveBalance;

        uint256 result;
        uint8 precision;
        uint256 baseN = _amount.add(_reserveBalance);
        (result, precision) = power(
            baseN,
            _reserveBalance,
            _reserveWeight,
            MAX_WEIGHT
        );
        uint256 temp = _supply.mul(result) >> precision;
        return temp - _supply;
    }

    /**
     * @dev given a token supply, reserve balance, weight and a sell amount (in the main token),
     * calculates the target amount for a given conversion (in the reserve token)
     *
     * Formula:
     * return = _reserveBalance * (1 - (1 - _amount / _supply) ^ (1000000 / _reserveWeight))
     *
     * @param _supply          liquid token supply
     * @param _reserveBalance  reserve balance
     * @param _reserveWeight   reserve weight, represented in ppm (1-1000000)
     * @param _amount          amount of liquid tokens to get the target amount for
     *
     * @return reserve token amount
     */
    function saleTargetAmount(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveWeight,
        uint256 _amount
    ) public view override returns (uint256) {
        // validate input
        require(_supply > 0, "ERR_INVALID_SUPPLY");
        require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
        require(
            _reserveWeight > 0 && _reserveWeight <= MAX_WEIGHT,
            "ERR_INVALID_RESERVE_WEIGHT"
        );
        require(_amount <= _supply, "ERR_INVALID_AMOUNT");

        // special case for 0 sell amount
        if (_amount == 0) return 0;

        // special case for selling the entire supply
        if (_amount == _supply) return _reserveBalance;

        // special case if the weight = 100%
        if (_reserveWeight == MAX_WEIGHT)
            return _reserveBalance.mul(_amount) / _supply;

        uint256 result;
        uint8 precision;
        uint256 baseD = _supply - _amount;
        (result, precision) = power(_supply, baseD, MAX_WEIGHT, _reserveWeight);
        uint256 temp1 = _reserveBalance.mul(result);
        uint256 temp2 = _reserveBalance << precision;
        return (temp1 - temp2) / result;
    }

    /**
     * @dev given two reserve balances/weights and a sell amount (in the first reserve token),
     * calculates the target amount for a conversion from the source reserve token to the target reserve token
     *
     * Formula:
     * return = _targetReserveBalance * (1 - (_sourceReserveBalance / (_sourceReserveBalance + _amount)) ^ (_sourceReserveWeight / _targetReserveWeight))
     *
     * @param _sourceReserveBalance    source reserve balance
     * @param _sourceReserveWeight     source reserve weight, represented in ppm (1-1000000)
     * @param _targetReserveBalance    target reserve balance
     * @param _targetReserveWeight     target reserve weight, represented in ppm (1-1000000)
     * @param _amount                  source reserve amount
     *
     * @return target reserve amount
     */
    function crossReserveTargetAmount(
        uint256 _sourceReserveBalance,
        uint32 _sourceReserveWeight,
        uint256 _targetReserveBalance,
        uint32 _targetReserveWeight,
        uint256 _amount
    ) public view override returns (uint256) {
        // validate input
        require(
            _sourceReserveBalance > 0 && _targetReserveBalance > 0,
            "ERR_INVALID_RESERVE_BALANCE"
        );
        require(
            _sourceReserveWeight > 0 &&
                _sourceReserveWeight <= MAX_WEIGHT &&
                _targetReserveWeight > 0 &&
                _targetReserveWeight <= MAX_WEIGHT,
            "ERR_INVALID_RESERVE_WEIGHT"
        );

        // special case for equal weights
        if (_sourceReserveWeight == _targetReserveWeight)
            return
                _targetReserveBalance.mul(_amount) /
                _sourceReserveBalance.add(_amount);

        uint256 result;
        uint8 precision;
        uint256 baseN = _sourceReserveBalance.add(_amount);
        (result, precision) = power(
            baseN,
            _sourceReserveBalance,
            _sourceReserveWeight,
            _targetReserveWeight
        );
        uint256 temp1 = _targetReserveBalance.mul(result);
        uint256 temp2 = _targetReserveBalance << precision;
        return (temp1 - temp2) / result;
    }

    /**
     * @dev given a pool token supply, reserve balance, reserve ratio and an amount of requested pool tokens,
     * calculates the amount of reserve tokens required for purchasing the given amount of pool tokens
     *
     * Formula:
     * return = _reserveBalance * (((_supply + _amount) / _supply) ^ (MAX_WEIGHT / _reserveRatio) - 1)
     *
     * @param _supply          pool token supply
     * @param _reserveBalance  reserve balance
     * @param _reserveRatio    reserve ratio, represented in ppm (2-2000000)
     * @param _amount          requested amount of pool tokens
     *
     * @return reserve token amount
     */
    function fundCost(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveRatio,
        uint256 _amount
    ) public view override returns (uint256) {
        // validate input
        require(_supply > 0, "ERR_INVALID_SUPPLY");
        require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
        require(
            _reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2,
            "ERR_INVALID_RESERVE_RATIO"
        );

        // special case for 0 amount
        if (_amount == 0) return 0;

        // special case if the reserve ratio = 100%
        if (_reserveRatio == MAX_WEIGHT)
            return (_amount.mul(_reserveBalance) - 1) / _supply + 1;

        uint256 result;
        uint8 precision;
        uint256 baseN = _supply.add(_amount);
        (result, precision) = power(baseN, _supply, MAX_WEIGHT, _reserveRatio);
        uint256 temp = ((_reserveBalance.mul(result) - 1) >> precision) + 1;
        return temp - _reserveBalance;
    }

    /**
     * @dev given a pool token supply, reserve balance, reserve ratio and an amount of reserve tokens to fund with,
     * calculates the amount of pool tokens received for purchasing with the given amount of reserve tokens
     *
     * Formula:
     * return = _supply * ((_amount / _reserveBalance + 1) ^ (_reserveRatio / MAX_WEIGHT) - 1)
     *
     * @param _supply          pool token supply
     * @param _reserveBalance  reserve balance
     * @param _reserveRatio    reserve ratio, represented in ppm (2-2000000)
     * @param _amount          amount of reserve tokens to fund with
     *
     * @return pool token amount
     */
    function fundSupplyAmount(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveRatio,
        uint256 _amount
    ) public view override returns (uint256) {
        // validate input
        require(_supply > 0, "ERR_INVALID_SUPPLY");
        require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
        require(
            _reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2,
            "ERR_INVALID_RESERVE_RATIO"
        );

        // special case for 0 amount
        if (_amount == 0) return 0;

        // special case if the reserve ratio = 100%
        if (_reserveRatio == MAX_WEIGHT)
            return _amount.mul(_supply) / _reserveBalance;

        uint256 result;
        uint8 precision;
        uint256 baseN = _reserveBalance.add(_amount);
        (result, precision) = power(
            baseN,
            _reserveBalance,
            _reserveRatio,
            MAX_WEIGHT
        );
        uint256 temp = _supply.mul(result) >> precision;
        return temp - _supply;
    }

    /**
     * @dev given a pool token supply, reserve balance, reserve ratio and an amount of pool tokens to liquidate,
     * calculates the amount of reserve tokens received for selling the given amount of pool tokens
     *
     * Formula:
     * return = _reserveBalance * (1 - ((_supply - _amount) / _supply) ^ (MAX_WEIGHT / _reserveRatio))
     *
     * @param _supply          pool token supply
     * @param _reserveBalance  reserve balance
     * @param _reserveRatio    reserve ratio, represented in ppm (2-2000000)
     * @param _amount          amount of pool tokens to liquidate
     *
     * @return reserve token amount
     */
    function liquidateReserveAmount(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveRatio,
        uint256 _amount
    ) public view override returns (uint256) {
        // validate input
        require(_supply > 0, "ERR_INVALID_SUPPLY");
        require(_reserveBalance > 0, "ERR_INVALID_RESERVE_BALANCE");
        require(
            _reserveRatio > 1 && _reserveRatio <= MAX_WEIGHT * 2,
            "ERR_INVALID_RESERVE_RATIO"
        );
        require(_amount <= _supply, "ERR_INVALID_AMOUNT");

        // special case for 0 amount
        if (_amount == 0) return 0;

        // special case for liquidating the entire supply
        if (_amount == _supply) return _reserveBalance;

        // special case if the reserve ratio = 100%
        if (_reserveRatio == MAX_WEIGHT)
            return _amount.mul(_reserveBalance) / _supply;

        uint256 result;
        uint8 precision;
        uint256 baseD = _supply - _amount;
        (result, precision) = power(_supply, baseD, MAX_WEIGHT, _reserveRatio);
        uint256 temp1 = _reserveBalance.mul(result);
        uint256 temp2 = _reserveBalance << precision;
        return (temp1 - temp2) / result;
    }

    /**
     * @dev The arbitrage incentive is to convert to the point where the on-chain price is equal to the off-chain price.
     * We want this operation to also impact the primary reserve balance becoming equal to the primary reserve staked balance.
     * In other words, we want the arbitrager to convert the difference between the reserve balance and the reserve staked balance.
     *
     * Formula input:
     * - let t denote the primary reserve token staked balance
     * - let s denote the primary reserve token balance
     * - let r denote the secondary reserve token balance
     * - let q denote the numerator of the rate between the tokens
     * - let p denote the denominator of the rate between the tokens
     * Where p primary tokens are equal to q secondary tokens
     *
     * Formula output:
     * - compute x = W(t / r * q / p * log(s / t)) / log(s / t)
     * - return x / (1 + x) as the weight of the primary reserve token
     * - return 1 / (1 + x) as the weight of the secondary reserve token
     * Where W is the Lambert W Function
     *
     * If the rate-provider provides the rates for a common unit, for example:
     * - P = 2 ==> 2 primary reserve tokens = 1 ether
     * - Q = 3 ==> 3 secondary reserve tokens = 1 ether
     * Then you can simply use p = P and q = Q
     *
     * If the rate-provider provides the rates for a single unit, for example:
     * - P = 2 ==> 1 primary reserve token = 2 ethers
     * - Q = 3 ==> 1 secondary reserve token = 3 ethers
     * Then you can simply use p = Q and q = P
     *
     * @param _primaryReserveStakedBalance the primary reserve token staked balance
     * @param _primaryReserveBalance       the primary reserve token balance
     * @param _secondaryReserveBalance     the secondary reserve token balance
     * @param _reserveRateNumerator        the numerator of the rate between the tokens
     * @param _reserveRateDenominator      the denominator of the rate between the tokens
     *
     * Note that `numerator / denominator` should represent the amount of secondary tokens equal to one primary token
     *
     * @return the weight of the primary reserve token and the weight of the secondary reserve token, both in ppm (0-1000000)
     */
    function balancedWeights(
        uint256 _primaryReserveStakedBalance,
        uint256 _primaryReserveBalance,
        uint256 _secondaryReserveBalance,
        uint256 _reserveRateNumerator,
        uint256 _reserveRateDenominator
    ) public view override returns (uint32, uint32) {
        if (_primaryReserveStakedBalance == _primaryReserveBalance)
            require(
                _primaryReserveStakedBalance > 0 ||
                    _secondaryReserveBalance > 0,
                "ERR_INVALID_RESERVE_BALANCE"
            );
        else
            require(
                _primaryReserveStakedBalance > 0 &&
                    _primaryReserveBalance > 0 &&
                    _secondaryReserveBalance > 0,
                "ERR_INVALID_RESERVE_BALANCE"
            );
        require(
            _reserveRateNumerator > 0 && _reserveRateDenominator > 0,
            "ERR_INVALID_RESERVE_RATE"
        );

        uint256 tq = _primaryReserveStakedBalance.mul(_reserveRateNumerator);
        uint256 rp = _secondaryReserveBalance.mul(_reserveRateDenominator);

        if (_primaryReserveStakedBalance < _primaryReserveBalance)
            return
                balancedWeightsByStake(
                    _primaryReserveBalance,
                    _primaryReserveStakedBalance,
                    tq,
                    rp,
                    true
                );

        if (_primaryReserveStakedBalance > _primaryReserveBalance)
            return
                balancedWeightsByStake(
                    _primaryReserveStakedBalance,
                    _primaryReserveBalance,
                    tq,
                    rp,
                    false
                );

        return normalizedWeights(tq, rp);
    }



}
          

/https_/github.com/slim12kg/protofire-continous-token-task/blob/master/contracts/interfaces/IBancorFormula.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/*
    Bancor Formula interface
*/
interface IBancorFormula {
    function purchaseTargetAmount(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveWeight,
        uint256 _amount
    ) external view returns (uint256);

    function saleTargetAmount(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveWeight,
        uint256 _amount
    ) external view returns (uint256);

    function crossReserveTargetAmount(
        uint256 _sourceReserveBalance,
        uint32 _sourceReserveWeight,
        uint256 _targetReserveBalance,
        uint32 _targetReserveWeight,
        uint256 _amount
    ) external view returns (uint256);

    function fundCost(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveRatio,
        uint256 _amount
    ) external view returns (uint256);

    function fundSupplyAmount(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveRatio,
        uint256 _amount
    ) external view returns (uint256);

    function liquidateReserveAmount(
        uint256 _supply,
        uint256 _reserveBalance,
        uint32 _reserveRatio,
        uint256 _amount
    ) external view returns (uint256);

    function balancedWeights(
        uint256 _primaryReserveStakedBalance,
        uint256 _primaryReserveBalance,
        uint256 _secondaryReserveBalance,
        uint256 _reserveRateNumerator,
        uint256 _reserveRateDenominator
    ) external view returns (uint32, uint32);
}
          

/https_/github.com/slim12kg/protofire-continous-token-task/blob/master/contracts/utils/Power.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";


contract Power{
    using SafeMath for uint256;


    uint256 private constant ONE = 1;
    uint32 private constant MAX_WEIGHT = 1000000;
    uint8 private constant MIN_PRECISION = 32;
    uint8 private constant MAX_PRECISION = 127;

    // Auto-generated via 'PrintIntScalingFactors.py'
    uint256 private constant FIXED_1 = 0x080000000000000000000000000000000;
    uint256 private constant FIXED_2 = 0x100000000000000000000000000000000;
    uint256 private constant MAX_NUM = 0x200000000000000000000000000000000;

    // Auto-generated via 'PrintLn2ScalingFactors.py'
    uint256 private constant LN2_NUMERATOR = 0x3f80fe03f80fe03f80fe03f80fe03f8;
    uint256 private constant LN2_DENOMINATOR = 0x5b9de1d10bf4103d647b0955897ba80;

    // Auto-generated via 'PrintFunctionOptimalLog.py' and 'PrintFunctionOptimalExp.py'
    uint256 private constant OPT_LOG_MAX_VAL =
        0x15bf0a8b1457695355fb8ac404e7a79e3;
    uint256 private constant OPT_EXP_MAX_VAL =
        0x800000000000000000000000000000000;

    // Auto-generated via 'PrintLambertFactors.py'
    uint256 private constant LAMBERT_CONV_RADIUS =
        0x002f16ac6c59de6f8d5d6f63c1482a7c86;
    uint256 private constant LAMBERT_POS2_SAMPLE =
        0x0003060c183060c183060c183060c18306;
    uint256 private constant LAMBERT_POS2_MAXVAL =
        0x01af16ac6c59de6f8d5d6f63c1482a7c80;
    uint256 private constant LAMBERT_POS3_MAXVAL =
        0x6b22d43e72c326539cceeef8bb48f255ff;

    // Auto-generated via 'PrintWeightFactors.py'
    uint256 private constant MAX_UNF_WEIGHT =
        0x10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea9;

    // Auto-generated via 'PrintMaxExpArray.py'
    uint256[128] private maxExpArray;

    function initMaxExpArray() public {
        maxExpArray[32] = 0x1c35fedd14ffffffffffffffffffffffff;
        maxExpArray[33] = 0x1b0ce43b323fffffffffffffffffffffff;
        maxExpArray[34] = 0x19f0028ec1ffffffffffffffffffffffff;
        maxExpArray[35] = 0x18ded91f0e7fffffffffffffffffffffff;
        maxExpArray[36] = 0x17d8ec7f0417ffffffffffffffffffffff;
        maxExpArray[37] = 0x16ddc6556cdbffffffffffffffffffffff;
        maxExpArray[38] = 0x15ecf52776a1ffffffffffffffffffffff;
        maxExpArray[39] = 0x15060c256cb2ffffffffffffffffffffff;
        maxExpArray[40] = 0x1428a2f98d72ffffffffffffffffffffff;
        maxExpArray[41] = 0x13545598e5c23fffffffffffffffffffff;
        maxExpArray[42] = 0x1288c4161ce1dfffffffffffffffffffff;
        maxExpArray[43] = 0x11c592761c666fffffffffffffffffffff;
        maxExpArray[44] = 0x110a688680a757ffffffffffffffffffff;
        maxExpArray[45] = 0x1056f1b5bedf77ffffffffffffffffffff;
        maxExpArray[46] = 0x0faadceceeff8bffffffffffffffffffff;
        maxExpArray[47] = 0x0f05dc6b27edadffffffffffffffffffff;
        maxExpArray[48] = 0x0e67a5a25da4107fffffffffffffffffff;
        maxExpArray[49] = 0x0dcff115b14eedffffffffffffffffffff;
        maxExpArray[50] = 0x0d3e7a392431239fffffffffffffffffff;
        maxExpArray[51] = 0x0cb2ff529eb71e4fffffffffffffffffff;
        maxExpArray[52] = 0x0c2d415c3db974afffffffffffffffffff;
        maxExpArray[53] = 0x0bad03e7d883f69bffffffffffffffffff;
        maxExpArray[54] = 0x0b320d03b2c343d5ffffffffffffffffff;
        maxExpArray[55] = 0x0abc25204e02828dffffffffffffffffff;
        maxExpArray[56] = 0x0a4b16f74ee4bb207fffffffffffffffff;
        maxExpArray[57] = 0x09deaf736ac1f569ffffffffffffffffff;
        maxExpArray[58] = 0x0976bd9952c7aa957fffffffffffffffff;
        maxExpArray[59] = 0x09131271922eaa606fffffffffffffffff;
        maxExpArray[60] = 0x08b380f3558668c46fffffffffffffffff;
        maxExpArray[61] = 0x0857ddf0117efa215bffffffffffffffff;
        maxExpArray[62] = 0x07ffffffffffffffffffffffffffffffff;
        maxExpArray[63] = 0x07abbf6f6abb9d087fffffffffffffffff;
        maxExpArray[64] = 0x075af62cbac95f7dfa7fffffffffffffff;
        maxExpArray[65] = 0x070d7fb7452e187ac13fffffffffffffff;
        maxExpArray[66] = 0x06c3390ecc8af379295fffffffffffffff;
        maxExpArray[67] = 0x067c00a3b07ffc01fd6fffffffffffffff;
        maxExpArray[68] = 0x0637b647c39cbb9d3d27ffffffffffffff;
        maxExpArray[69] = 0x05f63b1fc104dbd39587ffffffffffffff;
        maxExpArray[70] = 0x05b771955b36e12f7235ffffffffffffff;
        maxExpArray[71] = 0x057b3d49dda84556d6f6ffffffffffffff;
        maxExpArray[72] = 0x054183095b2c8ececf30ffffffffffffff;
        maxExpArray[73] = 0x050a28be635ca2b888f77fffffffffffff;
        maxExpArray[74] = 0x04d5156639708c9db33c3fffffffffffff;
        maxExpArray[75] = 0x04a23105873875bd52dfdfffffffffffff;
        maxExpArray[76] = 0x0471649d87199aa990756fffffffffffff;
        maxExpArray[77] = 0x04429a21a029d4c1457cfbffffffffffff;
        maxExpArray[78] = 0x0415bc6d6fb7dd71af2cb3ffffffffffff;
        maxExpArray[79] = 0x03eab73b3bbfe282243ce1ffffffffffff;
        maxExpArray[80] = 0x03c1771ac9fb6b4c18e229ffffffffffff;
        maxExpArray[81] = 0x0399e96897690418f785257fffffffffff;
        maxExpArray[82] = 0x0373fc456c53bb779bf0ea9fffffffffff;
        maxExpArray[83] = 0x034f9e8e490c48e67e6ab8bfffffffffff;
        maxExpArray[84] = 0x032cbfd4a7adc790560b3337ffffffffff;
        maxExpArray[85] = 0x030b50570f6e5d2acca94613ffffffffff;
        maxExpArray[86] = 0x02eb40f9f620fda6b56c2861ffffffffff;
        maxExpArray[87] = 0x02cc8340ecb0d0f520a6af58ffffffffff;
        maxExpArray[88] = 0x02af09481380a0a35cf1ba02ffffffffff;
        maxExpArray[89] = 0x0292c5bdd3b92ec810287b1b3fffffffff;
        maxExpArray[90] = 0x0277abdcdab07d5a77ac6d6b9fffffffff;
        maxExpArray[91] = 0x025daf6654b1eaa55fd64df5efffffffff;
        maxExpArray[92] = 0x0244c49c648baa98192dce88b7ffffffff;
        maxExpArray[93] = 0x022ce03cd5619a311b2471268bffffffff;
        maxExpArray[94] = 0x0215f77c045fbe885654a44a0fffffffff;
        maxExpArray[95] = 0x01ffffffffffffffffffffffffffffffff;
        maxExpArray[96] = 0x01eaefdbdaaee7421fc4d3ede5ffffffff;
        maxExpArray[97] = 0x01d6bd8b2eb257df7e8ca57b09bfffffff;
        maxExpArray[98] = 0x01c35fedd14b861eb0443f7f133fffffff;
        maxExpArray[99] = 0x01b0ce43b322bcde4a56e8ada5afffffff;
        maxExpArray[100] = 0x019f0028ec1fff007f5a195a39dfffffff;
        maxExpArray[101] = 0x018ded91f0e72ee74f49b15ba527ffffff;
        maxExpArray[102] = 0x017d8ec7f04136f4e5615fd41a63ffffff;
        maxExpArray[103] = 0x016ddc6556cdb84bdc8d12d22e6fffffff;
        maxExpArray[104] = 0x015ecf52776a1155b5bd8395814f7fffff;
        maxExpArray[105] = 0x015060c256cb23b3b3cc3754cf40ffffff;
        maxExpArray[106] = 0x01428a2f98d728ae223ddab715be3fffff;
        maxExpArray[107] = 0x013545598e5c23276ccf0ede68034fffff;
        maxExpArray[108] = 0x01288c4161ce1d6f54b7f61081194fffff;
        maxExpArray[109] = 0x011c592761c666aa641d5a01a40f17ffff;
        maxExpArray[110] = 0x0110a688680a7530515f3e6e6cfdcdffff;
        maxExpArray[111] = 0x01056f1b5bedf75c6bcb2ce8aed428ffff;
        maxExpArray[112] = 0x00faadceceeff8a0890f3875f008277fff;
        maxExpArray[113] = 0x00f05dc6b27edad306388a600f6ba0bfff;
        maxExpArray[114] = 0x00e67a5a25da41063de1495d5b18cdbfff;
        maxExpArray[115] = 0x00dcff115b14eedde6fc3aa5353f2e4fff;
        maxExpArray[116] = 0x00d3e7a3924312399f9aae2e0f868f8fff;
        maxExpArray[117] = 0x00cb2ff529eb71e41582cccd5a1ee26fff;
        maxExpArray[118] = 0x00c2d415c3db974ab32a51840c0b67edff;
        maxExpArray[119] = 0x00bad03e7d883f69ad5b0a186184e06bff;
        maxExpArray[120] = 0x00b320d03b2c343d4829abd6075f0cc5ff;
        maxExpArray[121] = 0x00abc25204e02828d73c6e80bcdb1a95bf;
        maxExpArray[122] = 0x00a4b16f74ee4bb2040a1ec6c15fbbf2df;
        maxExpArray[123] = 0x009deaf736ac1f569deb1b5ae3f36c130f;
        maxExpArray[124] = 0x00976bd9952c7aa957f5937d790ef65037;
        maxExpArray[125] = 0x009131271922eaa6064b73a22d0bd4f2bf;
        maxExpArray[126] = 0x008b380f3558668c46c91c49a2f8e967b9;
        maxExpArray[127] = 0x00857ddf0117efa215952912839f6473e6;
    }

    // Auto-generated via 'PrintLambertArray.py'
    uint256[128] private lambertArray;

    function initLambertArray() public {
        lambertArray[0] = 0x60e393c68d20b1bd09deaabc0373b9c5;
        lambertArray[1] = 0x5f8f46e4854120989ed94719fb4c2011;
        lambertArray[2] = 0x5e479ebb9129fb1b7e72a648f992b606;
        lambertArray[3] = 0x5d0bd23fe42dfedde2e9586be12b85fe;
        lambertArray[4] = 0x5bdb29ddee979308ddfca81aeeb8095a;
        lambertArray[5] = 0x5ab4fd8a260d2c7e2c0d2afcf0009dad;
        lambertArray[6] = 0x5998b31359a55d48724c65cf09001221;
        lambertArray[7] = 0x5885bcad2b322dfc43e8860f9c018cf5;
        lambertArray[8] = 0x577b97aa1fe222bb452fdf111b1f0be2;
        lambertArray[9] = 0x5679cb5e3575632e5baa27e2b949f704;
        lambertArray[10] = 0x557fe8241b3a31c83c732f1cdff4a1c5;
        lambertArray[11] = 0x548d868026504875d6e59bbe95fc2a6b;
        lambertArray[12] = 0x53a2465ce347cf34d05a867c17dd3088;
        lambertArray[13] = 0x52bdce5dcd4faed59c7f5511cf8f8acc;
        lambertArray[14] = 0x51dfcb453c07f8da817606e7885f7c3e;
        lambertArray[15] = 0x5107ef6b0a5a2be8f8ff15590daa3cce;
        lambertArray[16] = 0x5035f241d6eae0cd7bacba119993de7b;
        lambertArray[17] = 0x4f698fe90d5b53d532171e1210164c66;
        lambertArray[18] = 0x4ea288ca297a0e6a09a0eee240e16c85;
        lambertArray[19] = 0x4de0a13fdcf5d4213fc398ba6e3becde;
        lambertArray[20] = 0x4d23a145eef91fec06b06140804c4808;
        lambertArray[21] = 0x4c6b5430d4c1ee5526473db4ae0f11de;
        lambertArray[22] = 0x4bb7886c240562eba11f4963a53b4240;
        lambertArray[23] = 0x4b080f3f1cb491d2d521e0ea4583521e;
        lambertArray[24] = 0x4a5cbc96a05589cb4d86be1db3168364;
        lambertArray[25] = 0x49b566d40243517658d78c33162d6ece;
        lambertArray[26] = 0x4911e6a02e5507a30f947383fd9a3276;
        lambertArray[27] = 0x487216c2b31be4adc41db8a8d5cc0c88;
        lambertArray[28] = 0x47d5d3fc4a7a1b188cd3d788b5c5e9fc;
        lambertArray[29] = 0x473cfce4871a2c40bc4f9e1c32b955d0;
        lambertArray[30] = 0x46a771ca578ab878485810e285e31c67;
        lambertArray[31] = 0x4615149718aed4c258c373dc676aa72d;
        lambertArray[32] = 0x4585c8b3f8fe489c6e1833ca47871384;
        lambertArray[33] = 0x44f972f174e41e5efb7e9d63c29ce735;
        lambertArray[34] = 0x446ff970ba86d8b00beb05ecebf3c4dc;
        lambertArray[35] = 0x43e9438ec88971812d6f198b5ccaad96;
        lambertArray[36] = 0x436539d11ff7bea657aeddb394e809ef;
        lambertArray[37] = 0x42e3c5d3e5a913401d86f66db5d81c2c;
        lambertArray[38] = 0x4264d2395303070ea726cbe98df62174;
        lambertArray[39] = 0x41e84a9a593bb7194c3a6349ecae4eea;
        lambertArray[40] = 0x416e1b785d13eba07a08f3f18876a5ab;
        lambertArray[41] = 0x40f6322ff389d423ba9dd7e7e7b7e809;
        lambertArray[42] = 0x40807cec8a466880ecf4184545d240a4;
        lambertArray[43] = 0x400cea9ce88a8d3ae668e8ea0d9bf07f;
        lambertArray[44] = 0x3f9b6ae8772d4c55091e0ed7dfea0ac1;
        lambertArray[45] = 0x3f2bee253fd84594f54bcaafac383a13;
        lambertArray[46] = 0x3ebe654e95208bb9210c575c081c5958;
        lambertArray[47] = 0x3e52c1fc5665635b78ce1f05ad53c086;
        lambertArray[48] = 0x3de8f65ac388101ddf718a6f5c1eff65;
        lambertArray[49] = 0x3d80f522d59bd0b328ca012df4cd2d49;
        lambertArray[50] = 0x3d1ab193129ea72b23648a161163a85a;
        lambertArray[51] = 0x3cb61f68d32576c135b95cfb53f76d75;
        lambertArray[52] = 0x3c5332d9f1aae851a3619e77e4cc8473;
        lambertArray[53] = 0x3bf1e08edbe2aa109e1525f65759ef73;
        lambertArray[54] = 0x3b921d9cff13fa2c197746a3dfc4918f;
        lambertArray[55] = 0x3b33df818910bfc1a5aefb8f63ae2ac4;
        lambertArray[56] = 0x3ad71c1c77e34fa32a9f184967eccbf6;
        lambertArray[57] = 0x3a7bc9abf2c5bb53e2f7384a8a16521a;
        lambertArray[58] = 0x3a21dec7e76369783a68a0c6385a1c57;
        lambertArray[59] = 0x39c9525de6c9cdf7c1c157ca4a7a6ee3;
        lambertArray[60] = 0x39721bad3dc85d1240ff0190e0adaac3;
        lambertArray[61] = 0x391c324344d3248f0469eb28dd3d77e0;
        lambertArray[62] = 0x38c78df7e3c796279fb4ff84394ab3da;
        lambertArray[63] = 0x387426ea4638ae9aae08049d3554c20a;
        lambertArray[64] = 0x3821f57dbd2763256c1a99bbd2051378;
        lambertArray[65] = 0x37d0f256cb46a8c92ff62fbbef289698;
        lambertArray[66] = 0x37811658591ffc7abdd1feaf3cef9b73;
        lambertArray[67] = 0x37325aa10e9e82f7df0f380f7997154b;
        lambertArray[68] = 0x36e4b888cfb408d873b9a80d439311c6;
        lambertArray[69] = 0x3698299e59f4bb9de645fc9b08c64cca;
        lambertArray[70] = 0x364ca7a5012cb603023b57dd3ebfd50d;
        lambertArray[71] = 0x36022c928915b778ab1b06aaee7e61d4;
        lambertArray[72] = 0x35b8b28d1a73dc27500ffe35559cc028;
        lambertArray[73] = 0x357033e951fe250ec5eb4e60955132d7;
        lambertArray[74] = 0x3528ab2867934e3a21b5412e4c4f8881;
        lambertArray[75] = 0x34e212f66c55057f9676c80094a61d59;
        lambertArray[76] = 0x349c66289e5b3c4b540c24f42fa4b9bb;
        lambertArray[77] = 0x34579fbbd0c733a9c8d6af6b0f7d00f7;
        lambertArray[78] = 0x3413bad2e712288b924b5882b5b369bf;
        lambertArray[79] = 0x33d0b2b56286510ef730e213f71f12e9;
        lambertArray[80] = 0x338e82ce00e2496262c64457535ba1a1;
        lambertArray[81] = 0x334d26a96b373bb7c2f8ea1827f27a92;
        lambertArray[82] = 0x330c99f4f4211469e00b3e18c31475ea;
        lambertArray[83] = 0x32ccd87d6486094999c7d5e6f33237d8;
        lambertArray[84] = 0x328dde2dd617b6665a2e8556f250c1af;
        lambertArray[85] = 0x324fa70e9adc270f8262755af5a99af9;
        lambertArray[86] = 0x32122f443110611ca51040f41fa6e1e3;
        lambertArray[87] = 0x31d5730e42c0831482f0f1485c4263d8;
        lambertArray[88] = 0x31996ec6b07b4a83421b5ebc4ab4e1f1;
        lambertArray[89] = 0x315e1ee0a68ff46bb43ec2b85032e876;
        lambertArray[90] = 0x31237fe7bc4deacf6775b9efa1a145f8;
        lambertArray[91] = 0x30e98e7f1cc5a356e44627a6972ea2ff;
        lambertArray[92] = 0x30b04760b8917ec74205a3002650ec05;
        lambertArray[93] = 0x3077a75c803468e9132ce0cf3224241d;
        lambertArray[94] = 0x303fab57a6a275c36f19cda9bace667a;
        lambertArray[95] = 0x3008504beb8dcbd2cf3bc1f6d5a064f0;
        lambertArray[96] = 0x2fd19346ed17dac61219ce0c2c5ac4b0;
        lambertArray[97] = 0x2f9b7169808c324b5852fd3d54ba9714;
        lambertArray[98] = 0x2f65e7e711cf4b064eea9c08cbdad574;
        lambertArray[99] = 0x2f30f405093042ddff8a251b6bf6d103;
        lambertArray[100] = 0x2efc931a3750f2e8bfe323edfe037574;
        lambertArray[101] = 0x2ec8c28e46dbe56d98685278339400cb;
        lambertArray[102] = 0x2e957fd933c3926d8a599b602379b851;
        lambertArray[103] = 0x2e62c882c7c9ed4473412702f08ba0e5;
        lambertArray[104] = 0x2e309a221c12ba361e3ed695167feee2;
        lambertArray[105] = 0x2dfef25d1f865ae18dd07cfea4bcea10;
        lambertArray[106] = 0x2dcdcee821cdc80decc02c44344aeb31;
        lambertArray[107] = 0x2d9d2d8562b34944d0b201bb87260c83;
        lambertArray[108] = 0x2d6d0c04a5b62a2c42636308669b729a;
        lambertArray[109] = 0x2d3d6842c9a235517fc5a0332691528f;
        lambertArray[110] = 0x2d0e402963fe1ea2834abc408c437c10;
        lambertArray[111] = 0x2cdf91ae602647908aff975e4d6a2a8c;
        lambertArray[112] = 0x2cb15ad3a1eb65f6d74a75da09a1b6c5;
        lambertArray[113] = 0x2c8399a6ab8e9774d6fcff373d210727;
        lambertArray[114] = 0x2c564c4046f64edba6883ca06bbc4535;
        lambertArray[115] = 0x2c2970c431f952641e05cb493e23eed3;
        lambertArray[116] = 0x2bfd0560cd9eb14563bc7c0732856c18;
        lambertArray[117] = 0x2bd1084ed0332f7ff4150f9d0ef41a2c;
        lambertArray[118] = 0x2ba577d0fa1628b76d040b12a82492fb;
        lambertArray[119] = 0x2b7a5233cd21581e855e89dc2f1e8a92;
        lambertArray[120] = 0x2b4f95cd46904d05d72bdcde337d9cc7;
        lambertArray[121] = 0x2b2540fc9b4d9abba3faca6691914675;
        lambertArray[122] = 0x2afb5229f68d0830d8be8adb0a0db70f;
        lambertArray[123] = 0x2ad1c7c63a9b294c5bc73a3ba3ab7a2b;
        lambertArray[124] = 0x2aa8a04ac3cbe1ee1c9c86361465dbb8;
        lambertArray[125] = 0x2a7fda392d725a44a2c8aeb9ab35430d;
        lambertArray[126] = 0x2a57741b18cde618717792b4faa216db;
        lambertArray[127] = 0x2a2f6c81f5d84dd950a35626d6d5503a;
    }

    /**
     * @dev General Description:
     *     Determine a value of precision.
     *     Calculate an integer approximation of (_baseN / _baseD) ^ (_expN / _expD) * 2 ^ precision.
     *     Return the result along with the precision used.
     *
     * Detailed Description:
     *     Instead of calculating "base ^ exp", we calculate "e ^ (log(base) * exp)".
     *     The value of "log(base)" is represented with an integer slightly smaller than "log(base) * 2 ^ precision".
     *     The larger "precision" is, the more accurately this value represents the real value.
     *     However, the larger "precision" is, the more bits are required in order to store this value.
     *     And the exponentiation function, which takes "x" and calculates "e ^ x", is limited to a maximum exponent (maximum value of "x").
     *     This maximum exponent depends on the "precision" used, and it is given by "maxExpArray[precision] >> (MAX_PRECISION - precision)".
     *     Hence we need to determine the highest precision which can be used for the given input, before calling the exponentiation function.
     *     This allows us to compute "base ^ exp" with maximum accuracy and without exceeding 256 bits in any of the intermediate computations.
     *     This functions assumes that "_expN < 2 ^ 256 / log(MAX_NUM - 1)", otherwise the multiplication should be replaced with a "safeMul".
     *     Since we rely on unsigned-integer arithmetic and "base < 1" ==> "log(base) < 0", this function does not support "_baseN < _baseD".
     */
    function power(
        uint256 _baseN,
        uint256 _baseD,
        uint32 _expN,
        uint32 _expD
    ) public view returns (uint256, uint8) {
        require(_baseN < MAX_NUM);

        uint256 baseLog;
        uint256 base = (_baseN * FIXED_1) / _baseD;
        if (base < OPT_LOG_MAX_VAL) {
            baseLog = optimalLog(base);
        } else {
            baseLog = generalLog(base);
        }

        uint256 baseLogTimesExp = (baseLog * _expN) / _expD;
        if (baseLogTimesExp < OPT_EXP_MAX_VAL) {
            return (optimalExp(baseLogTimesExp), MAX_PRECISION);
        } else {
            uint8 precision = findPositionInMaxExpArray(baseLogTimesExp);
            return (
                generalExp(
                    baseLogTimesExp >> (MAX_PRECISION - precision),
                    precision
                ),
                precision
            );
        }
    }

    /**
     * @dev computes log(x / FIXED_1) * FIXED_1.
     * This functions assumes that "x >= FIXED_1", because the output would be negative otherwise.
     */
    function generalLog(uint256 x) internal pure returns (uint256) {
        uint256 res = 0;

        // If x >= 2, then we compute the integer part of log2(x), which is larger than 0.
        if (x >= FIXED_2) {
            uint8 count = floorLog2(x / FIXED_1);
            x >>= count; // now x < 2
            res = count * FIXED_1;
        }

        // If x > 1, then we compute the fraction part of log2(x), which is larger than 0.
        if (x > FIXED_1) {
            for (uint8 i = MAX_PRECISION; i > 0; --i) {
                x = (x * x) / FIXED_1; // now 1 < x < 4
                if (x >= FIXED_2) {
                    x >>= 1; // now 1 < x < 2
                    res += ONE << (i - 1);
                }
            }
        }

        return (res * LN2_NUMERATOR) / LN2_DENOMINATOR;
    }

    /**
     * @dev computes the largest integer smaller than or equal to the binary logarithm of the input.
     */
    function floorLog2(uint256 _n) internal pure returns (uint8) {
        uint8 res = 0;

        if (_n < 256) {
            // At most 8 iterations
            while (_n > 1) {
                _n >>= 1;
                res += 1;
            }
        } else {
            // Exactly 8 iterations
            for (uint8 s = 128; s > 0; s >>= 1) {
                if (_n >= (ONE << s)) {
                    _n >>= s;
                    res |= s;
                }
            }
        }

        return res;
    }

    /**
     * @dev the global "maxExpArray" is sorted in descending order, and therefore the following statements are equivalent:
     * - This function finds the position of [the smallest value in "maxExpArray" larger than or equal to "x"]
     * - This function finds the highest position of [a value in "maxExpArray" larger than or equal to "x"]
     */
    function findPositionInMaxExpArray(uint256 _x)
        internal
        view
        returns (uint8 pos)
    {
        uint8 lo = MIN_PRECISION;
        uint8 hi = MAX_PRECISION;

        while (lo + 1 < hi) {
            uint8 mid = (lo + hi) / 2;
            if (maxExpArray[mid] >= _x) lo = mid;
            else hi = mid;
        }

        if (maxExpArray[hi] >= _x) return pos = hi;
        if (maxExpArray[lo] >= _x) return pos = lo;

        require(false);
    }

    /**
     * @dev this function can be auto-generated by the script 'PrintFunctionGeneralExp.py'.
     * it approximates "e ^ x" via maclaurin summation: "(x^0)/0! + (x^1)/1! + ... + (x^n)/n!".
     * it returns "e ^ (x / 2 ^ precision) * 2 ^ precision", that is, the result is upshifted for accuracy.
     * the global "maxExpArray" maps each "precision" to "((maximumExponent + 1) << (MAX_PRECISION - precision)) - 1".
     * the maximum permitted value for "x" is therefore given by "maxExpArray[precision] >> (MAX_PRECISION - precision)".
     */
    function generalExp(uint256 _x, uint8 _precision)
        internal
        pure
        returns (uint256)
    {
        uint256 xi = _x;
        uint256 res = 0;

        xi = (xi * _x) >> _precision;
        res += xi * 0x3442c4e6074a82f1797f72ac0000000; // add x^02 * (33! / 02!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x116b96f757c380fb287fd0e40000000; // add x^03 * (33! / 03!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x045ae5bdd5f0e03eca1ff4390000000; // add x^04 * (33! / 04!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00defabf91302cd95b9ffda50000000; // add x^05 * (33! / 05!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x002529ca9832b22439efff9b8000000; // add x^06 * (33! / 06!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00054f1cf12bd04e516b6da88000000; // add x^07 * (33! / 07!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000a9e39e257a09ca2d6db51000000; // add x^08 * (33! / 08!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x000012e066e7b839fa050c309000000; // add x^09 * (33! / 09!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x000001e33d7d926c329a1ad1a800000; // add x^10 * (33! / 10!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000002bee513bdb4a6b19b5f800000; // add x^11 * (33! / 11!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00000003a9316fa79b88eccf2a00000; // add x^12 * (33! / 12!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000048177ebe1fa812375200000; // add x^13 * (33! / 13!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000005263fe90242dcbacf00000; // add x^14 * (33! / 14!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x000000000057e22099c030d94100000; // add x^15 * (33! / 15!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000000057e22099c030d9410000; // add x^16 * (33! / 16!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00000000000052b6b54569976310000; // add x^17 * (33! / 17!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00000000000004985f67696bf748000; // add x^18 * (33! / 18!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x000000000000003dea12ea99e498000; // add x^19 * (33! / 19!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00000000000000031880f2214b6e000; // add x^20 * (33! / 20!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x000000000000000025bcff56eb36000; // add x^21 * (33! / 21!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x000000000000000001b722e10ab1000; // add x^22 * (33! / 22!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000000000000001317c70077000; // add x^23 * (33! / 23!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00000000000000000000cba84aafa00; // add x^24 * (33! / 24!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00000000000000000000082573a0a00; // add x^25 * (33! / 25!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00000000000000000000005035ad900; // add x^26 * (33! / 26!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x000000000000000000000002f881b00; // add x^27 * (33! / 27!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000000000000000000001b29340; // add x^28 * (33! / 28!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x00000000000000000000000000efc40; // add x^29 * (33! / 29!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000000000000000000000007fe0; // add x^30 * (33! / 30!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000000000000000000000000420; // add x^31 * (33! / 31!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000000000000000000000000021; // add x^32 * (33! / 32!)
        xi = (xi * _x) >> _precision;
        res += xi * 0x0000000000000000000000000000001; // add x^33 * (33! / 33!)

        return
            res / 0x688589cc0e9505e2f2fee5580000000 + _x + (ONE << _precision); // divide by 33! and then add x^1 / 1! + x^0 / 0!
    }

    /**
     * @dev computes log(x / FIXED_1) * FIXED_1
     * Input range: FIXED_1 <= x <= OPT_LOG_MAX_VAL - 1
     * Auto-generated via 'PrintFunctionOptimalLog.py'
     * Detailed description:
     * - Rewrite the input as a product of natural exponents and a single residual r, such that 1 < r < 2
     * - The natural logarithm of each (pre-calculated) exponent is the degree of the exponent
     * - The natural logarithm of r is calculated via Taylor series for log(1 + x), where x = r - 1
     * - The natural logarithm of the input is calculated by summing up the intermediate results above
     * - For example: log(250) = log(e^4 * e^1 * e^0.5 * 1.021692859) = 4 + 1 + 0.5 + log(1 + 0.021692859)
     */
    function optimalLog(uint256 x) internal pure returns (uint256) {
        uint256 res = 0;

        uint256 y;
        uint256 z;
        uint256 w;

        if (x >= 0xd3094c70f034de4b96ff7d5b6f99fcd8) {
            res += 0x40000000000000000000000000000000;
            x = (x * FIXED_1) / 0xd3094c70f034de4b96ff7d5b6f99fcd8;
        } // add 1 / 2^1
        if (x >= 0xa45af1e1f40c333b3de1db4dd55f29a7) {
            res += 0x20000000000000000000000000000000;
            x = (x * FIXED_1) / 0xa45af1e1f40c333b3de1db4dd55f29a7;
        } // add 1 / 2^2
        if (x >= 0x910b022db7ae67ce76b441c27035c6a1) {
            res += 0x10000000000000000000000000000000;
            x = (x * FIXED_1) / 0x910b022db7ae67ce76b441c27035c6a1;
        } // add 1 / 2^3
        if (x >= 0x88415abbe9a76bead8d00cf112e4d4a8) {
            res += 0x08000000000000000000000000000000;
            x = (x * FIXED_1) / 0x88415abbe9a76bead8d00cf112e4d4a8;
        } // add 1 / 2^4
        if (x >= 0x84102b00893f64c705e841d5d4064bd3) {
            res += 0x04000000000000000000000000000000;
            x = (x * FIXED_1) / 0x84102b00893f64c705e841d5d4064bd3;
        } // add 1 / 2^5
        if (x >= 0x8204055aaef1c8bd5c3259f4822735a2) {
            res += 0x02000000000000000000000000000000;
            x = (x * FIXED_1) / 0x8204055aaef1c8bd5c3259f4822735a2;
        } // add 1 / 2^6
        if (x >= 0x810100ab00222d861931c15e39b44e99) {
            res += 0x01000000000000000000000000000000;
            x = (x * FIXED_1) / 0x810100ab00222d861931c15e39b44e99;
        } // add 1 / 2^7
        if (x >= 0x808040155aabbbe9451521693554f733) {
            res += 0x00800000000000000000000000000000;
            x = (x * FIXED_1) / 0x808040155aabbbe9451521693554f733;
        } // add 1 / 2^8

        z = y = x - FIXED_1;
        w = (y * y) / FIXED_1;
        res +=
            (z * (0x100000000000000000000000000000000 - y)) /
            0x100000000000000000000000000000000;
        z = (z * w) / FIXED_1; // add y^01 / 01 - y^02 / 02
        res +=
            (z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y)) /
            0x200000000000000000000000000000000;
        z = (z * w) / FIXED_1; // add y^03 / 03 - y^04 / 04
        res +=
            (z * (0x099999999999999999999999999999999 - y)) /
            0x300000000000000000000000000000000;
        z = (z * w) / FIXED_1; // add y^05 / 05 - y^06 / 06
        res +=
            (z * (0x092492492492492492492492492492492 - y)) /
            0x400000000000000000000000000000000;
        z = (z * w) / FIXED_1; // add y^07 / 07 - y^08 / 08
        res +=
            (z * (0x08e38e38e38e38e38e38e38e38e38e38e - y)) /
            0x500000000000000000000000000000000;
        z = (z * w) / FIXED_1; // add y^09 / 09 - y^10 / 10
        res +=
            (z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y)) /
            0x600000000000000000000000000000000;
        z = (z * w) / FIXED_1; // add y^11 / 11 - y^12 / 12
        res +=
            (z * (0x089d89d89d89d89d89d89d89d89d89d89 - y)) /
            0x700000000000000000000000000000000;
        z = (z * w) / FIXED_1; // add y^13 / 13 - y^14 / 14
        res +=
            (z * (0x088888888888888888888888888888888 - y)) /
            0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16

        return res;
    }

    /**
     * @dev computes e ^ (x / FIXED_1) * FIXED_1
     * input range: 0 <= x <= OPT_EXP_MAX_VAL - 1
     * auto-generated via 'PrintFunctionOptimalExp.py'
     * Detailed description:
     * - Rewrite the input as a sum of binary exponents and a single residual r, as small as possible
     * - The exponentiation of each binary exponent is given (pre-calculated)
     * - The exponentiation of r is calculated via Taylor series for e^x, where x = r
     * - The exponentiation of the input is calculated by multiplying the intermediate results above
     * - For example: e^5.521692859 = e^(4 + 1 + 0.5 + 0.021692859) = e^4 * e^1 * e^0.5 * e^0.021692859
     */
    function optimalExp(uint256 x) internal pure returns (uint256) {
        uint256 res = 0;

        uint256 y;
        uint256 z;

        z = y = x % 0x10000000000000000000000000000000; // get the input modulo 2^(-3)
        z = (z * y) / FIXED_1;
        res += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!)
        z = (z * y) / FIXED_1;
        res += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!)
        z = (z * y) / FIXED_1;
        res += z * 0x0168244fdac78000; // add y^04 * (20! / 04!)
        z = (z * y) / FIXED_1;
        res += z * 0x004807432bc18000; // add y^05 * (20! / 05!)
        z = (z * y) / FIXED_1;
        res += z * 0x000c0135dca04000; // add y^06 * (20! / 06!)
        z = (z * y) / FIXED_1;
        res += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!)
        z = (z * y) / FIXED_1;
        res += z * 0x000036e0f639b800; // add y^08 * (20! / 08!)
        z = (z * y) / FIXED_1;
        res += z * 0x00000618fee9f800; // add y^09 * (20! / 09!)
        z = (z * y) / FIXED_1;
        res += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!)
        z = (z * y) / FIXED_1;
        res += z * 0x0000000e30dce400; // add y^11 * (20! / 11!)
        z = (z * y) / FIXED_1;
        res += z * 0x000000012ebd1300; // add y^12 * (20! / 12!)
        z = (z * y) / FIXED_1;
        res += z * 0x0000000017499f00; // add y^13 * (20! / 13!)
        z = (z * y) / FIXED_1;
        res += z * 0x0000000001a9d480; // add y^14 * (20! / 14!)
        z = (z * y) / FIXED_1;
        res += z * 0x00000000001c6380; // add y^15 * (20! / 15!)
        z = (z * y) / FIXED_1;
        res += z * 0x000000000001c638; // add y^16 * (20! / 16!)
        z = (z * y) / FIXED_1;
        res += z * 0x0000000000001ab8; // add y^17 * (20! / 17!)
        z = (z * y) / FIXED_1;
        res += z * 0x000000000000017c; // add y^18 * (20! / 18!)
        z = (z * y) / FIXED_1;
        res += z * 0x0000000000000014; // add y^19 * (20! / 19!)
        z = (z * y) / FIXED_1;
        res += z * 0x0000000000000001; // add y^20 * (20! / 20!)
        res = res / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!

        if ((x & 0x010000000000000000000000000000000) != 0)
            res =
                (res * 0x1c3d6a24ed82218787d624d3e5eba95f9) /
                0x18ebef9eac820ae8682b9793ac6d1e776; // multiply by e^2^(-3)
        if ((x & 0x020000000000000000000000000000000) != 0)
            res =
                (res * 0x18ebef9eac820ae8682b9793ac6d1e778) /
                0x1368b2fc6f9609fe7aceb46aa619baed4; // multiply by e^2^(-2)
        if ((x & 0x040000000000000000000000000000000) != 0)
            res =
                (res * 0x1368b2fc6f9609fe7aceb46aa619baed5) /
                0x0bc5ab1b16779be3575bd8f0520a9f21f; // multiply by e^2^(-1)
        if ((x & 0x080000000000000000000000000000000) != 0)
            res =
                (res * 0x0bc5ab1b16779be3575bd8f0520a9f21e) /
                0x0454aaa8efe072e7f6ddbab84b40a55c9; // multiply by e^2^(+0)
        if ((x & 0x100000000000000000000000000000000) != 0)
            res =
                (res * 0x0454aaa8efe072e7f6ddbab84b40a55c5) /
                0x00960aadc109e7a3bf4578099615711ea; // multiply by e^2^(+1)
        if ((x & 0x200000000000000000000000000000000) != 0)
            res =
                (res * 0x00960aadc109e7a3bf4578099615711d7) /
                0x0002bf84208204f5977f9a8cf01fdce3d; // multiply by e^2^(+2)
        if ((x & 0x400000000000000000000000000000000) != 0)
            res =
                (res * 0x0002bf84208204f5977f9a8cf01fdc307) /
                0x0000003c6ab775dd0b95b4cbee7e65d11; // multiply by e^2^(+3)

        return res;
    }

    /**
     * @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
     */
    function lowerStake(uint256 _x) internal view returns (uint256 pos) {
        if (_x <= LAMBERT_CONV_RADIUS) return pos = lambertPos1(_x);
        if (_x <= LAMBERT_POS2_MAXVAL) return pos = lambertPos2(_x);
        if (_x <= LAMBERT_POS3_MAXVAL) return pos = lambertPos3(_x);
        require(false);
    }

    /**
     * @dev computes W(-x / FIXED_1) / (-x / FIXED_1) * FIXED_1
     */
    function higherStake(uint256 _x) internal pure returns (uint256) {
        if (_x <= LAMBERT_CONV_RADIUS) return lambertNeg1(_x);
        return (FIXED_1 * FIXED_1) / _x;
    }

    /**
     * @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
     * input range: 1 <= x <= 1 / e * FIXED_1
     * auto-generated via 'PrintFunctionLambertPos1.py'
     */
    function lambertPos1(uint256 _x) internal pure returns (uint256) {
        uint256 xi = _x;
        uint256 res = (FIXED_1 - _x) * 0xde1bc4d19efcac82445da75b00000000; // x^(1-1) * (34! * 1^(1-1) / 1!) - x^(2-1) * (34! * 2^(2-1) / 2!)

        xi = (xi * _x) / FIXED_1;
        res += xi * 0x00000000014d29a73a6e7b02c3668c7b0880000000; // add x^(03-1) * (34! * 03^(03-1) / 03!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x0000000002504a0cd9a7f7215b60f9be4800000000; // sub x^(04-1) * (34! * 04^(04-1) / 04!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000000000484d0a1191c0ead267967c7a4a0000000; // add x^(05-1) * (34! * 05^(05-1) / 05!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x00000000095ec580d7e8427a4baf26a90a00000000; // sub x^(06-1) * (34! * 06^(06-1) / 06!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000000001440b0be1615a47dba6e5b3b1f10000000; // add x^(07-1) * (34! * 07^(07-1) / 07!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x000000002d207601f46a99b4112418400000000000; // sub x^(08-1) * (34! * 08^(08-1) / 08!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000000066ebaac4c37c622dd8288a7eb1b2000000; // add x^(09-1) * (34! * 09^(09-1) / 09!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x00000000ef17240135f7dbd43a1ba10cf200000000; // sub x^(10-1) * (34! * 10^(10-1) / 10!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000000233c33c676a5eb2416094a87b3657000000; // add x^(11-1) * (34! * 11^(11-1) / 11!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x0000000541cde48bc0254bed49a9f8700000000000; // sub x^(12-1) * (34! * 12^(12-1) / 12!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000000cae1fad2cdd4d4cb8d73abca0d19a400000; // add x^(13-1) * (34! * 13^(13-1) / 13!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x0000001edb2aa2f760d15c41ceedba956400000000; // sub x^(14-1) * (34! * 14^(14-1) / 14!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000004ba8d20d2dabd386c9529659841a2e200000; // add x^(15-1) * (34! * 15^(15-1) / 15!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x000000bac08546b867cdaa20000000000000000000; // sub x^(16-1) * (34! * 16^(16-1) / 16!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000001cfa8e70c03625b9db76c8ebf5bbf24820000; // add x^(17-1) * (34! * 17^(17-1) / 17!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x000004851d99f82060df265f3309b26f8200000000; // sub x^(18-1) * (34! * 18^(18-1) / 18!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x00000b550d19b129d270c44f6f55f027723cbb0000; // add x^(19-1) * (34! * 19^(19-1) / 19!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x00001c877dadc761dc272deb65d4b0000000000000; // sub x^(20-1) * (34! * 20^(20-1) / 20!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000048178ece97479f33a77f2ad22a81b64406c000; // add x^(21-1) * (34! * 21^(21-1) / 21!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x0000b6ca8268b9d810fedf6695ef2f8a6c00000000; // sub x^(22-1) * (34! * 22^(22-1) / 22!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0001d0e76631a5b05d007b8cb72a7c7f11ec36e000; // add x^(23-1) * (34! * 23^(23-1) / 23!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x0004a1c37bd9f85fd9c6c780000000000000000000; // sub x^(24-1) * (34! * 24^(24-1) / 24!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000bd8369f1b702bf491e2ebfcee08250313b65400; // add x^(25-1) * (34! * 25^(25-1) / 25!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x001e5c7c32a9f6c70ab2cb59d9225764d400000000; // sub x^(26-1) * (34! * 26^(26-1) / 26!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x004dff5820e165e910f95120a708e742496221e600; // add x^(27-1) * (34! * 27^(27-1) / 27!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x00c8c8f66db1fced378ee50e536000000000000000; // sub x^(28-1) * (34! * 28^(28-1) / 28!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0205db8dffff45bfa2938f128f599dbf16eb11d880; // add x^(29-1) * (34! * 29^(29-1) / 29!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x053a044ebd984351493e1786af38d39a0800000000; // sub x^(30-1) * (34! * 30^(30-1) / 30!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0d86dae2a4cc0f47633a544479735869b487b59c40; // add x^(31-1) * (34! * 31^(31-1) / 31!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0x231000000000000000000000000000000000000000; // sub x^(32-1) * (34! * 32^(32-1) / 32!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x5b0485a76f6646c2039db1507cdd51b08649680822; // add x^(33-1) * (34! * 33^(33-1) / 33!)
        xi = (xi * _x) / FIXED_1;
        res -= xi * 0xec983c46c49545bc17efa6b5b0055e242200000000; // sub x^(34-1) * (34! * 34^(34-1) / 34!)

        return res / 0xde1bc4d19efcac82445da75b00000000; // divide by 34!
    }

    /**
     * @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
     * input range: LAMBERT_CONV_RADIUS + 1 <= x <= LAMBERT_POS2_MAXVAL
     */
    function lambertPos2(uint256 _x) internal view returns (uint256) {
        uint256 x = _x - LAMBERT_CONV_RADIUS - 1;
        uint256 i = x / LAMBERT_POS2_SAMPLE;
        uint256 a = LAMBERT_POS2_SAMPLE * i;
        uint256 b = LAMBERT_POS2_SAMPLE * (i + 1);
        uint256 c = lambertArray[i];
        uint256 d = lambertArray[i + 1];
        return (c * (b - x) + d * (x - a)) / LAMBERT_POS2_SAMPLE;
    }

    /**
     * @dev computes W(x / FIXED_1) / (x / FIXED_1) * FIXED_1
     * input range: LAMBERT_POS2_MAXVAL + 1 <= x <= LAMBERT_POS3_MAXVAL
     */
    function lambertPos3(uint256 _x) internal pure returns (uint256) {
        uint256 l1 = _x < OPT_LOG_MAX_VAL ? optimalLog(_x) : generalLog(_x);
        uint256 l2 = l1 < OPT_LOG_MAX_VAL ? optimalLog(l1) : generalLog(l1);
        return ((l1 - l2 + (l2 * FIXED_1) / l1) * FIXED_1) / _x;
    }

    /**
     * @dev computes W(-x / FIXED_1) / (-x / FIXED_1) * FIXED_1
     * input range: 1 <= x <= 1 / e * FIXED_1
     * auto-generated via 'PrintFunctionLambertNeg1.py'
     */
    function lambertNeg1(uint256 _x) internal pure returns (uint256) {
        uint256 xi = _x;
        uint256 res = 0;

        xi = (xi * _x) / FIXED_1;
        res += xi * 0x00000000014d29a73a6e7b02c3668c7b0880000000; // add x^(03-1) * (34! * 03^(03-1) / 03!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000000002504a0cd9a7f7215b60f9be4800000000; // add x^(04-1) * (34! * 04^(04-1) / 04!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000000000484d0a1191c0ead267967c7a4a0000000; // add x^(05-1) * (34! * 05^(05-1) / 05!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x00000000095ec580d7e8427a4baf26a90a00000000; // add x^(06-1) * (34! * 06^(06-1) / 06!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000000001440b0be1615a47dba6e5b3b1f10000000; // add x^(07-1) * (34! * 07^(07-1) / 07!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000000002d207601f46a99b4112418400000000000; // add x^(08-1) * (34! * 08^(08-1) / 08!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000000066ebaac4c37c622dd8288a7eb1b2000000; // add x^(09-1) * (34! * 09^(09-1) / 09!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x00000000ef17240135f7dbd43a1ba10cf200000000; // add x^(10-1) * (34! * 10^(10-1) / 10!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000000233c33c676a5eb2416094a87b3657000000; // add x^(11-1) * (34! * 11^(11-1) / 11!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000000541cde48bc0254bed49a9f8700000000000; // add x^(12-1) * (34! * 12^(12-1) / 12!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000000cae1fad2cdd4d4cb8d73abca0d19a400000; // add x^(13-1) * (34! * 13^(13-1) / 13!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000001edb2aa2f760d15c41ceedba956400000000; // add x^(14-1) * (34! * 14^(14-1) / 14!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000004ba8d20d2dabd386c9529659841a2e200000; // add x^(15-1) * (34! * 15^(15-1) / 15!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000000bac08546b867cdaa20000000000000000000; // add x^(16-1) * (34! * 16^(16-1) / 16!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000001cfa8e70c03625b9db76c8ebf5bbf24820000; // add x^(17-1) * (34! * 17^(17-1) / 17!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000004851d99f82060df265f3309b26f8200000000; // add x^(18-1) * (34! * 18^(18-1) / 18!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x00000b550d19b129d270c44f6f55f027723cbb0000; // add x^(19-1) * (34! * 19^(19-1) / 19!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x00001c877dadc761dc272deb65d4b0000000000000; // add x^(20-1) * (34! * 20^(20-1) / 20!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000048178ece97479f33a77f2ad22a81b64406c000; // add x^(21-1) * (34! * 21^(21-1) / 21!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0000b6ca8268b9d810fedf6695ef2f8a6c00000000; // add x^(22-1) * (34! * 22^(22-1) / 22!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0001d0e76631a5b05d007b8cb72a7c7f11ec36e000; // add x^(23-1) * (34! * 23^(23-1) / 23!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0004a1c37bd9f85fd9c6c780000000000000000000; // add x^(24-1) * (34! * 24^(24-1) / 24!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x000bd8369f1b702bf491e2ebfcee08250313b65400; // add x^(25-1) * (34! * 25^(25-1) / 25!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x001e5c7c32a9f6c70ab2cb59d9225764d400000000; // add x^(26-1) * (34! * 26^(26-1) / 26!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x004dff5820e165e910f95120a708e742496221e600; // add x^(27-1) * (34! * 27^(27-1) / 27!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x00c8c8f66db1fced378ee50e536000000000000000; // add x^(28-1) * (34! * 28^(28-1) / 28!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0205db8dffff45bfa2938f128f599dbf16eb11d880; // add x^(29-1) * (34! * 29^(29-1) / 29!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x053a044ebd984351493e1786af38d39a0800000000; // add x^(30-1) * (34! * 30^(30-1) / 30!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x0d86dae2a4cc0f47633a544479735869b487b59c40; // add x^(31-1) * (34! * 31^(31-1) / 31!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x231000000000000000000000000000000000000000; // add x^(32-1) * (34! * 32^(32-1) / 32!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0x5b0485a76f6646c2039db1507cdd51b08649680822; // add x^(33-1) * (34! * 33^(33-1) / 33!)
        xi = (xi * _x) / FIXED_1;
        res += xi * 0xec983c46c49545bc17efa6b5b0055e242200000000; // add x^(34-1) * (34! * 34^(34-1) / 34!)

        return res / 0xde1bc4d19efcac82445da75b00000000 + _x + FIXED_1; // divide by 34! and then add x^(2-1) * (34! * 2^(2-1) / 2!) + x^(1-1) * (34! * 1^(1-1) / 1!)
    }

    /**
     * @dev computes the weights based on "W(log(hi / lo) * tq / rp) * tq / rp", where "W" is a variation of the Lambert W function.
     */
    function balancedWeightsByStake(
        uint256 _hi,
        uint256 _lo,
        uint256 _tq,
        uint256 _rp,
        bool _lowerStake
    ) public view returns (uint32, uint32) {
        (_tq, _rp) = safeFactors(_tq, _rp);
        uint256 f = _hi.mul(FIXED_1) / _lo;
        uint256 g = f < OPT_LOG_MAX_VAL ? optimalLog(f) : generalLog(f);
        uint256 x = g.mul(_tq) / _rp;
        uint256 y = _lowerStake ? lowerStake(x) : higherStake(x);
        return normalizedWeights(y.mul(_tq), _rp.mul(FIXED_1));
    }

    /**
     * @dev reduces "a" and "b" while maintaining their ratio.
     */
    function safeFactors(uint256 _a, uint256 _b)
        public
        pure
        returns (uint256, uint256)
    {
        if (_a <= FIXED_2 && _b <= FIXED_2) return (_a, _b);
        if (_a < FIXED_2) return ((_a * FIXED_2) / _b, FIXED_2);
        if (_b < FIXED_2) return (FIXED_2, (_b * FIXED_2) / _a);
        uint256 c = _a > _b ? _a : _b;
        uint256 n = floorLog2(c / FIXED_1);
        return (_a >> n, _b >> n);
    }

    /**
     * @dev computes "MAX_WEIGHT * a / (a + b)" and "MAX_WEIGHT * b / (a + b)".
     */
    function normalizedWeights(uint256 _a, uint256 _b)
        public
        pure
        returns (uint32, uint32)
    {
        if (_a <= _b) return accurateWeights(_a, _b);
        (uint32 y, uint32 x) = accurateWeights(_b, _a);
        return (x, y);
    }

    /**
     * @dev computes "MAX_WEIGHT * a / (a + b)" and "MAX_WEIGHT * b / (a + b)", assuming that "a <= b".
     */
    function accurateWeights(uint256 _a, uint256 _b)
        public
        pure
        returns (uint32, uint32)
    {
        if (_a > MAX_UNF_WEIGHT) {
            uint256 c = _a / (MAX_UNF_WEIGHT + 1) + 1;
            _a /= c;
            _b /= c;
        }
        uint256 x = roundDiv(_a * MAX_WEIGHT, _a.add(_b));
        uint256 y = MAX_WEIGHT - x;
        return (uint32(x), uint32(y));
    }

    /**
     * @dev computes the nearest integer to a given quotient without overflowing or underflowing.
     */
    function roundDiv(uint256 _n, uint256 _d) internal pure returns (uint256) {
        return _n / _d + (_n % _d) / (_d - _d / 2);
    }

}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"uint256","name":"_reserveRatio","internalType":"uint256"},{"type":"address","name":"_reserveTokenAddress","internalType":"address"}]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ContinuousBurn","inputs":[{"type":"address","name":"_address","internalType":"address","indexed":false},{"type":"uint256","name":"continuousTokenAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"reserveTokenAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"ContinuousMint","inputs":[{"type":"address","name":"_address","internalType":"address","indexed":false},{"type":"uint256","name":"reserveTokenAmount","internalType":"uint256","indexed":false},{"type":"uint256","name":"continuousTokenAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint32","name":"","internalType":"uint32"},{"type":"uint32","name":"","internalType":"uint32"}],"name":"accurateWeights","inputs":[{"type":"uint256","name":"_a","internalType":"uint256"},{"type":"uint256","name":"_b","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"},{"type":"uint32","name":"","internalType":"uint32"}],"name":"balancedWeights","inputs":[{"type":"uint256","name":"_primaryReserveStakedBalance","internalType":"uint256"},{"type":"uint256","name":"_primaryReserveBalance","internalType":"uint256"},{"type":"uint256","name":"_secondaryReserveBalance","internalType":"uint256"},{"type":"uint256","name":"_reserveRateNumerator","internalType":"uint256"},{"type":"uint256","name":"_reserveRateDenominator","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint32","name":"","internalType":"uint32"},{"type":"uint32","name":"","internalType":"uint32"}],"name":"balancedWeightsByStake","inputs":[{"type":"uint256","name":"_hi","internalType":"uint256"},{"type":"uint256","name":"_lo","internalType":"uint256"},{"type":"uint256","name":"_tq","internalType":"uint256"},{"type":"uint256","name":"_rp","internalType":"uint256"},{"type":"bool","name":"_lowerStake","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"burn","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"burnAmount","internalType":"uint256"}],"name":"calculateContinuousBurnReturn","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"mintAmount","internalType":"uint256"}],"name":"calculateContinuousMintReturn","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"crossReserveTargetAmount","inputs":[{"type":"uint256","name":"_sourceReserveBalance","internalType":"uint256"},{"type":"uint32","name":"_sourceReserveWeight","internalType":"uint32"},{"type":"uint256","name":"_targetReserveBalance","internalType":"uint256"},{"type":"uint32","name":"_targetReserveWeight","internalType":"uint32"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"subtractedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"fundCost","inputs":[{"type":"uint256","name":"_supply","internalType":"uint256"},{"type":"uint256","name":"_reserveBalance","internalType":"uint256"},{"type":"uint32","name":"_reserveRatio","internalType":"uint32"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"fundSupplyAmount","inputs":[{"type":"uint256","name":"_supply","internalType":"uint256"},{"type":"uint256","name":"_reserveBalance","internalType":"uint256"},{"type":"uint32","name":"_reserveRatio","internalType":"uint32"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"addedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"init","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initLambertArray","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initMaxExpArray","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"liquidateReserveAmount","inputs":[{"type":"uint256","name":"_supply","internalType":"uint256"},{"type":"uint256","name":"_reserveBalance","internalType":"uint256"},{"type":"uint32","name":"_reserveRatio","internalType":"uint32"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"_amountMinted","internalType":"uint256"}],"name":"mint","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint32","name":"","internalType":"uint32"},{"type":"uint32","name":"","internalType":"uint32"}],"name":"normalizedWeights","inputs":[{"type":"uint256","name":"_a","internalType":"uint256"},{"type":"uint256","name":"_b","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint8","name":"","internalType":"uint8"}],"name":"power","inputs":[{"type":"uint256","name":"_baseN","internalType":"uint256"},{"type":"uint256","name":"_baseD","internalType":"uint256"},{"type":"uint32","name":"_expN","internalType":"uint32"},{"type":"uint32","name":"_expD","internalType":"uint32"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"purchaseTargetAmount","inputs":[{"type":"uint256","name":"_supply","internalType":"uint256"},{"type":"uint256","name":"_reserveBalance","internalType":"uint256"},{"type":"uint32","name":"_reserveWeight","internalType":"uint32"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"reserveBalance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"reserveRatio","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"reserveTokenAddress","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"safeFactors","inputs":[{"type":"uint256","name":"_a","internalType":"uint256"},{"type":"uint256","name":"_b","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"saleTargetAmount","inputs":[{"type":"uint256","name":"_supply","internalType":"uint256"},{"type":"uint256","name":"_reserveBalance","internalType":"uint256"},{"type":"uint32","name":"_reserveWeight","internalType":"uint32"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"scale","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]}]
              

Contract Creation Code

0x6080604052670de0b6b3a76400006101858190556200002090600a62000330565b610186553480156200003157600080fd5b506040516200620138038062006201833981016040819052620000549162000298565b604080518082018252600f81526e21b7b73a34b737bab9902a37b5b2b760891b602080830191825283518085019094526003845262544f4b60e81b908401528151919291620000a79161018391620001f2565b508051620000be90610184906020840190620001f2565b50505061018782905561018880546001600160a01b0319166001600160a01b0383161790556101855462000101903390620000fb90600162000330565b62000109565b5050620003a5565b6001600160a01b0382166200013b5760405162461bcd60e51b81526004016200013290620002d5565b60405180910390fd5b6200014960008383620001ed565b8061018260008282546200015e919062000315565b90915550506001600160a01b03821660009081526101806020526040812080548392906200018e90849062000315565b90915550506040516001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90620001d39085906200030c565b60405180910390a3620001e960008383620001ed565b5050565b505050565b828054620002009062000352565b90600052602060002090601f0160209004810192826200022457600085556200026f565b82601f106200023f57805160ff19168380011785556200026f565b828001600101855582156200026f579182015b828111156200026f57825182559160200191906001019062000252565b506200027d92915062000281565b5090565b5b808211156200027d576000815560010162000282565b60008060408385031215620002ab578182fd5b825160208401519092506001600160a01b0381168114620002ca578182fd5b809150509250929050565b6020808252601f908201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604082015260600190565b90815260200190565b600082198211156200032b576200032b6200038f565b500190565b60008160001904831182151516156200034d576200034d6200038f565b500290565b6002810460018216806200036757607f821691505b602082108114156200038957634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b615e4c80620003b56000396000f3fe608060405234801561001057600080fd5b50600436106102065760003560e01c806394491fab1161011a578063a67453b0116100ad578063e1c7392a1161007c578063e1c7392a14610443578063ebbb21581461044b578063ee1ea4ff1461045e578063f3250fe214610473578063f51e181a1461048657610206565b8063a67453b0146103f4578063a9059cbb14610415578063aa248a4614610428578063dd62ed3e1461043057610206565b8063a11aa1b4116100e9578063a11aa1b4146103b3578063a179dd93146103c6578063a3882291146103ce578063a457c2d7146103e157610206565b806394491fab1461037d57806395d89b4114610390578063a0712d6814610398578063a10954fe146103ab57610206565b8063313ce5671161019d57806370a082311161016c57806370a082311461031e57806376c2d5101461033157806376cf0b56146103445780638074590a1461035757806390f0c2ea1461036a57610206565b8063313ce567146102c057806332833d51146102d557806339509351146102f657806342966c681461030957610206565b806323b872dd116101d957806323b872dd1461026657806328c3d701146102795780632f55bdb51461028c57806330c7d6371461029f57610206565b806306fdde031461020b578063095ea7b3146102295780630c7d5cd81461024957806318160ddd1461025e575b600080fd5b61021361048e565b60405161022091906156bc565b60405180910390f35b61023c61023736600461543b565b610521565b60405161022091906156b1565b61025161053e565b6040516102209190615c28565b610251610545565b61023c610274366004615400565b61054c565b610251610287366004615480565b6105e6565b61025161029a366004615557565b610609565b6102b26102ad3660046154b0565b610720565b604051610220929190615c50565b6102c86107e6565b6040516102209190615c67565b6102e86102e3366004615593565b6107eb565b604051610220929190615c3f565b61023c61030436600461543b565b6108d8565b61031c610317366004615480565b61092d565b005b61025161032c3660046153b4565b6109c3565b6102b261033f3660046154b0565b6109df565b610251610352366004615557565b610a15565b610251610365366004615557565b610b45565b610251610378366004615480565b610c29565b61025161038b3660046155d8565b610c44565b610213610d68565b6102516103a6366004615480565b610d78565b610251610f0b565b6102b26103c136600461551d565b610f12565b61031c61101f565b6102b26103dc3660046154d1565b611a26565b61023c6103ef36600461543b565b611aee565b6104076104023660046154b0565b611b68565b604051610220929190615c31565b61023c61042336600461543b565b611c28565b61031c611c3c565b61025161043e3660046153ce565b612412565b61031c61243e565b610251610459366004615557565b612450565b61046661257e565b6040516102209190615625565b610251610481366004615557565b61258e565b610251612641565b6060610183805461049e90615d8a565b80601f01602080910402602001604051908101604052809291908181526020018280546104ca90615d8a565b80156105175780601f106104ec57610100808354040283529160200191610517565b820191906000526020600020905b8154815290600101906020018083116104fa57829003601f168201915b5050505050905090565b600061053561052e612648565b848461264c565b50600192915050565b6101875481565b6101825490565b6000610559848484612701565b6001600160a01b0384166000908152610181602052604081208161057b612648565b6001600160a01b03166001600160a01b03168152602001908152602001600020549050828110156105c75760405162461bcd60e51b81526004016105be9061595b565b60405180910390fd5b6105db856105d3612648565b85840361264c565b506001949350505050565b60006106016105f3610545565b61018654610187548561258e565b90505b919050565b600080851161062a5760405162461bcd60e51b81526004016105be906158f8565b6000841161064a5760405162461bcd60e51b81526004016105be9061588a565b60018363ffffffff161180156106795750610669620f42406002615d07565b63ffffffff168363ffffffff1611155b6106955760405162461bcd60e51b81526004016105be90615924565b816106a257506000610718565b63ffffffff8316620f424014156106cf57836106be838761282d565b6106c89190615cb2565b9050610718565b600080806106dd8786612840565b90506106ee818888620f42406107eb565b9093509150600060ff83166107038a8661282d565b901c90506107118982615d33565b9450505050505b949350505050565b6000807d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea98411156107a55760006107747d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea96001615c75565b61077e9086615cb2565b610789906001615c75565b90506107958186615cb2565b94506107a18185615cb2565b9350505b60006107c66107b7620f424087615ce8565b6107c18787612840565b61284c565b905060006107d782620f4240615d33565b919350909150505b9250929050565b601290565b600080600160811b86106107fe57600080fd5b600080866108106001607f1b8a615ce8565b61081a9190615cb2565b905070015bf0a8b1457695355fb8ac404e7a79e38110156108455761083e8161288b565b9150610851565b61084e81612e04565b91505b60008563ffffffff168763ffffffff168461086c9190615ce8565b6108769190615cb2565b9050600160831b81101561089b5761088d81612eed565b607f945094505050506108cf565b60006108a682613518565b90506108c26108b682607f615d4a565b60ff1683901c826135f8565b955093506108cf92505050565b94509492505050565b60006105356108e5612648565b848461018160006108f4612648565b6001600160a01b03908116825260208083019390935260409182016000908120918b16815292529020546109289190615c75565b61264c565b600061093882613c56565b6101885460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb9061096c9033908590600401615677565b602060405180830381600087803b15801561098657600080fd5b505af115801561099a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109be9190615464565b505050565b6001600160a01b03166000908152610180602052604090205490565b6000808284116109fc576109f38484610720565b915091506107df565b600080610a098587610720565b97909650945050505050565b6000808511610a365760405162461bcd60e51b81526004016105be906158f8565b60008411610a565760405162461bcd60e51b81526004016105be9061588a565b60008363ffffffff16118015610a755750620f424063ffffffff841611155b610a915760405162461bcd60e51b81526004016105be90615b3e565b84821115610ab15760405162461bcd60e51b81526004016105be90615a97565b81610abe57506000610718565b84821415610acd575082610718565b63ffffffff8316620f42401415610ae957846106be858461282d565b60008080610af78589615d33565b9050610b088882620f4240896107eb565b90935091506000610b19888561282d565b905060ff831688901b84610b2d8284615d33565b610b379190615cb2565b9a9950505050505050505050565b6000808511610b665760405162461bcd60e51b81526004016105be906158f8565b60008411610b865760405162461bcd60e51b81526004016105be9061588a565b60018363ffffffff16118015610bb55750610ba5620f42406002615d07565b63ffffffff168363ffffffff1611155b610bd15760405162461bcd60e51b81526004016105be90615924565b84821115610bf15760405162461bcd60e51b81526004016105be90615a97565b81610bfe57506000610718565b84821415610c0d575082610718565b63ffffffff8316620f42401415610ae957846106be838661282d565b6000610601610c36610545565b610186546101875485610a15565b60008086118015610c555750600084115b610c715760405162461bcd60e51b81526004016105be9061588a565b60008563ffffffff16118015610c905750620f424063ffffffff861611155b8015610ca2575060008363ffffffff16115b8015610cb75750620f424063ffffffff841611155b610cd35760405162461bcd60e51b81526004016105be90615b3e565b8263ffffffff168563ffffffff161415610d0c57610cf18683612840565b610cfb858461282d565b610d059190615cb2565b9050610d5f565b60008080610d1a8986612840565b9050610d28818a8a896107eb565b90935091506000610d39888561282d565b905060ff831688901b84610d4d8284615d33565b610d579190615cb2565b955050505050505b95945050505050565b6060610184805461049e90615d8a565b61018854604051636eb1769f60e11b815260009182916001600160a01b039091169063dd62ed3e90610db09033903090600401615639565b60206040518083038186803b158015610dc857600080fd5b505afa158015610ddc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e009190615498565b905060008111610e225760405162461bcd60e51b81526004016105be906158c1565b82811015610e425760405162461bcd60e51b81526004016105be906159a3565b610188546040516323b872dd60e01b81526000916001600160a01b0316906323b872dd90610e7890339030908790600401615653565b602060405180830381600087803b158015610e9257600080fd5b505af1158015610ea6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eca9190615464565b90508015610ee457610edb82613d0a565b92505050610604565b60008211610f045760405162461bcd60e51b81526004016105be90615794565b5050919050565b6101865481565b60008085871415610f4d576000871180610f2c5750600085115b610f485760405162461bcd60e51b81526004016105be9061588a565b610f85565b600087118015610f5d5750600086115b8015610f695750600085115b610f855760405162461bcd60e51b81526004016105be9061588a565b600084118015610f955750600083115b610fb15760405162461bcd60e51b81526004016105be90615b75565b6000610fbd888661282d565b90506000610fcb878661282d565b905087891015610fee57610fe3888a84846001611a26565b935093505050611015565b8789111561100457610fe3898984846000611a26565b61100e82826109df565b9350935050505b9550959350505050565b6f60e393c68d20b1bd09deaabc0373b9c560809081556f5f8f46e4854120989ed94719fb4c20116081556f5e479ebb9129fb1b7e72a648f992b6066082556f5d0bd23fe42dfedde2e9586be12b85fe6083556f5bdb29ddee979308ddfca81aeeb8095a6084556f5ab4fd8a260d2c7e2c0d2afcf0009dad6085556f5998b31359a55d48724c65cf090012216086556f5885bcad2b322dfc43e8860f9c018cf56087556f577b97aa1fe222bb452fdf111b1f0be26088556f5679cb5e3575632e5baa27e2b949f7046089556f557fe8241b3a31c83c732f1cdff4a1c5608a556f548d868026504875d6e59bbe95fc2a6b608b556f53a2465ce347cf34d05a867c17dd3088608c556f52bdce5dcd4faed59c7f5511cf8f8acc608d556f51dfcb453c07f8da817606e7885f7c3e608e556f5107ef6b0a5a2be8f8ff15590daa3cce608f556f5035f241d6eae0cd7bacba119993de7b6090556f4f698fe90d5b53d532171e1210164c666091556f4ea288ca297a0e6a09a0eee240e16c856092556f4de0a13fdcf5d4213fc398ba6e3becde6093556f4d23a145eef91fec06b06140804c48086094556f4c6b5430d4c1ee5526473db4ae0f11de6095556f4bb7886c240562eba11f4963a53b42406096556f4b080f3f1cb491d2d521e0ea4583521e6097556f4a5cbc96a05589cb4d86be1db31683646098556f49b566d40243517658d78c33162d6ece6099556f4911e6a02e5507a30f947383fd9a3276609a556f487216c2b31be4adc41db8a8d5cc0c88609b556f47d5d3fc4a7a1b188cd3d788b5c5e9fc609c556f473cfce4871a2c40bc4f9e1c32b955d0609d556f46a771ca578ab878485810e285e31c67609e556f4615149718aed4c258c373dc676aa72d609f556f4585c8b3f8fe489c6e1833ca4787138460a0556f44f972f174e41e5efb7e9d63c29ce73560a1556f446ff970ba86d8b00beb05ecebf3c4dc60a2556f43e9438ec88971812d6f198b5ccaad9660a3556f436539d11ff7bea657aeddb394e809ef60a4556f42e3c5d3e5a913401d86f66db5d81c2c60a5556f4264d2395303070ea726cbe98df6217460a6556f41e84a9a593bb7194c3a6349ecae4eea60a7556f416e1b785d13eba07a08f3f18876a5ab60a8556f40f6322ff389d423ba9dd7e7e7b7e80960a9556f40807cec8a466880ecf4184545d240a460aa556f400cea9ce88a8d3ae668e8ea0d9bf07f60ab556f3f9b6ae8772d4c55091e0ed7dfea0ac160ac556f3f2bee253fd84594f54bcaafac383a1360ad556f3ebe654e95208bb9210c575c081c595860ae556f3e52c1fc5665635b78ce1f05ad53c08660af556f3de8f65ac388101ddf718a6f5c1eff6560b0556f3d80f522d59bd0b328ca012df4cd2d4960b1556f3d1ab193129ea72b23648a161163a85a60b2556f3cb61f68d32576c135b95cfb53f76d7560b3556f3c5332d9f1aae851a3619e77e4cc847360b4556f3bf1e08edbe2aa109e1525f65759ef7360b5556f3b921d9cff13fa2c197746a3dfc4918f60b6556f3b33df818910bfc1a5aefb8f63ae2ac460b7556f3ad71c1c77e34fa32a9f184967eccbf660b8556f3a7bc9abf2c5bb53e2f7384a8a16521a60b9556f3a21dec7e76369783a68a0c6385a1c5760ba556f39c9525de6c9cdf7c1c157ca4a7a6ee360bb556f39721bad3dc85d1240ff0190e0adaac360bc556f391c324344d3248f0469eb28dd3d77e060bd556f38c78df7e3c796279fb4ff84394ab3da60be556f387426ea4638ae9aae08049d3554c20a60bf556f3821f57dbd2763256c1a99bbd205137860c0556f37d0f256cb46a8c92ff62fbbef28969860c1556f37811658591ffc7abdd1feaf3cef9b7360c2556f37325aa10e9e82f7df0f380f7997154b60c3556f36e4b888cfb408d873b9a80d439311c660c4556f3698299e59f4bb9de645fc9b08c64cca60c5556f364ca7a5012cb603023b57dd3ebfd50d60c6556f36022c928915b778ab1b06aaee7e61d460c7556f35b8b28d1a73dc27500ffe35559cc02860c8556f357033e951fe250ec5eb4e60955132d760c9556f3528ab2867934e3a21b5412e4c4f888160ca556f34e212f66c55057f9676c80094a61d5960cb556f349c66289e5b3c4b540c24f42fa4b9bb60cc556f34579fbbd0c733a9c8d6af6b0f7d00f760cd556f3413bad2e712288b924b5882b5b369bf60ce556f33d0b2b56286510ef730e213f71f12e960cf556f338e82ce00e2496262c64457535ba1a160d0556f334d26a96b373bb7c2f8ea1827f27a9260d1556f330c99f4f4211469e00b3e18c31475ea60d2556f32ccd87d6486094999c7d5e6f33237d860d3556f328dde2dd617b6665a2e8556f250c1af60d4556f324fa70e9adc270f8262755af5a99af960d5556f32122f443110611ca51040f41fa6e1e360d6556f31d5730e42c0831482f0f1485c4263d860d7556f31996ec6b07b4a83421b5ebc4ab4e1f160d8556f315e1ee0a68ff46bb43ec2b85032e87660d9556f31237fe7bc4deacf6775b9efa1a145f860da556f30e98e7f1cc5a356e44627a6972ea2ff60db556f30b04760b8917ec74205a3002650ec0560dc556f3077a75c803468e9132ce0cf3224241d60dd556f303fab57a6a275c36f19cda9bace667a60de556f3008504beb8dcbd2cf3bc1f6d5a064f060df556f2fd19346ed17dac61219ce0c2c5ac4b060e0556f2f9b7169808c324b5852fd3d54ba971460e1556f2f65e7e711cf4b064eea9c08cbdad57460e2556f2f30f405093042ddff8a251b6bf6d10360e3556f2efc931a3750f2e8bfe323edfe03757460e4556f2ec8c28e46dbe56d98685278339400cb60e5556f2e957fd933c3926d8a599b602379b85160e6556f2e62c882c7c9ed4473412702f08ba0e560e7556f2e309a221c12ba361e3ed695167feee260e8556f2dfef25d1f865ae18dd07cfea4bcea1060e9556f2dcdcee821cdc80decc02c44344aeb3160ea556f2d9d2d8562b34944d0b201bb87260c8360eb556f2d6d0c04a5b62a2c42636308669b729a60ec556f2d3d6842c9a235517fc5a0332691528f60ed556f2d0e402963fe1ea2834abc408c437c1060ee556f2cdf91ae602647908aff975e4d6a2a8c60ef556f2cb15ad3a1eb65f6d74a75da09a1b6c560f0556f2c8399a6ab8e9774d6fcff373d21072760f1556f2c564c4046f64edba6883ca06bbc453560f2556f2c2970c431f952641e05cb493e23eed360f3556f2bfd0560cd9eb14563bc7c0732856c1860f4556f2bd1084ed0332f7ff4150f9d0ef41a2c60f5556f2ba577d0fa1628b76d040b12a82492fb60f6556f2b7a5233cd21581e855e89dc2f1e8a9260f7556f2b4f95cd46904d05d72bdcde337d9cc760f8556f2b2540fc9b4d9abba3faca669191467560f9556f2afb5229f68d0830d8be8adb0a0db70f60fa556f2ad1c7c63a9b294c5bc73a3ba3ab7a2b60fb556f2aa8a04ac3cbe1ee1c9c86361465dbb860fc556f2a7fda392d725a44a2c8aeb9ab35430d60fd556f2a57741b18cde618717792b4faa216db60fe556f2a2f6c81f5d84dd950a35626d6d5503a90607f5b0155565b600080611a338585611b68565b9095509350600086611a49896001607f1b61282d565b611a539190615cb2565b9050600070015bf0a8b1457695355fb8ac404e7a79e38210611a7d57611a7882612e04565b611a86565b611a868261288b565b9050600086611a95838a61282d565b611a9f9190615cb2565b9050600086611ab657611ab182613d88565b611abf565b611abf82613dca565b9050611adc611ace828b61282d565b61033f8a6001607f1b61282d565b95509550505050509550959350505050565b6000806101816000611afe612648565b6001600160a01b0390811682526020808301939093526040918201600090812091881681529252902054905082811015611b4a5760405162461bcd60e51b81526004016105be90615bac565b611b5e611b55612648565b8585840361264c565b5060019392505050565b600080600160801b8411158015611b835750600160801b8311155b15611b925750829050816107df565b600160801b841015611bc55782611bad600160801b86615ce8565b611bb79190615cb2565b600160801b915091506107df565b600160801b831015611beb57600160801b84611be18286615ce8565b6109f39190615cb2565b6000838511611bfa5783611bfc565b845b90506000611c16611c116001607f1b84615cb2565b613e2e565b60ff1695861c969490951c9450505050565b6000610535611c35612648565b8484612701565b701c35fedd14ffffffffffffffffffffffff602055701b0ce43b323fffffffffffffffffffffff6021557019f0028ec1ffffffffffffffffffffffff6022557018ded91f0e7fffffffffffffffffffffff6023557017d8ec7f0417ffffffffffffffffffffff6024557016ddc6556cdbffffffffffffffffffffff6025557015ecf52776a1ffffffffffffffffffffff6026557015060c256cb2ffffffffffffffffffffff602755701428a2f98d72ffffffffffffffffffffff6028557013545598e5c23fffffffffffffffffffff602955701288c4161ce1dfffffffffffffffffffff602a557011c592761c666fffffffffffffffffffff602b5570110a688680a757ffffffffffffffffffff602c55701056f1b5bedf77ffffffffffffffffffff602d55700faadceceeff8bffffffffffffffffffff602e55700f05dc6b27edadffffffffffffffffffff602f55700e67a5a25da4107fffffffffffffffffff603055700dcff115b14eedffffffffffffffffffff603155700d3e7a392431239fffffffffffffffffff603255700cb2ff529eb71e4fffffffffffffffffff603355700c2d415c3db974afffffffffffffffffff603455700bad03e7d883f69bffffffffffffffffff603555700b320d03b2c343d5ffffffffffffffffff603655700abc25204e02828dffffffffffffffffff603755700a4b16f74ee4bb207fffffffffffffffff6038557009deaf736ac1f569ffffffffffffffffff603955700976bd9952c7aa957fffffffffffffffff603a557009131271922eaa606fffffffffffffffff603b557008b380f3558668c46fffffffffffffffff603c55700857ddf0117efa215bffffffffffffffff603d557007ffffffffffffffffffffffffffffffff603e557007abbf6f6abb9d087fffffffffffffffff603f5570075af62cbac95f7dfa7fffffffffffffff60405570070d7fb7452e187ac13fffffffffffffff6041557006c3390ecc8af379295fffffffffffffff60425570067c00a3b07ffc01fd6fffffffffffffff604355700637b647c39cbb9d3d27ffffffffffffff6044557005f63b1fc104dbd39587ffffffffffffff6045557005b771955b36e12f7235ffffffffffffff60465570057b3d49dda84556d6f6ffffffffffffff60475570054183095b2c8ececf30ffffffffffffff60485570050a28be635ca2b888f77fffffffffffff6049557004d5156639708c9db33c3fffffffffffff604a557004a23105873875bd52dfdfffffffffffff604b55700471649d87199aa990756fffffffffffff604c557004429a21a029d4c1457cfbffffffffffff604d55700415bc6d6fb7dd71af2cb3ffffffffffff604e557003eab73b3bbfe282243ce1ffffffffffff604f557003c1771ac9fb6b4c18e229ffffffffffff605055700399e96897690418f785257fffffffffff605155700373fc456c53bb779bf0ea9fffffffffff60525570034f9e8e490c48e67e6ab8bfffffffffff60535570032cbfd4a7adc790560b3337ffffffffff60545570030b50570f6e5d2acca94613ffffffffff6055557002eb40f9f620fda6b56c2861ffffffffff6056557002cc8340ecb0d0f520a6af58ffffffffff6057557002af09481380a0a35cf1ba02ffffffffff605855700292c5bdd3b92ec810287b1b3fffffffff605955700277abdcdab07d5a77ac6d6b9fffffffff605a5570025daf6654b1eaa55fd64df5efffffffff605b55700244c49c648baa98192dce88b7ffffffff605c5570022ce03cd5619a311b2471268bffffffff605d55700215f77c045fbe885654a44a0fffffffff605e557001ffffffffffffffffffffffffffffffff605f557001eaefdbdaaee7421fc4d3ede5ffffffff6060557001d6bd8b2eb257df7e8ca57b09bfffffff6061557001c35fedd14b861eb0443f7f133fffffff6062557001b0ce43b322bcde4a56e8ada5afffffff60635570019f0028ec1fff007f5a195a39dfffffff60645570018ded91f0e72ee74f49b15ba527ffffff60655570017d8ec7f04136f4e5615fd41a63ffffff60665570016ddc6556cdb84bdc8d12d22e6fffffff60675570015ecf52776a1155b5bd8395814f7fffff60685570015060c256cb23b3b3cc3754cf40ffffff6069557001428a2f98d728ae223ddab715be3fffff606a5570013545598e5c23276ccf0ede68034fffff606b557001288c4161ce1d6f54b7f61081194fffff606c5570011c592761c666aa641d5a01a40f17ffff606d55700110a688680a7530515f3e6e6cfdcdffff606e557001056f1b5bedf75c6bcb2ce8aed428ffff606f556ffaadceceeff8a0890f3875f008277fff6070556ff05dc6b27edad306388a600f6ba0bfff6071556fe67a5a25da41063de1495d5b18cdbfff6072556fdcff115b14eedde6fc3aa5353f2e4fff6073556fd3e7a3924312399f9aae2e0f868f8fff6074556fcb2ff529eb71e41582cccd5a1ee26fff6075556fc2d415c3db974ab32a51840c0b67edff6076556fbad03e7d883f69ad5b0a186184e06bff6077556fb320d03b2c343d4829abd6075f0cc5ff6078556fabc25204e02828d73c6e80bcdb1a95bf6079556fa4b16f74ee4bb2040a1ec6c15fbbf2df607a556f9deaf736ac1f569deb1b5ae3f36c130f607b556f976bd9952c7aa957f5937d790ef65037607c556f9131271922eaa6064b73a22d0bd4f2bf607d556f8b380f3558668c46c91c49a2f8e967b9607e556f857ddf0117efa215952912839f6473e66000607f611a22565b6001600160a01b0391821660009081526101816020908152604080832093909416825291909152205490565b612446611c3c565b61244e61101f565b565b60008085116124715760405162461bcd60e51b81526004016105be906158f8565b600084116124915760405162461bcd60e51b81526004016105be9061588a565b60018363ffffffff161180156124c057506124b0620f42406002615d07565b63ffffffff168363ffffffff1611155b6124dc5760405162461bcd60e51b81526004016105be90615924565b816124e957506000610718565b63ffffffff8316620f4240141561252657846001612507848761282d565b6125119190615d33565b61251b9190615cb2565b6106c8906001615c75565b600080806125348886612840565b90506125458189620f4240896107eb565b9093509150600060ff8316600161255c8a8761282d565b6125669190615d33565b612572911c6001615c75565b90506107118882615d33565b610188546001600160a01b031681565b60008085116125af5760405162461bcd60e51b81526004016105be906158f8565b600084116125cf5760405162461bcd60e51b81526004016105be9061588a565b60008363ffffffff161180156125ee5750620f424063ffffffff841611155b61260a5760405162461bcd60e51b81526004016105be90615b3e565b8161261757506000610718565b63ffffffff8316620f4240141561263357836106be868461282d565b600080806106dd8588612840565b6101855481565b3390565b6001600160a01b0383166126725760405162461bcd60e51b81526004016105be90615ac3565b6001600160a01b0382166126985760405162461bcd60e51b81526004016105be906157cb565b6001600160a01b038084166000818152610181602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906126f4908590615c28565b60405180910390a3505050565b6001600160a01b0383166127275760405162461bcd60e51b81526004016105be90615a52565b6001600160a01b03821661274d5760405162461bcd60e51b81526004016105be9061570f565b6127588383836109be565b6001600160a01b03831660009081526101806020526040902054818110156127925760405162461bcd60e51b81526004016105be90615844565b6001600160a01b03808516600090815261018060205260408082208585039055918516815290812080548492906127ca908490615c75565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516128149190615c28565b60405180910390a36128278484846109be565b50505050565b60006128398284615ce8565b9392505050565b60006128398284615c75565b6000612859600283615cb2565b6128639083615d33565b61286d8385615dc5565b6128779190615cb2565b6128818385615cb2565b6128399190615c75565b6000808080806fd3094c70f034de4b96ff7d5b6f99fcd886106128e4576128b66001607e1b85615c75565b93506fd3094c70f034de4b96ff7d5b6f99fcd86128d76001607f1b88615ce8565b6128e19190615cb2565b95505b6fa45af1e1f40c333b3de1db4dd55f29a78610612937576129096001607d1b85615c75565b93506fa45af1e1f40c333b3de1db4dd55f29a761292a6001607f1b88615ce8565b6129349190615cb2565b95505b6f910b022db7ae67ce76b441c27035c6a1861061298a5761295c6001607c1b85615c75565b93506f910b022db7ae67ce76b441c27035c6a161297d6001607f1b88615ce8565b6129879190615cb2565b95505b6f88415abbe9a76bead8d00cf112e4d4a886106129dd576129af6001607b1b85615c75565b93506f88415abbe9a76bead8d00cf112e4d4a86129d06001607f1b88615ce8565b6129da9190615cb2565b95505b6f84102b00893f64c705e841d5d4064bd38610612a3057612a026001607a1b85615c75565b93506f84102b00893f64c705e841d5d4064bd3612a236001607f1b88615ce8565b612a2d9190615cb2565b95505b6f8204055aaef1c8bd5c3259f4822735a28610612a8357612a55600160791b85615c75565b93506f8204055aaef1c8bd5c3259f4822735a2612a766001607f1b88615ce8565b612a809190615cb2565b95505b6f810100ab00222d861931c15e39b44e998610612ad657612aa8600160781b85615c75565b93506f810100ab00222d861931c15e39b44e99612ac96001607f1b88615ce8565b612ad39190615cb2565b95505b6f808040155aabbbe9451521693554f7338610612b2957612afb600160771b85615c75565b93506f808040155aabbbe9451521693554f733612b1c6001607f1b88615ce8565b612b269190615cb2565b95505b612b376001607f1b87615d33565b92508291506001607f1b612b4b8380615ce8565b612b559190615cb2565b9050600160801b612b668482615d33565b612b709084615ce8565b612b7a9190615cb2565b612b849085615c75565b93506001607f1b612b958284615ce8565b612b9f9190615cb2565b9150600160811b612bc0846faaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa615d33565b612bca9084615ce8565b612bd49190615cb2565b612bde9085615c75565b93506001607f1b612bef8284615ce8565b612bf99190615cb2565b9150600360801b612c1a846f99999999999999999999999999999999615d33565b612c249084615ce8565b612c2e9190615cb2565b612c389085615c75565b93506001607f1b612c498284615ce8565b612c539190615cb2565b9150600160821b612c74846f92492492492492492492492492492492615d33565b612c7e9084615ce8565b612c889190615cb2565b612c929085615c75565b93506001607f1b612ca38284615ce8565b612cad9190615cb2565b9150600560801b612cce846f8e38e38e38e38e38e38e38e38e38e38e615d33565b612cd89084615ce8565b612ce29190615cb2565b612cec9085615c75565b93506001607f1b612cfd8284615ce8565b612d079190615cb2565b9150600360811b612d28846f8ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b615d33565b612d329084615ce8565b612d3c9190615cb2565b612d469085615c75565b93506001607f1b612d578284615ce8565b612d619190615cb2565b9150600760801b612d82846f89d89d89d89d89d89d89d89d89d89d89615d33565b612d8c9084615ce8565b612d969190615cb2565b612da09085615c75565b93506001607f1b612db18284615ce8565b612dbb9190615cb2565b9150600160831b612ddc846f88888888888888888888888888888888615d33565b612de69084615ce8565b612df09190615cb2565b612dfa9085615c75565b9695505050505050565b600080600160801b8310612e43576000612e25611c116001607f1b86615cb2565b60ff811694851c94909150612e3f906001607f1b90615ce8565b9150505b6001607f1b831115612eb857607f5b60ff811615612eb6576001607f1b612e6a8580615ce8565b612e749190615cb2565b9350600160801b8410612ea657600193841c93612e919082615d4a565b60ff166001901b82612ea39190615c75565b91505b612eaf81615d6d565b9050612e52565b505b6f05b9de1d10bf4103d647b0955897ba80612ee36f03f80fe03f80fe03f80fe03f80fe03f883615ce8565b6128399190615cb2565b6000808080612f006001607c1b86615dc5565b91508190506001607f1b612f148280615ce8565b612f1e9190615cb2565b9050612f32816710e1b3be415a0000615ce8565b612f3c9084615c75565b92506001607f1b612f4d8383615ce8565b612f579190615cb2565b9050612f6b816705a0913f6b1e0000615ce8565b612f759084615c75565b92506001607f1b612f868383615ce8565b612f909190615cb2565b9050612fa481670168244fdac78000615ce8565b612fae9084615c75565b92506001607f1b612fbf8383615ce8565b612fc99190615cb2565b9050612fdc81664807432bc18000615ce8565b612fe69084615c75565b92506001607f1b612ff78383615ce8565b6130019190615cb2565b905061301481660c0135dca04000615ce8565b61301e9084615c75565b92506001607f1b61302f8383615ce8565b6130399190615cb2565b905061304c816601b707b1cdc000615ce8565b6130569084615c75565b92506001607f1b6130678383615ce8565b6130719190615cb2565b9050613083816536e0f639b800615ce8565b61308d9084615c75565b92506001607f1b61309e8383615ce8565b6130a89190615cb2565b90506130ba81650618fee9f800615ce8565b6130c49084615c75565b92506001607f1b6130d58383615ce8565b6130df9190615cb2565b90506130f081649c197dcc00615ce8565b6130fa9084615c75565b92506001607f1b61310b8383615ce8565b6131159190615cb2565b905061312681640e30dce400615ce8565b6131309084615c75565b92506001607f1b6131418383615ce8565b61314b9190615cb2565b905061315c8164012ebd1300615ce8565b6131669084615c75565b92506001607f1b6131778383615ce8565b6131819190615cb2565b9050613191816317499f00615ce8565b61319b9084615c75565b92506001607f1b6131ac8383615ce8565b6131b69190615cb2565b90506131c6816301a9d480615ce8565b6131d09084615c75565b92506001607f1b6131e18383615ce8565b6131eb9190615cb2565b90506131fa81621c6380615ce8565b6132049084615c75565b92506001607f1b6132158383615ce8565b61321f9190615cb2565b905061322e816201c638615ce8565b6132389084615c75565b92506001607f1b6132498383615ce8565b6132539190615cb2565b905061326181611ab8615ce8565b61326b9084615c75565b92506001607f1b61327c8383615ce8565b6132869190615cb2565b90506132948161017c615ce8565b61329e9084615c75565b92506001607f1b6132af8383615ce8565b6132b99190615cb2565b90506132c6816014615ce8565b6132d09084615c75565b92506001607f1b6132e18383615ce8565b6132eb9190615cb2565b90506132f8816001615ce8565b6133029084615c75565b92506001607f1b8261331c6721c3677c82b4000086615cb2565b6133269190615c75565b6133309190615c75565b92506001607c1b8516156133785770018ebef9eac820ae8682b9793ac6d1e77661336b847001c3d6a24ed82218787d624d3e5eba95f9615ce8565b6133759190615cb2565b92505b6001607d1b8516156133be577001368b2fc6f9609fe7aceb46aa619baed46133b18470018ebef9eac820ae8682b9793ac6d1e778615ce8565b6133bb9190615cb2565b92505b6001607e1b851615613403576fbc5ab1b16779be3575bd8f0520a9f21f6133f6847001368b2fc6f9609fe7aceb46aa619baed5615ce8565b6134009190615cb2565b92505b6001607f1b851615613447576f454aaa8efe072e7f6ddbab84b40a55c961343a846fbc5ab1b16779be3575bd8f0520a9f21e615ce8565b6134449190615cb2565b92505b600160801b85161561348b576f0960aadc109e7a3bf4578099615711ea61347e846f454aaa8efe072e7f6ddbab84b40a55c5615ce8565b6134889190615cb2565b92505b600160811b8516156134ce576e2bf84208204f5977f9a8cf01fdce3d6134c1846f0960aadc109e7a3bf4578099615711d7615ce8565b6134cb9190615cb2565b92505b600160821b85161561350f576d03c6ab775dd0b95b4cbee7e65d11613502846e2bf84208204f5977f9a8cf01fdc307615ce8565b61350c9190615cb2565b92505b50909392505050565b60006020607f5b60ff811661352e836001615c8d565b60ff16101561358f57600060026135458385615c8d565b61354f9190615cc6565b90508460008260ff166080811061357657634e487b7160e01b600052603260045260246000fd5b01541061358557809250613589565b8091505b5061351f565b8360008260ff16608081106135b457634e487b7160e01b600052603260045260246000fd5b0154106135c45791506106049050565b8360008360ff16608081106135e957634e487b7160e01b600052603260045260246000fd5b01541061020657509050610604565b6000828160ff841661360a8380615ce8565b901c9150613628826f03442c4e6074a82f1797f72ac0000000615ce8565b6136329082615c75565b905060ff84166136428684615ce8565b901c9150613660826f0116b96f757c380fb287fd0e40000000615ce8565b61366a9082615c75565b905060ff841661367a8684615ce8565b901c9150613697826e45ae5bdd5f0e03eca1ff4390000000615ce8565b6136a19082615c75565b905060ff84166136b18684615ce8565b901c91506136ce826e0defabf91302cd95b9ffda50000000615ce8565b6136d89082615c75565b905060ff84166136e88684615ce8565b901c9150613705826e02529ca9832b22439efff9b8000000615ce8565b61370f9082615c75565b905060ff841661371f8684615ce8565b901c915061373b826d54f1cf12bd04e516b6da88000000615ce8565b6137459082615c75565b905060ff84166137558684615ce8565b901c9150613771826d0a9e39e257a09ca2d6db51000000615ce8565b61377b9082615c75565b905060ff841661378b8684615ce8565b901c91506137a7826d012e066e7b839fa050c309000000615ce8565b6137b19082615c75565b905060ff84166137c18684615ce8565b901c91506137dc826c1e33d7d926c329a1ad1a800000615ce8565b6137e69082615c75565b905060ff84166137f68684615ce8565b901c9150613811826c02bee513bdb4a6b19b5f800000615ce8565b61381b9082615c75565b905060ff841661382b8684615ce8565b901c9150613845826b3a9316fa79b88eccf2a00000615ce8565b61384f9082615c75565b905060ff841661385f8684615ce8565b901c9150613879826b048177ebe1fa812375200000615ce8565b6138839082615c75565b905060ff84166138938684615ce8565b901c91506138ac826a5263fe90242dcbacf00000615ce8565b6138b69082615c75565b905060ff84166138c68684615ce8565b901c91506138df826a057e22099c030d94100000615ce8565b6138e99082615c75565b905060ff84166138f98684615ce8565b901c9150613911826957e22099c030d9410000615ce8565b61391b9082615c75565b905060ff841661392b8684615ce8565b901c91506139438269052b6b54569976310000615ce8565b61394d9082615c75565b905060ff841661395d8684615ce8565b901c915061397482684985f67696bf748000615ce8565b61397e9082615c75565b905060ff841661398e8684615ce8565b901c91506139a5826803dea12ea99e498000615ce8565b6139af9082615c75565b905060ff84166139bf8684615ce8565b901c91506139d5826731880f2214b6e000615ce8565b6139df9082615c75565b905060ff84166139ef8684615ce8565b901c9150613a058267025bcff56eb36000615ce8565b613a0f9082615c75565b905060ff8416613a1f8684615ce8565b901c9150613a3482661b722e10ab1000615ce8565b613a3e9082615c75565b905060ff8416613a4e8684615ce8565b901c9150613a63826601317c70077000615ce8565b613a6d9082615c75565b905060ff8416613a7d8684615ce8565b901c9150613a9182650cba84aafa00615ce8565b613a9b9082615c75565b905060ff8416613aab8684615ce8565b901c9150613abe826482573a0a00615ce8565b613ac89082615c75565b905060ff8416613ad88684615ce8565b901c9150613aeb826405035ad900615ce8565b613af59082615c75565b905060ff8416613b058684615ce8565b901c9150613b1782632f881b00615ce8565b613b219082615c75565b905060ff8416613b318684615ce8565b901c9150613b43826301b29340615ce8565b613b4d9082615c75565b905060ff8416613b5d8684615ce8565b901c9150613b6e82620efc40615ce8565b613b789082615c75565b905060ff8416613b888684615ce8565b901c9150613b9882617fe0615ce8565b613ba29082615c75565b905060ff8416613bb28684615ce8565b901c9150613bc282610420615ce8565b613bcc9082615c75565b905060ff8416613bdc8684615ce8565b901c9150613beb826021615ce8565b613bf59082615c75565b905060ff8416613c058684615ce8565b901c9150613c14826001615ce8565b613c1e9082615c75565b9050600160ff85161b85613c426f0688589cc0e9505e2f2fee558000000084615cb2565b613c4c9190615c75565b610d5f9190615c75565b6000808211613c775760405162461bcd60e51b81526004016105be90615b07565b81613c81336109c3565b1015613c9f5760405162461bcd60e51b81526004016105be906159da565b6000613caa83610c29565b9050613cb63384613e9a565b61018654613cc49082613f8e565b610186556040517f42cdee5ad59c78ee53adb50e8d3f4b66918ce660505ab55abb44035e4c79ad1d90613cfc90339086908590615690565b60405180910390a192915050565b6000808211613d2b5760405162461bcd60e51b81526004016105be9061580d565b6000613d36836105e6565b9050613d423382613f9a565b61018654613d509084612840565b610186556040517fa9b5c37908cbe11fc228c10af0ce98bf944b069510207388ac875a3d2a314bbe90613cfc90339086908590615690565b60006f2f16ac6c59de6f8d5d6f63c1482a7c868211613db157613daa82614068565b9050610604565b81613dc06001607f1b80615ce8565b6106019190615cb2565b60006f2f16ac6c59de6f8d5d6f63c1482a7c868211613dec57613daa826148fb565b7001af16ac6c59de6f8d5d6f63c1482a7c808211613e0d57613daa8261519e565b706b22d43e72c326539cceeef8bb48f255ff821161020657613daa826152df565b600080610100831015613e61575b6001831115613e5c57600192831c92613e559082615c8d565b9050613e3c565b610601565b60805b60ff811615613e9357600160ff82161b8410613e885760ff81169390931c92908117905b60011c607f16613e64565b5092915050565b6001600160a01b038216613ec05760405162461bcd60e51b81526004016105be90615a11565b613ecc826000836109be565b6001600160a01b0382166000908152610180602052604090205481811015613f065760405162461bcd60e51b81526004016105be90615752565b6001600160a01b03831660009081526101806020526040812083830390556101828054849290613f37908490615d33565b90915550506040516000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90613f7a908690615c28565b60405180910390a36109be836000846109be565b60006128398284615d33565b6001600160a01b038216613fc05760405162461bcd60e51b81526004016105be90615bf1565b613fcc600083836109be565b806101826000828254613fdf9190615c75565b90915550506001600160a01b038216600090815261018060205260408120805483929061400d908490615c75565b90915550506040516001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90614050908590615c28565b60405180910390a3614064600083836109be565b5050565b600081816001607f1b61407b8380615ce8565b6140859190615cb2565b91506140a28270014d29a73a6e7b02c3668c7b0880000000615ce8565b6140ac9082615c75565b90506001607f1b6140bd8584615ce8565b6140c79190615cb2565b91506140e4827002504a0cd9a7f7215b60f9be4800000000615ce8565b6140ee9082615c75565b90506001607f1b6140ff8584615ce8565b6141099190615cb2565b915061412682700484d0a1191c0ead267967c7a4a0000000615ce8565b6141309082615c75565b90506001607f1b6141418584615ce8565b61414b9190615cb2565b91506141688270095ec580d7e8427a4baf26a90a00000000615ce8565b6141729082615c75565b90506001607f1b6141838584615ce8565b61418d9190615cb2565b91506141aa82701440b0be1615a47dba6e5b3b1f10000000615ce8565b6141b49082615c75565b90506001607f1b6141c58584615ce8565b6141cf9190615cb2565b91506141ec82702d207601f46a99b4112418400000000000615ce8565b6141f69082615c75565b90506001607f1b6142078584615ce8565b6142119190615cb2565b915061422e827066ebaac4c37c622dd8288a7eb1b2000000615ce8565b6142389082615c75565b90506001607f1b6142498584615ce8565b6142539190615cb2565b91506142708270ef17240135f7dbd43a1ba10cf200000000615ce8565b61427a9082615c75565b90506001607f1b61428b8584615ce8565b6142959190615cb2565b91506142b382710233c33c676a5eb2416094a87b3657000000615ce8565b6142bd9082615c75565b90506001607f1b6142ce8584615ce8565b6142d89190615cb2565b91506142f682710541cde48bc0254bed49a9f8700000000000615ce8565b6143009082615c75565b90506001607f1b6143118584615ce8565b61431b9190615cb2565b915061433982710cae1fad2cdd4d4cb8d73abca0d19a400000615ce8565b6143439082615c75565b90506001607f1b6143548584615ce8565b61435e9190615cb2565b915061437c82711edb2aa2f760d15c41ceedba956400000000615ce8565b6143869082615c75565b90506001607f1b6143978584615ce8565b6143a19190615cb2565b91506143bf82714ba8d20d2dabd386c9529659841a2e200000615ce8565b6143c99082615c75565b90506001607f1b6143da8584615ce8565b6143e49190615cb2565b91506143fc826805d6042a35c33e6d51604d1b615ce8565b6144069082615c75565b90506001607f1b6144178584615ce8565b6144219190615cb2565b9150614440827201cfa8e70c03625b9db76c8ebf5bbf24820000615ce8565b61444a9082615c75565b90506001607f1b61445b8584615ce8565b6144659190615cb2565b9150614484827204851d99f82060df265f3309b26f8200000000615ce8565b61448e9082615c75565b90506001607f1b61449f8584615ce8565b6144a99190615cb2565b91506144c882720b550d19b129d270c44f6f55f027723cbb0000615ce8565b6144d29082615c75565b90506001607f1b6144e38584615ce8565b6144ed9190615cb2565b915061450c82721c877dadc761dc272deb65d4b0000000000000615ce8565b6145169082615c75565b90506001607f1b6145278584615ce8565b6145319190615cb2565b9150614550827248178ece97479f33a77f2ad22a81b64406c000615ce8565b61455a9082615c75565b90506001607f1b61456b8584615ce8565b6145759190615cb2565b91506145948272b6ca8268b9d810fedf6695ef2f8a6c00000000615ce8565b61459e9082615c75565b90506001607f1b6145af8584615ce8565b6145b99190615cb2565b91506145d9827301d0e76631a5b05d007b8cb72a7c7f11ec36e000615ce8565b6145e39082615c75565b90506001607f1b6145f48584615ce8565b6145fe9190615cb2565b91506146178269094386f7b3f0bfb38d8f604f1b615ce8565b6146219082615c75565b90506001607f1b6146328584615ce8565b61463c9190615cb2565b915061465c82730bd8369f1b702bf491e2ebfcee08250313b65400615ce8565b6146669082615c75565b90506001607f1b6146778584615ce8565b6146819190615cb2565b91506146a182731e5c7c32a9f6c70ab2cb59d9225764d400000000615ce8565b6146ab9082615c75565b90506001607f1b6146bc8584615ce8565b6146c69190615cb2565b91506146e682734dff5820e165e910f95120a708e742496221e600615ce8565b6146f09082615c75565b90506001607f1b6147018584615ce8565b61470b9190615cb2565b9150614727826c064647b36d8fe769bc7728729b603d1b615ce8565b6147319082615c75565b90506001607f1b6147428584615ce8565b61474c9190615cb2565b915061476d82740205db8dffff45bfa2938f128f599dbf16eb11d880615ce8565b6147779082615c75565b90506001607f1b6147888584615ce8565b6147929190615cb2565b91506147b38274053a044ebd984351493e1786af38d39a0800000000615ce8565b6147bd9082615c75565b90506001607f1b6147ce8584615ce8565b6147d89190615cb2565b91506147f982740d86dae2a4cc0f47633a544479735869b487b59c40615ce8565b6148039082615c75565b90506001607f1b6148148584615ce8565b61481e9190615cb2565b915061482f82610231609c1b615ce8565b6148399082615c75565b90506001607f1b61484a8584615ce8565b6148549190615cb2565b915061487582745b0485a76f6646c2039db1507cdd51b08649680822615ce8565b61487f9082615c75565b90506001607f1b6148908584615ce8565b61489a9190615cb2565b91506148bb8274ec983c46c49545bc17efa6b5b0055e242200000000615ce8565b6148c59082615c75565b90506001607f1b846148e76fde1bc4d19efcac82445da75b0000000084615cb2565b6148f19190615c75565b6107189190615c75565b6000818161490d826001607f1b615d33565b614927906fde1bc4d19efcac82445da75b00000000615ce8565b90506001607f1b6149388584615ce8565b6149429190615cb2565b915061495f8270014d29a73a6e7b02c3668c7b0880000000615ce8565b6149699082615c75565b90506001607f1b61497a8584615ce8565b6149849190615cb2565b91506149a1827002504a0cd9a7f7215b60f9be4800000000615ce8565b6149ab9082615d33565b90506001607f1b6149bc8584615ce8565b6149c69190615cb2565b91506149e382700484d0a1191c0ead267967c7a4a0000000615ce8565b6149ed9082615c75565b90506001607f1b6149fe8584615ce8565b614a089190615cb2565b9150614a258270095ec580d7e8427a4baf26a90a00000000615ce8565b614a2f9082615d33565b90506001607f1b614a408584615ce8565b614a4a9190615cb2565b9150614a6782701440b0be1615a47dba6e5b3b1f10000000615ce8565b614a719082615c75565b90506001607f1b614a828584615ce8565b614a8c9190615cb2565b9150614aa982702d207601f46a99b4112418400000000000615ce8565b614ab39082615d33565b90506001607f1b614ac48584615ce8565b614ace9190615cb2565b9150614aeb827066ebaac4c37c622dd8288a7eb1b2000000615ce8565b614af59082615c75565b90506001607f1b614b068584615ce8565b614b109190615cb2565b9150614b2d8270ef17240135f7dbd43a1ba10cf200000000615ce8565b614b379082615d33565b90506001607f1b614b488584615ce8565b614b529190615cb2565b9150614b7082710233c33c676a5eb2416094a87b3657000000615ce8565b614b7a9082615c75565b90506001607f1b614b8b8584615ce8565b614b959190615cb2565b9150614bb382710541cde48bc0254bed49a9f8700000000000615ce8565b614bbd9082615d33565b90506001607f1b614bce8584615ce8565b614bd89190615cb2565b9150614bf682710cae1fad2cdd4d4cb8d73abca0d19a400000615ce8565b614c009082615c75565b90506001607f1b614c118584615ce8565b614c1b9190615cb2565b9150614c3982711edb2aa2f760d15c41ceedba956400000000615ce8565b614c439082615d33565b90506001607f1b614c548584615ce8565b614c5e9190615cb2565b9150614c7c82714ba8d20d2dabd386c9529659841a2e200000615ce8565b614c869082615c75565b90506001607f1b614c978584615ce8565b614ca19190615cb2565b9150614cb9826805d6042a35c33e6d51604d1b615ce8565b614cc39082615d33565b90506001607f1b614cd48584615ce8565b614cde9190615cb2565b9150614cfd827201cfa8e70c03625b9db76c8ebf5bbf24820000615ce8565b614d079082615c75565b90506001607f1b614d188584615ce8565b614d229190615cb2565b9150614d41827204851d99f82060df265f3309b26f8200000000615ce8565b614d4b9082615d33565b90506001607f1b614d5c8584615ce8565b614d669190615cb2565b9150614d8582720b550d19b129d270c44f6f55f027723cbb0000615ce8565b614d8f9082615c75565b90506001607f1b614da08584615ce8565b614daa9190615cb2565b9150614dc982721c877dadc761dc272deb65d4b0000000000000615ce8565b614dd39082615d33565b90506001607f1b614de48584615ce8565b614dee9190615cb2565b9150614e0d827248178ece97479f33a77f2ad22a81b64406c000615ce8565b614e179082615c75565b90506001607f1b614e288584615ce8565b614e329190615cb2565b9150614e518272b6ca8268b9d810fedf6695ef2f8a6c00000000615ce8565b614e5b9082615d33565b90506001607f1b614e6c8584615ce8565b614e769190615cb2565b9150614e96827301d0e76631a5b05d007b8cb72a7c7f11ec36e000615ce8565b614ea09082615c75565b90506001607f1b614eb18584615ce8565b614ebb9190615cb2565b9150614ed48269094386f7b3f0bfb38d8f604f1b615ce8565b614ede9082615d33565b90506001607f1b614eef8584615ce8565b614ef99190615cb2565b9150614f1982730bd8369f1b702bf491e2ebfcee08250313b65400615ce8565b614f239082615c75565b90506001607f1b614f348584615ce8565b614f3e9190615cb2565b9150614f5e82731e5c7c32a9f6c70ab2cb59d9225764d400000000615ce8565b614f689082615d33565b90506001607f1b614f798584615ce8565b614f839190615cb2565b9150614fa382734dff5820e165e910f95120a708e742496221e600615ce8565b614fad9082615c75565b90506001607f1b614fbe8584615ce8565b614fc89190615cb2565b9150614fe4826c064647b36d8fe769bc7728729b603d1b615ce8565b614fee9082615d33565b90506001607f1b614fff8584615ce8565b6150099190615cb2565b915061502a82740205db8dffff45bfa2938f128f599dbf16eb11d880615ce8565b6150349082615c75565b90506001607f1b6150458584615ce8565b61504f9190615cb2565b91506150708274053a044ebd984351493e1786af38d39a0800000000615ce8565b61507a9082615d33565b90506001607f1b61508b8584615ce8565b6150959190615cb2565b91506150b682740d86dae2a4cc0f47633a544479735869b487b59c40615ce8565b6150c09082615c75565b90506001607f1b6150d18584615ce8565b6150db9190615cb2565b91506150ec82610231609c1b615ce8565b6150f69082615d33565b90506001607f1b6151078584615ce8565b6151119190615cb2565b915061513282745b0485a76f6646c2039db1507cdd51b08649680822615ce8565b61513c9082615c75565b90506001607f1b61514d8584615ce8565b6151579190615cb2565b91506151788274ec983c46c49545bc17efa6b5b0055e242200000000615ce8565b6151829082615d33565b90506107186fde1bc4d19efcac82445da75b0000000082615cb2565b60008060016151bd6f2f16ac6c59de6f8d5d6f63c1482a7c8685615d33565b6151c79190615d33565b905060006151e56f03060c183060c183060c183060c1830683615cb2565b90506000615203826f03060c183060c183060c183060c18306615ce8565b90506000615212836001615c75565b61522c906f03060c183060c183060c183060c18306615ce8565b905060006080846080811061525157634e487b7160e01b600052603260045260246000fd5b0154905060006080615264866001615c75565b6080811061528257634e487b7160e01b600052603260045260246000fd5b015490506f03060c183060c183060c183060c183066152a18588615d33565b6152ab9083615ce8565b6152b58886615d33565b6152bf9085615ce8565b6152c99190615c75565b6152d39190615cb2565b98975050505050505050565b60008070015bf0a8b1457695355fb8ac404e7a79e383106153085761530383612e04565b615311565b6153118361288b565b9050600070015bf0a8b1457695355fb8ac404e7a79e3821061533b5761533682612e04565b615344565b6153448261288b565b9050836001607f1b836153578285615ce8565b6153619190615cb2565b61536b8486615d33565b6153759190615c75565b61537f9190615ce8565b6107189190615cb2565b80356001600160a01b038116811461060457600080fd5b803563ffffffff8116811461060457600080fd5b6000602082840312156153c5578081fd5b61283982615389565b600080604083850312156153e0578081fd5b6153e983615389565b91506153f760208401615389565b90509250929050565b600080600060608486031215615414578081fd5b61541d84615389565b925061542b60208501615389565b9150604084013590509250925092565b6000806040838503121561544d578182fd5b61545683615389565b946020939093013593505050565b600060208284031215615475578081fd5b815161283981615e05565b600060208284031215615491578081fd5b5035919050565b6000602082840312156154a9578081fd5b5051919050565b600080604083850312156154c2578182fd5b50508035926020909101359150565b600080600080600060a086880312156154e8578081fd5b85359450602086013593506040860135925060608601359150608086013561550f81615e05565b809150509295509295909350565b600080600080600060a08688031215615534578081fd5b505083359560208501359550604085013594606081013594506080013592509050565b6000806000806080858703121561556c578384fd5b8435935060208501359250615583604086016153a0565b9396929550929360600135925050565b600080600080608085870312156155a8578384fd5b84359350602085013592506155bf604086016153a0565b91506155cd606086016153a0565b905092959194509250565b600080600080600060a086880312156155ef578081fd5b853594506155ff602087016153a0565b935060408601359250615614606087016153a0565b949793965091946080013592915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b901515815260200190565b6000602080835283518082850152825b818110156156e8578581018301518582016040015282016156cc565b818111156156f95783604083870101525b50601f01601f1916929092016040019392505050565b60208082526023908201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260408201526265737360e81b606082015260800190565b60208082526022908201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604082015261636560f01b606082015260800190565b6020808252601d908201527f4661696c656420746f207472616e736665722044616920746f6b656e73000000604082015260600190565b60208082526022908201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604082015261737360f01b606082015260800190565b60208082526019908201527f4465706f736974206d757374206265206e6f6e2d7a65726f2e00000000000000604082015260600190565b60208082526026908201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604082015265616c616e636560d01b606082015260800190565b6020808252601b908201527f4552525f494e56414c49445f524553455256455f42414c414e43450000000000604082015260600190565b6020808252601f908201527f4d75737420617070726f76652044414920746f2062757920746f6b656e732e00604082015260600190565b6020808252601290820152714552525f494e56414c49445f535550504c5960701b604082015260600190565b60208082526019908201527f4552525f494e56414c49445f524553455256455f524154494f00000000000000604082015260600190565b60208082526028908201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616040820152676c6c6f77616e636560c01b606082015260800190565b60208082526018908201527f4d75737420617070726f766520656e6f756768204441492e0000000000000000604082015260600190565b6020808252601c908201527f496e73756666696369656e7420746f6b656e7320746f206275726e2e00000000604082015260600190565b60208082526021908201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736040820152607360f81b606082015260800190565b60208082526025908201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604082015264647265737360d81b606082015260800190565b60208082526012908201527111549497d253959053125117d05353d5539560721b604082015260600190565b60208082526024908201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646040820152637265737360e01b606082015260800190565b60208082526018908201527f416d6f756e74206d757374206265206e6f6e2d7a65726f2e0000000000000000604082015260600190565b6020808252601a908201527f4552525f494e56414c49445f524553455256455f574549474854000000000000604082015260600190565b60208082526018908201527f4552525f494e56414c49445f524553455256455f524154450000000000000000604082015260600190565b60208082526025908201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604082015264207a65726f60d81b606082015260800190565b6020808252601f908201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604082015260600190565b90815260200190565b918252602082015260400190565b91825260ff16602082015260400190565b63ffffffff92831681529116602082015260400190565b60ff91909116815260200190565b60008219821115615c8857615c88615dd9565b500190565b600060ff821660ff84168060ff03821115615caa57615caa615dd9565b019392505050565b600082615cc157615cc1615def565b500490565b600060ff831680615cd957615cd9615def565b8060ff84160491505092915050565b6000816000190483118215151615615d0257615d02615dd9565b500290565b600063ffffffff80831681851681830481118215151615615d2a57615d2a615dd9565b02949350505050565b600082821015615d4557615d45615dd9565b500390565b600060ff821660ff841680821015615d6457615d64615dd9565b90039392505050565b600060ff821680615d8057615d80615dd9565b6000190192915050565b600281046001821680615d9e57607f821691505b60208210811415615dbf57634e487b7160e01b600052602260045260246000fd5b50919050565b600082615dd457615dd4615def565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b8015158114615e1357600080fd5b5056fea2646970667358221220ef5825b993891458b48a0f8873f3a2659e644e8dd9ceae4df4d96b2d60fe043b64736f6c634300080000330000000000000000000000000000000000000000000000000000000000000014000000000000000000000000e91286d05c1c93d300abf9f4ebdb8e25749209d6

Deployed ByteCode

0x608060405234801561001057600080fd5b50600436106102065760003560e01c806394491fab1161011a578063a67453b0116100ad578063e1c7392a1161007c578063e1c7392a14610443578063ebbb21581461044b578063ee1ea4ff1461045e578063f3250fe214610473578063f51e181a1461048657610206565b8063a67453b0146103f4578063a9059cbb14610415578063aa248a4614610428578063dd62ed3e1461043057610206565b8063a11aa1b4116100e9578063a11aa1b4146103b3578063a179dd93146103c6578063a3882291146103ce578063a457c2d7146103e157610206565b806394491fab1461037d57806395d89b4114610390578063a0712d6814610398578063a10954fe146103ab57610206565b8063313ce5671161019d57806370a082311161016c57806370a082311461031e57806376c2d5101461033157806376cf0b56146103445780638074590a1461035757806390f0c2ea1461036a57610206565b8063313ce567146102c057806332833d51146102d557806339509351146102f657806342966c681461030957610206565b806323b872dd116101d957806323b872dd1461026657806328c3d701146102795780632f55bdb51461028c57806330c7d6371461029f57610206565b806306fdde031461020b578063095ea7b3146102295780630c7d5cd81461024957806318160ddd1461025e575b600080fd5b61021361048e565b60405161022091906156bc565b60405180910390f35b61023c61023736600461543b565b610521565b60405161022091906156b1565b61025161053e565b6040516102209190615c28565b610251610545565b61023c610274366004615400565b61054c565b610251610287366004615480565b6105e6565b61025161029a366004615557565b610609565b6102b26102ad3660046154b0565b610720565b604051610220929190615c50565b6102c86107e6565b6040516102209190615c67565b6102e86102e3366004615593565b6107eb565b604051610220929190615c3f565b61023c61030436600461543b565b6108d8565b61031c610317366004615480565b61092d565b005b61025161032c3660046153b4565b6109c3565b6102b261033f3660046154b0565b6109df565b610251610352366004615557565b610a15565b610251610365366004615557565b610b45565b610251610378366004615480565b610c29565b61025161038b3660046155d8565b610c44565b610213610d68565b6102516103a6366004615480565b610d78565b610251610f0b565b6102b26103c136600461551d565b610f12565b61031c61101f565b6102b26103dc3660046154d1565b611a26565b61023c6103ef36600461543b565b611aee565b6104076104023660046154b0565b611b68565b604051610220929190615c31565b61023c61042336600461543b565b611c28565b61031c611c3c565b61025161043e3660046153ce565b612412565b61031c61243e565b610251610459366004615557565b612450565b61046661257e565b6040516102209190615625565b610251610481366004615557565b61258e565b610251612641565b6060610183805461049e90615d8a565b80601f01602080910402602001604051908101604052809291908181526020018280546104ca90615d8a565b80156105175780601f106104ec57610100808354040283529160200191610517565b820191906000526020600020905b8154815290600101906020018083116104fa57829003601f168201915b5050505050905090565b600061053561052e612648565b848461264c565b50600192915050565b6101875481565b6101825490565b6000610559848484612701565b6001600160a01b0384166000908152610181602052604081208161057b612648565b6001600160a01b03166001600160a01b03168152602001908152602001600020549050828110156105c75760405162461bcd60e51b81526004016105be9061595b565b60405180910390fd5b6105db856105d3612648565b85840361264c565b506001949350505050565b60006106016105f3610545565b61018654610187548561258e565b90505b919050565b600080851161062a5760405162461bcd60e51b81526004016105be906158f8565b6000841161064a5760405162461bcd60e51b81526004016105be9061588a565b60018363ffffffff161180156106795750610669620f42406002615d07565b63ffffffff168363ffffffff1611155b6106955760405162461bcd60e51b81526004016105be90615924565b816106a257506000610718565b63ffffffff8316620f424014156106cf57836106be838761282d565b6106c89190615cb2565b9050610718565b600080806106dd8786612840565b90506106ee818888620f42406107eb565b9093509150600060ff83166107038a8661282d565b901c90506107118982615d33565b9450505050505b949350505050565b6000807d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea98411156107a55760006107747d10c6f7a0b5ed8d36b4c7f34938583621fafc8b0079a2834d26fa3fcc9ea96001615c75565b61077e9086615cb2565b610789906001615c75565b90506107958186615cb2565b94506107a18185615cb2565b9350505b60006107c66107b7620f424087615ce8565b6107c18787612840565b61284c565b905060006107d782620f4240615d33565b919350909150505b9250929050565b601290565b600080600160811b86106107fe57600080fd5b600080866108106001607f1b8a615ce8565b61081a9190615cb2565b905070015bf0a8b1457695355fb8ac404e7a79e38110156108455761083e8161288b565b9150610851565b61084e81612e04565b91505b60008563ffffffff168763ffffffff168461086c9190615ce8565b6108769190615cb2565b9050600160831b81101561089b5761088d81612eed565b607f945094505050506108cf565b60006108a682613518565b90506108c26108b682607f615d4a565b60ff1683901c826135f8565b955093506108cf92505050565b94509492505050565b60006105356108e5612648565b848461018160006108f4612648565b6001600160a01b03908116825260208083019390935260409182016000908120918b16815292529020546109289190615c75565b61264c565b600061093882613c56565b6101885460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb9061096c9033908590600401615677565b602060405180830381600087803b15801561098657600080fd5b505af115801561099a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109be9190615464565b505050565b6001600160a01b03166000908152610180602052604090205490565b6000808284116109fc576109f38484610720565b915091506107df565b600080610a098587610720565b97909650945050505050565b6000808511610a365760405162461bcd60e51b81526004016105be906158f8565b60008411610a565760405162461bcd60e51b81526004016105be9061588a565b60008363ffffffff16118015610a755750620f424063ffffffff841611155b610a915760405162461bcd60e51b81526004016105be90615b3e565b84821115610ab15760405162461bcd60e51b81526004016105be90615a97565b81610abe57506000610718565b84821415610acd575082610718565b63ffffffff8316620f42401415610ae957846106be858461282d565b60008080610af78589615d33565b9050610b088882620f4240896107eb565b90935091506000610b19888561282d565b905060ff831688901b84610b2d8284615d33565b610b379190615cb2565b9a9950505050505050505050565b6000808511610b665760405162461bcd60e51b81526004016105be906158f8565b60008411610b865760405162461bcd60e51b81526004016105be9061588a565b60018363ffffffff16118015610bb55750610ba5620f42406002615d07565b63ffffffff168363ffffffff1611155b610bd15760405162461bcd60e51b81526004016105be90615924565b84821115610bf15760405162461bcd60e51b81526004016105be90615a97565b81610bfe57506000610718565b84821415610c0d575082610718565b63ffffffff8316620f42401415610ae957846106be838661282d565b6000610601610c36610545565b610186546101875485610a15565b60008086118015610c555750600084115b610c715760405162461bcd60e51b81526004016105be9061588a565b60008563ffffffff16118015610c905750620f424063ffffffff861611155b8015610ca2575060008363ffffffff16115b8015610cb75750620f424063ffffffff841611155b610cd35760405162461bcd60e51b81526004016105be90615b3e565b8263ffffffff168563ffffffff161415610d0c57610cf18683612840565b610cfb858461282d565b610d059190615cb2565b9050610d5f565b60008080610d1a8986612840565b9050610d28818a8a896107eb565b90935091506000610d39888561282d565b905060ff831688901b84610d4d8284615d33565b610d579190615cb2565b955050505050505b95945050505050565b6060610184805461049e90615d8a565b61018854604051636eb1769f60e11b815260009182916001600160a01b039091169063dd62ed3e90610db09033903090600401615639565b60206040518083038186803b158015610dc857600080fd5b505afa158015610ddc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e009190615498565b905060008111610e225760405162461bcd60e51b81526004016105be906158c1565b82811015610e425760405162461bcd60e51b81526004016105be906159a3565b610188546040516323b872dd60e01b81526000916001600160a01b0316906323b872dd90610e7890339030908790600401615653565b602060405180830381600087803b158015610e9257600080fd5b505af1158015610ea6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eca9190615464565b90508015610ee457610edb82613d0a565b92505050610604565b60008211610f045760405162461bcd60e51b81526004016105be90615794565b5050919050565b6101865481565b60008085871415610f4d576000871180610f2c5750600085115b610f485760405162461bcd60e51b81526004016105be9061588a565b610f85565b600087118015610f5d5750600086115b8015610f695750600085115b610f855760405162461bcd60e51b81526004016105be9061588a565b600084118015610f955750600083115b610fb15760405162461bcd60e51b81526004016105be90615b75565b6000610fbd888661282d565b90506000610fcb878661282d565b905087891015610fee57610fe3888a84846001611a26565b935093505050611015565b8789111561100457610fe3898984846000611a26565b61100e82826109df565b9350935050505b9550959350505050565b6f60e393c68d20b1bd09deaabc0373b9c560809081556f5f8f46e4854120989ed94719fb4c20116081556f5e479ebb9129fb1b7e72a648f992b6066082556f5d0bd23fe42dfedde2e9586be12b85fe6083556f5bdb29ddee979308ddfca81aeeb8095a6084556f5ab4fd8a260d2c7e2c0d2afcf0009dad6085556f5998b31359a55d48724c65cf090012216086556f5885bcad2b322dfc43e8860f9c018cf56087556f577b97aa1fe222bb452fdf111b1f0be26088556f5679cb5e3575632e5baa27e2b949f7046089556f557fe8241b3a31c83c732f1cdff4a1c5608a556f548d868026504875d6e59bbe95fc2a6b608b556f53a2465ce347cf34d05a867c17dd3088608c556f52bdce5dcd4faed59c7f5511cf8f8acc608d556f51dfcb453c07f8da817606e7885f7c3e608e556f5107ef6b0a5a2be8f8ff15590daa3cce608f556f5035f241d6eae0cd7bacba119993de7b6090556f4f698fe90d5b53d532171e1210164c666091556f4ea288ca297a0e6a09a0eee240e16c856092556f4de0a13fdcf5d4213fc398ba6e3becde6093556f4d23a145eef91fec06b06140804c48086094556f4c6b5430d4c1ee5526473db4ae0f11de6095556f4bb7886c240562eba11f4963a53b42406096556f4b080f3f1cb491d2d521e0ea4583521e6097556f4a5cbc96a05589cb4d86be1db31683646098556f49b566d40243517658d78c33162d6ece6099556f4911e6a02e5507a30f947383fd9a3276609a556f487216c2b31be4adc41db8a8d5cc0c88609b556f47d5d3fc4a7a1b188cd3d788b5c5e9fc609c556f473cfce4871a2c40bc4f9e1c32b955d0609d556f46a771ca578ab878485810e285e31c67609e556f4615149718aed4c258c373dc676aa72d609f556f4585c8b3f8fe489c6e1833ca4787138460a0556f44f972f174e41e5efb7e9d63c29ce73560a1556f446ff970ba86d8b00beb05ecebf3c4dc60a2556f43e9438ec88971812d6f198b5ccaad9660a3556f436539d11ff7bea657aeddb394e809ef60a4556f42e3c5d3e5a913401d86f66db5d81c2c60a5556f4264d2395303070ea726cbe98df6217460a6556f41e84a9a593bb7194c3a6349ecae4eea60a7556f416e1b785d13eba07a08f3f18876a5ab60a8556f40f6322ff389d423ba9dd7e7e7b7e80960a9556f40807cec8a466880ecf4184545d240a460aa556f400cea9ce88a8d3ae668e8ea0d9bf07f60ab556f3f9b6ae8772d4c55091e0ed7dfea0ac160ac556f3f2bee253fd84594f54bcaafac383a1360ad556f3ebe654e95208bb9210c575c081c595860ae556f3e52c1fc5665635b78ce1f05ad53c08660af556f3de8f65ac388101ddf718a6f5c1eff6560b0556f3d80f522d59bd0b328ca012df4cd2d4960b1556f3d1ab193129ea72b23648a161163a85a60b2556f3cb61f68d32576c135b95cfb53f76d7560b3556f3c5332d9f1aae851a3619e77e4cc847360b4556f3bf1e08edbe2aa109e1525f65759ef7360b5556f3b921d9cff13fa2c197746a3dfc4918f60b6556f3b33df818910bfc1a5aefb8f63ae2ac460b7556f3ad71c1c77e34fa32a9f184967eccbf660b8556f3a7bc9abf2c5bb53e2f7384a8a16521a60b9556f3a21dec7e76369783a68a0c6385a1c5760ba556f39c9525de6c9cdf7c1c157ca4a7a6ee360bb556f39721bad3dc85d1240ff0190e0adaac360bc556f391c324344d3248f0469eb28dd3d77e060bd556f38c78df7e3c796279fb4ff84394ab3da60be556f387426ea4638ae9aae08049d3554c20a60bf556f3821f57dbd2763256c1a99bbd205137860c0556f37d0f256cb46a8c92ff62fbbef28969860c1556f37811658591ffc7abdd1feaf3cef9b7360c2556f37325aa10e9e82f7df0f380f7997154b60c3556f36e4b888cfb408d873b9a80d439311c660c4556f3698299e59f4bb9de645fc9b08c64cca60c5556f364ca7a5012cb603023b57dd3ebfd50d60c6556f36022c928915b778ab1b06aaee7e61d460c7556f35b8b28d1a73dc27500ffe35559cc02860c8556f357033e951fe250ec5eb4e60955132d760c9556f3528ab2867934e3a21b5412e4c4f888160ca556f34e212f66c55057f9676c80094a61d5960cb556f349c66289e5b3c4b540c24f42fa4b9bb60cc556f34579fbbd0c733a9c8d6af6b0f7d00f760cd556f3413bad2e712288b924b5882b5b369bf60ce556f33d0b2b56286510ef730e213f71f12e960cf556f338e82ce00e2496262c64457535ba1a160d0556f334d26a96b373bb7c2f8ea1827f27a9260d1556f330c99f4f4211469e00b3e18c31475ea60d2556f32ccd87d6486094999c7d5e6f33237d860d3556f328dde2dd617b6665a2e8556f250c1af60d4556f324fa70e9adc270f8262755af5a99af960d5556f32122f443110611ca51040f41fa6e1e360d6556f31d5730e42c0831482f0f1485c4263d860d7556f31996ec6b07b4a83421b5ebc4ab4e1f160d8556f315e1ee0a68ff46bb43ec2b85032e87660d9556f31237fe7bc4deacf6775b9efa1a145f860da556f30e98e7f1cc5a356e44627a6972ea2ff60db556f30b04760b8917ec74205a3002650ec0560dc556f3077a75c803468e9132ce0cf3224241d60dd556f303fab57a6a275c36f19cda9bace667a60de556f3008504beb8dcbd2cf3bc1f6d5a064f060df556f2fd19346ed17dac61219ce0c2c5ac4b060e0556f2f9b7169808c324b5852fd3d54ba971460e1556f2f65e7e711cf4b064eea9c08cbdad57460e2556f2f30f405093042ddff8a251b6bf6d10360e3556f2efc931a3750f2e8bfe323edfe03757460e4556f2ec8c28e46dbe56d98685278339400cb60e5556f2e957fd933c3926d8a599b602379b85160e6556f2e62c882c7c9ed4473412702f08ba0e560e7556f2e309a221c12ba361e3ed695167feee260e8556f2dfef25d1f865ae18dd07cfea4bcea1060e9556f2dcdcee821cdc80decc02c44344aeb3160ea556f2d9d2d8562b34944d0b201bb87260c8360eb556f2d6d0c04a5b62a2c42636308669b729a60ec556f2d3d6842c9a235517fc5a0332691528f60ed556f2d0e402963fe1ea2834abc408c437c1060ee556f2cdf91ae602647908aff975e4d6a2a8c60ef556f2cb15ad3a1eb65f6d74a75da09a1b6c560f0556f2c8399a6ab8e9774d6fcff373d21072760f1556f2c564c4046f64edba6883ca06bbc453560f2556f2c2970c431f952641e05cb493e23eed360f3556f2bfd0560cd9eb14563bc7c0732856c1860f4556f2bd1084ed0332f7ff4150f9d0ef41a2c60f5556f2ba577d0fa1628b76d040b12a82492fb60f6556f2b7a5233cd21581e855e89dc2f1e8a9260f7556f2b4f95cd46904d05d72bdcde337d9cc760f8556f2b2540fc9b4d9abba3faca669191467560f9556f2afb5229f68d0830d8be8adb0a0db70f60fa556f2ad1c7c63a9b294c5bc73a3ba3ab7a2b60fb556f2aa8a04ac3cbe1ee1c9c86361465dbb860fc556f2a7fda392d725a44a2c8aeb9ab35430d60fd556f2a57741b18cde618717792b4faa216db60fe556f2a2f6c81f5d84dd950a35626d6d5503a90607f5b0155565b600080611a338585611b68565b9095509350600086611a49896001607f1b61282d565b611a539190615cb2565b9050600070015bf0a8b1457695355fb8ac404e7a79e38210611a7d57611a7882612e04565b611a86565b611a868261288b565b9050600086611a95838a61282d565b611a9f9190615cb2565b9050600086611ab657611ab182613d88565b611abf565b611abf82613dca565b9050611adc611ace828b61282d565b61033f8a6001607f1b61282d565b95509550505050509550959350505050565b6000806101816000611afe612648565b6001600160a01b0390811682526020808301939093526040918201600090812091881681529252902054905082811015611b4a5760405162461bcd60e51b81526004016105be90615bac565b611b5e611b55612648565b8585840361264c565b5060019392505050565b600080600160801b8411158015611b835750600160801b8311155b15611b925750829050816107df565b600160801b841015611bc55782611bad600160801b86615ce8565b611bb79190615cb2565b600160801b915091506107df565b600160801b831015611beb57600160801b84611be18286615ce8565b6109f39190615cb2565b6000838511611bfa5783611bfc565b845b90506000611c16611c116001607f1b84615cb2565b613e2e565b60ff1695861c969490951c9450505050565b6000610535611c35612648565b8484612701565b701c35fedd14ffffffffffffffffffffffff602055701b0ce43b323fffffffffffffffffffffff6021557019f0028ec1ffffffffffffffffffffffff6022557018ded91f0e7fffffffffffffffffffffff6023557017d8ec7f0417ffffffffffffffffffffff6024557016ddc6556cdbffffffffffffffffffffff6025557015ecf52776a1ffffffffffffffffffffff6026557015060c256cb2ffffffffffffffffffffff602755701428a2f98d72ffffffffffffffffffffff6028557013545598e5c23fffffffffffffffffffff602955701288c4161ce1dfffffffffffffffffffff602a557011c592761c666fffffffffffffffffffff602b5570110a688680a757ffffffffffffffffffff602c55701056f1b5bedf77ffffffffffffffffffff602d55700faadceceeff8bffffffffffffffffffff602e55700f05dc6b27edadffffffffffffffffffff602f55700e67a5a25da4107fffffffffffffffffff603055700dcff115b14eedffffffffffffffffffff603155700d3e7a392431239fffffffffffffffffff603255700cb2ff529eb71e4fffffffffffffffffff603355700c2d415c3db974afffffffffffffffffff603455700bad03e7d883f69bffffffffffffffffff603555700b320d03b2c343d5ffffffffffffffffff603655700abc25204e02828dffffffffffffffffff603755700a4b16f74ee4bb207fffffffffffffffff6038557009deaf736ac1f569ffffffffffffffffff603955700976bd9952c7aa957fffffffffffffffff603a557009131271922eaa606fffffffffffffffff603b557008b380f3558668c46fffffffffffffffff603c55700857ddf0117efa215bffffffffffffffff603d557007ffffffffffffffffffffffffffffffff603e557007abbf6f6abb9d087fffffffffffffffff603f5570075af62cbac95f7dfa7fffffffffffffff60405570070d7fb7452e187ac13fffffffffffffff6041557006c3390ecc8af379295fffffffffffffff60425570067c00a3b07ffc01fd6fffffffffffffff604355700637b647c39cbb9d3d27ffffffffffffff6044557005f63b1fc104dbd39587ffffffffffffff6045557005b771955b36e12f7235ffffffffffffff60465570057b3d49dda84556d6f6ffffffffffffff60475570054183095b2c8ececf30ffffffffffffff60485570050a28be635ca2b888f77fffffffffffff6049557004d5156639708c9db33c3fffffffffffff604a557004a23105873875bd52dfdfffffffffffff604b55700471649d87199aa990756fffffffffffff604c557004429a21a029d4c1457cfbffffffffffff604d55700415bc6d6fb7dd71af2cb3ffffffffffff604e557003eab73b3bbfe282243ce1ffffffffffff604f557003c1771ac9fb6b4c18e229ffffffffffff605055700399e96897690418f785257fffffffffff605155700373fc456c53bb779bf0ea9fffffffffff60525570034f9e8e490c48e67e6ab8bfffffffffff60535570032cbfd4a7adc790560b3337ffffffffff60545570030b50570f6e5d2acca94613ffffffffff6055557002eb40f9f620fda6b56c2861ffffffffff6056557002cc8340ecb0d0f520a6af58ffffffffff6057557002af09481380a0a35cf1ba02ffffffffff605855700292c5bdd3b92ec810287b1b3fffffffff605955700277abdcdab07d5a77ac6d6b9fffffffff605a5570025daf6654b1eaa55fd64df5efffffffff605b55700244c49c648baa98192dce88b7ffffffff605c5570022ce03cd5619a311b2471268bffffffff605d55700215f77c045fbe885654a44a0fffffffff605e557001ffffffffffffffffffffffffffffffff605f557001eaefdbdaaee7421fc4d3ede5ffffffff6060557001d6bd8b2eb257df7e8ca57b09bfffffff6061557001c35fedd14b861eb0443f7f133fffffff6062557001b0ce43b322bcde4a56e8ada5afffffff60635570019f0028ec1fff007f5a195a39dfffffff60645570018ded91f0e72ee74f49b15ba527ffffff60655570017d8ec7f04136f4e5615fd41a63ffffff60665570016ddc6556cdb84bdc8d12d22e6fffffff60675570015ecf52776a1155b5bd8395814f7fffff60685570015060c256cb23b3b3cc3754cf40ffffff6069557001428a2f98d728ae223ddab715be3fffff606a5570013545598e5c23276ccf0ede68034fffff606b557001288c4161ce1d6f54b7f61081194fffff606c5570011c592761c666aa641d5a01a40f17ffff606d55700110a688680a7530515f3e6e6cfdcdffff606e557001056f1b5bedf75c6bcb2ce8aed428ffff606f556ffaadceceeff8a0890f3875f008277fff6070556ff05dc6b27edad306388a600f6ba0bfff6071556fe67a5a25da41063de1495d5b18cdbfff6072556fdcff115b14eedde6fc3aa5353f2e4fff6073556fd3e7a3924312399f9aae2e0f868f8fff6074556fcb2ff529eb71e41582cccd5a1ee26fff6075556fc2d415c3db974ab32a51840c0b67edff6076556fbad03e7d883f69ad5b0a186184e06bff6077556fb320d03b2c343d4829abd6075f0cc5ff6078556fabc25204e02828d73c6e80bcdb1a95bf6079556fa4b16f74ee4bb2040a1ec6c15fbbf2df607a556f9deaf736ac1f569deb1b5ae3f36c130f607b556f976bd9952c7aa957f5937d790ef65037607c556f9131271922eaa6064b73a22d0bd4f2bf607d556f8b380f3558668c46c91c49a2f8e967b9607e556f857ddf0117efa215952912839f6473e66000607f611a22565b6001600160a01b0391821660009081526101816020908152604080832093909416825291909152205490565b612446611c3c565b61244e61101f565b565b60008085116124715760405162461bcd60e51b81526004016105be906158f8565b600084116124915760405162461bcd60e51b81526004016105be9061588a565b60018363ffffffff161180156124c057506124b0620f42406002615d07565b63ffffffff168363ffffffff1611155b6124dc5760405162461bcd60e51b81526004016105be90615924565b816124e957506000610718565b63ffffffff8316620f4240141561252657846001612507848761282d565b6125119190615d33565b61251b9190615cb2565b6106c8906001615c75565b600080806125348886612840565b90506125458189620f4240896107eb565b9093509150600060ff8316600161255c8a8761282d565b6125669190615d33565b612572911c6001615c75565b90506107118882615d33565b610188546001600160a01b031681565b60008085116125af5760405162461bcd60e51b81526004016105be906158f8565b600084116125cf5760405162461bcd60e51b81526004016105be9061588a565b60008363ffffffff161180156125ee5750620f424063ffffffff841611155b61260a5760405162461bcd60e51b81526004016105be90615b3e565b8161261757506000610718565b63ffffffff8316620f4240141561263357836106be868461282d565b600080806106dd8588612840565b6101855481565b3390565b6001600160a01b0383166126725760405162461bcd60e51b81526004016105be90615ac3565b6001600160a01b0382166126985760405162461bcd60e51b81526004016105be906157cb565b6001600160a01b038084166000818152610181602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906126f4908590615c28565b60405180910390a3505050565b6001600160a01b0383166127275760405162461bcd60e51b81526004016105be90615a52565b6001600160a01b03821661274d5760405162461bcd60e51b81526004016105be9061570f565b6127588383836109be565b6001600160a01b03831660009081526101806020526040902054818110156127925760405162461bcd60e51b81526004016105be90615844565b6001600160a01b03808516600090815261018060205260408082208585039055918516815290812080548492906127ca908490615c75565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516128149190615c28565b60405180910390a36128278484846109be565b50505050565b60006128398284615ce8565b9392505050565b60006128398284615c75565b6000612859600283615cb2565b6128639083615d33565b61286d8385615dc5565b6128779190615cb2565b6128818385615cb2565b6128399190615c75565b6000808080806fd3094c70f034de4b96ff7d5b6f99fcd886106128e4576128b66001607e1b85615c75565b93506fd3094c70f034de4b96ff7d5b6f99fcd86128d76001607f1b88615ce8565b6128e19190615cb2565b95505b6fa45af1e1f40c333b3de1db4dd55f29a78610612937576129096001607d1b85615c75565b93506fa45af1e1f40c333b3de1db4dd55f29a761292a6001607f1b88615ce8565b6129349190615cb2565b95505b6f910b022db7ae67ce76b441c27035c6a1861061298a5761295c6001607c1b85615c75565b93506f910b022db7ae67ce76b441c27035c6a161297d6001607f1b88615ce8565b6129879190615cb2565b95505b6f88415abbe9a76bead8d00cf112e4d4a886106129dd576129af6001607b1b85615c75565b93506f88415abbe9a76bead8d00cf112e4d4a86129d06001607f1b88615ce8565b6129da9190615cb2565b95505b6f84102b00893f64c705e841d5d4064bd38610612a3057612a026001607a1b85615c75565b93506f84102b00893f64c705e841d5d4064bd3612a236001607f1b88615ce8565b612a2d9190615cb2565b95505b6f8204055aaef1c8bd5c3259f4822735a28610612a8357612a55600160791b85615c75565b93506f8204055aaef1c8bd5c3259f4822735a2612a766001607f1b88615ce8565b612a809190615cb2565b95505b6f810100ab00222d861931c15e39b44e998610612ad657612aa8600160781b85615c75565b93506f810100ab00222d861931c15e39b44e99612ac96001607f1b88615ce8565b612ad39190615cb2565b95505b6f808040155aabbbe9451521693554f7338610612b2957612afb600160771b85615c75565b93506f808040155aabbbe9451521693554f733612b1c6001607f1b88615ce8565b612b269190615cb2565b95505b612b376001607f1b87615d33565b92508291506001607f1b612b4b8380615ce8565b612b559190615cb2565b9050600160801b612b668482615d33565b612b709084615ce8565b612b7a9190615cb2565b612b849085615c75565b93506001607f1b612b958284615ce8565b612b9f9190615cb2565b9150600160811b612bc0846faaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa615d33565b612bca9084615ce8565b612bd49190615cb2565b612bde9085615c75565b93506001607f1b612bef8284615ce8565b612bf99190615cb2565b9150600360801b612c1a846f99999999999999999999999999999999615d33565b612c249084615ce8565b612c2e9190615cb2565b612c389085615c75565b93506001607f1b612c498284615ce8565b612c539190615cb2565b9150600160821b612c74846f92492492492492492492492492492492615d33565b612c7e9084615ce8565b612c889190615cb2565b612c929085615c75565b93506001607f1b612ca38284615ce8565b612cad9190615cb2565b9150600560801b612cce846f8e38e38e38e38e38e38e38e38e38e38e615d33565b612cd89084615ce8565b612ce29190615cb2565b612cec9085615c75565b93506001607f1b612cfd8284615ce8565b612d079190615cb2565b9150600360811b612d28846f8ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b615d33565b612d329084615ce8565b612d3c9190615cb2565b612d469085615c75565b93506001607f1b612d578284615ce8565b612d619190615cb2565b9150600760801b612d82846f89d89d89d89d89d89d89d89d89d89d89615d33565b612d8c9084615ce8565b612d969190615cb2565b612da09085615c75565b93506001607f1b612db18284615ce8565b612dbb9190615cb2565b9150600160831b612ddc846f88888888888888888888888888888888615d33565b612de69084615ce8565b612df09190615cb2565b612dfa9085615c75565b9695505050505050565b600080600160801b8310612e43576000612e25611c116001607f1b86615cb2565b60ff811694851c94909150612e3f906001607f1b90615ce8565b9150505b6001607f1b831115612eb857607f5b60ff811615612eb6576001607f1b612e6a8580615ce8565b612e749190615cb2565b9350600160801b8410612ea657600193841c93612e919082615d4a565b60ff166001901b82612ea39190615c75565b91505b612eaf81615d6d565b9050612e52565b505b6f05b9de1d10bf4103d647b0955897ba80612ee36f03f80fe03f80fe03f80fe03f80fe03f883615ce8565b6128399190615cb2565b6000808080612f006001607c1b86615dc5565b91508190506001607f1b612f148280615ce8565b612f1e9190615cb2565b9050612f32816710e1b3be415a0000615ce8565b612f3c9084615c75565b92506001607f1b612f4d8383615ce8565b612f579190615cb2565b9050612f6b816705a0913f6b1e0000615ce8565b612f759084615c75565b92506001607f1b612f868383615ce8565b612f909190615cb2565b9050612fa481670168244fdac78000615ce8565b612fae9084615c75565b92506001607f1b612fbf8383615ce8565b612fc99190615cb2565b9050612fdc81664807432bc18000615ce8565b612fe69084615c75565b92506001607f1b612ff78383615ce8565b6130019190615cb2565b905061301481660c0135dca04000615ce8565b61301e9084615c75565b92506001607f1b61302f8383615ce8565b6130399190615cb2565b905061304c816601b707b1cdc000615ce8565b6130569084615c75565b92506001607f1b6130678383615ce8565b6130719190615cb2565b9050613083816536e0f639b800615ce8565b61308d9084615c75565b92506001607f1b61309e8383615ce8565b6130a89190615cb2565b90506130ba81650618fee9f800615ce8565b6130c49084615c75565b92506001607f1b6130d58383615ce8565b6130df9190615cb2565b90506130f081649c197dcc00615ce8565b6130fa9084615c75565b92506001607f1b61310b8383615ce8565b6131159190615cb2565b905061312681640e30dce400615ce8565b6131309084615c75565b92506001607f1b6131418383615ce8565b61314b9190615cb2565b905061315c8164012ebd1300615ce8565b6131669084615c75565b92506001607f1b6131778383615ce8565b6131819190615cb2565b9050613191816317499f00615ce8565b61319b9084615c75565b92506001607f1b6131ac8383615ce8565b6131b69190615cb2565b90506131c6816301a9d480615ce8565b6131d09084615c75565b92506001607f1b6131e18383615ce8565b6131eb9190615cb2565b90506131fa81621c6380615ce8565b6132049084615c75565b92506001607f1b6132158383615ce8565b61321f9190615cb2565b905061322e816201c638615ce8565b6132389084615c75565b92506001607f1b6132498383615ce8565b6132539190615cb2565b905061326181611ab8615ce8565b61326b9084615c75565b92506001607f1b61327c8383615ce8565b6132869190615cb2565b90506132948161017c615ce8565b61329e9084615c75565b92506001607f1b6132af8383615ce8565b6132b99190615cb2565b90506132c6816014615ce8565b6132d09084615c75565b92506001607f1b6132e18383615ce8565b6132eb9190615cb2565b90506132f8816001615ce8565b6133029084615c75565b92506001607f1b8261331c6721c3677c82b4000086615cb2565b6133269190615c75565b6133309190615c75565b92506001607c1b8516156133785770018ebef9eac820ae8682b9793ac6d1e77661336b847001c3d6a24ed82218787d624d3e5eba95f9615ce8565b6133759190615cb2565b92505b6001607d1b8516156133be577001368b2fc6f9609fe7aceb46aa619baed46133b18470018ebef9eac820ae8682b9793ac6d1e778615ce8565b6133bb9190615cb2565b92505b6001607e1b851615613403576fbc5ab1b16779be3575bd8f0520a9f21f6133f6847001368b2fc6f9609fe7aceb46aa619baed5615ce8565b6134009190615cb2565b92505b6001607f1b851615613447576f454aaa8efe072e7f6ddbab84b40a55c961343a846fbc5ab1b16779be3575bd8f0520a9f21e615ce8565b6134449190615cb2565b92505b600160801b85161561348b576f0960aadc109e7a3bf4578099615711ea61347e846f454aaa8efe072e7f6ddbab84b40a55c5615ce8565b6134889190615cb2565b92505b600160811b8516156134ce576e2bf84208204f5977f9a8cf01fdce3d6134c1846f0960aadc109e7a3bf4578099615711d7615ce8565b6134cb9190615cb2565b92505b600160821b85161561350f576d03c6ab775dd0b95b4cbee7e65d11613502846e2bf84208204f5977f9a8cf01fdc307615ce8565b61350c9190615cb2565b92505b50909392505050565b60006020607f5b60ff811661352e836001615c8d565b60ff16101561358f57600060026135458385615c8d565b61354f9190615cc6565b90508460008260ff166080811061357657634e487b7160e01b600052603260045260246000fd5b01541061358557809250613589565b8091505b5061351f565b8360008260ff16608081106135b457634e487b7160e01b600052603260045260246000fd5b0154106135c45791506106049050565b8360008360ff16608081106135e957634e487b7160e01b600052603260045260246000fd5b01541061020657509050610604565b6000828160ff841661360a8380615ce8565b901c9150613628826f03442c4e6074a82f1797f72ac0000000615ce8565b6136329082615c75565b905060ff84166136428684615ce8565b901c9150613660826f0116b96f757c380fb287fd0e40000000615ce8565b61366a9082615c75565b905060ff841661367a8684615ce8565b901c9150613697826e45ae5bdd5f0e03eca1ff4390000000615ce8565b6136a19082615c75565b905060ff84166136b18684615ce8565b901c91506136ce826e0defabf91302cd95b9ffda50000000615ce8565b6136d89082615c75565b905060ff84166136e88684615ce8565b901c9150613705826e02529ca9832b22439efff9b8000000615ce8565b61370f9082615c75565b905060ff841661371f8684615ce8565b901c915061373b826d54f1cf12bd04e516b6da88000000615ce8565b6137459082615c75565b905060ff84166137558684615ce8565b901c9150613771826d0a9e39e257a09ca2d6db51000000615ce8565b61377b9082615c75565b905060ff841661378b8684615ce8565b901c91506137a7826d012e066e7b839fa050c309000000615ce8565b6137b19082615c75565b905060ff84166137c18684615ce8565b901c91506137dc826c1e33d7d926c329a1ad1a800000615ce8565b6137e69082615c75565b905060ff84166137f68684615ce8565b901c9150613811826c02bee513bdb4a6b19b5f800000615ce8565b61381b9082615c75565b905060ff841661382b8684615ce8565b901c9150613845826b3a9316fa79b88eccf2a00000615ce8565b61384f9082615c75565b905060ff841661385f8684615ce8565b901c9150613879826b048177ebe1fa812375200000615ce8565b6138839082615c75565b905060ff84166138938684615ce8565b901c91506138ac826a5263fe90242dcbacf00000615ce8565b6138b69082615c75565b905060ff84166138c68684615ce8565b901c91506138df826a057e22099c030d94100000615ce8565b6138e99082615c75565b905060ff84166138f98684615ce8565b901c9150613911826957e22099c030d9410000615ce8565b61391b9082615c75565b905060ff841661392b8684615ce8565b901c91506139438269052b6b54569976310000615ce8565b61394d9082615c75565b905060ff841661395d8684615ce8565b901c915061397482684985f67696bf748000615ce8565b61397e9082615c75565b905060ff841661398e8684615ce8565b901c91506139a5826803dea12ea99e498000615ce8565b6139af9082615c75565b905060ff84166139bf8684615ce8565b901c91506139d5826731880f2214b6e000615ce8565b6139df9082615c75565b905060ff84166139ef8684615ce8565b901c9150613a058267025bcff56eb36000615ce8565b613a0f9082615c75565b905060ff8416613a1f8684615ce8565b901c9150613a3482661b722e10ab1000615ce8565b613a3e9082615c75565b905060ff8416613a4e8684615ce8565b901c9150613a63826601317c70077000615ce8565b613a6d9082615c75565b905060ff8416613a7d8684615ce8565b901c9150613a9182650cba84aafa00615ce8565b613a9b9082615c75565b905060ff8416613aab8684615ce8565b901c9150613abe826482573a0a00615ce8565b613ac89082615c75565b905060ff8416613ad88684615ce8565b901c9150613aeb826405035ad900615ce8565b613af59082615c75565b905060ff8416613b058684615ce8565b901c9150613b1782632f881b00615ce8565b613b219082615c75565b905060ff8416613b318684615ce8565b901c9150613b43826301b29340615ce8565b613b4d9082615c75565b905060ff8416613b5d8684615ce8565b901c9150613b6e82620efc40615ce8565b613b789082615c75565b905060ff8416613b888684615ce8565b901c9150613b9882617fe0615ce8565b613ba29082615c75565b905060ff8416613bb28684615ce8565b901c9150613bc282610420615ce8565b613bcc9082615c75565b905060ff8416613bdc8684615ce8565b901c9150613beb826021615ce8565b613bf59082615c75565b905060ff8416613c058684615ce8565b901c9150613c14826001615ce8565b613c1e9082615c75565b9050600160ff85161b85613c426f0688589cc0e9505e2f2fee558000000084615cb2565b613c4c9190615c75565b610d5f9190615c75565b6000808211613c775760405162461bcd60e51b81526004016105be90615b07565b81613c81336109c3565b1015613c9f5760405162461bcd60e51b81526004016105be906159da565b6000613caa83610c29565b9050613cb63384613e9a565b61018654613cc49082613f8e565b610186556040517f42cdee5ad59c78ee53adb50e8d3f4b66918ce660505ab55abb44035e4c79ad1d90613cfc90339086908590615690565b60405180910390a192915050565b6000808211613d2b5760405162461bcd60e51b81526004016105be9061580d565b6000613d36836105e6565b9050613d423382613f9a565b61018654613d509084612840565b610186556040517fa9b5c37908cbe11fc228c10af0ce98bf944b069510207388ac875a3d2a314bbe90613cfc90339086908590615690565b60006f2f16ac6c59de6f8d5d6f63c1482a7c868211613db157613daa82614068565b9050610604565b81613dc06001607f1b80615ce8565b6106019190615cb2565b60006f2f16ac6c59de6f8d5d6f63c1482a7c868211613dec57613daa826148fb565b7001af16ac6c59de6f8d5d6f63c1482a7c808211613e0d57613daa8261519e565b706b22d43e72c326539cceeef8bb48f255ff821161020657613daa826152df565b600080610100831015613e61575b6001831115613e5c57600192831c92613e559082615c8d565b9050613e3c565b610601565b60805b60ff811615613e9357600160ff82161b8410613e885760ff81169390931c92908117905b60011c607f16613e64565b5092915050565b6001600160a01b038216613ec05760405162461bcd60e51b81526004016105be90615a11565b613ecc826000836109be565b6001600160a01b0382166000908152610180602052604090205481811015613f065760405162461bcd60e51b81526004016105be90615752565b6001600160a01b03831660009081526101806020526040812083830390556101828054849290613f37908490615d33565b90915550506040516000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90613f7a908690615c28565b60405180910390a36109be836000846109be565b60006128398284615d33565b6001600160a01b038216613fc05760405162461bcd60e51b81526004016105be90615bf1565b613fcc600083836109be565b806101826000828254613fdf9190615c75565b90915550506001600160a01b038216600090815261018060205260408120805483929061400d908490615c75565b90915550506040516001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90614050908590615c28565b60405180910390a3614064600083836109be565b5050565b600081816001607f1b61407b8380615ce8565b6140859190615cb2565b91506140a28270014d29a73a6e7b02c3668c7b0880000000615ce8565b6140ac9082615c75565b90506001607f1b6140bd8584615ce8565b6140c79190615cb2565b91506140e4827002504a0cd9a7f7215b60f9be4800000000615ce8565b6140ee9082615c75565b90506001607f1b6140ff8584615ce8565b6141099190615cb2565b915061412682700484d0a1191c0ead267967c7a4a0000000615ce8565b6141309082615c75565b90506001607f1b6141418584615ce8565b61414b9190615cb2565b91506141688270095ec580d7e8427a4baf26a90a00000000615ce8565b6141729082615c75565b90506001607f1b6141838584615ce8565b61418d9190615cb2565b91506141aa82701440b0be1615a47dba6e5b3b1f10000000615ce8565b6141b49082615c75565b90506001607f1b6141c58584615ce8565b6141cf9190615cb2565b91506141ec82702d207601f46a99b4112418400000000000615ce8565b6141f69082615c75565b90506001607f1b6142078584615ce8565b6142119190615cb2565b915061422e827066ebaac4c37c622dd8288a7eb1b2000000615ce8565b6142389082615c75565b90506001607f1b6142498584615ce8565b6142539190615cb2565b91506142708270ef17240135f7dbd43a1ba10cf200000000615ce8565b61427a9082615c75565b90506001607f1b61428b8584615ce8565b6142959190615cb2565b91506142b382710233c33c676a5eb2416094a87b3657000000615ce8565b6142bd9082615c75565b90506001607f1b6142ce8584615ce8565b6142d89190615cb2565b91506142f682710541cde48bc0254bed49a9f8700000000000615ce8565b6143009082615c75565b90506001607f1b6143118584615ce8565b61431b9190615cb2565b915061433982710cae1fad2cdd4d4cb8d73abca0d19a400000615ce8565b6143439082615c75565b90506001607f1b6143548584615ce8565b61435e9190615cb2565b915061437c82711edb2aa2f760d15c41ceedba956400000000615ce8565b6143869082615c75565b90506001607f1b6143978584615ce8565b6143a19190615cb2565b91506143bf82714ba8d20d2dabd386c9529659841a2e200000615ce8565b6143c99082615c75565b90506001607f1b6143da8584615ce8565b6143e49190615cb2565b91506143fc826805d6042a35c33e6d51604d1b615ce8565b6144069082615c75565b90506001607f1b6144178584615ce8565b6144219190615cb2565b9150614440827201cfa8e70c03625b9db76c8ebf5bbf24820000615ce8565b61444a9082615c75565b90506001607f1b61445b8584615ce8565b6144659190615cb2565b9150614484827204851d99f82060df265f3309b26f8200000000615ce8565b61448e9082615c75565b90506001607f1b61449f8584615ce8565b6144a99190615cb2565b91506144c882720b550d19b129d270c44f6f55f027723cbb0000615ce8565b6144d29082615c75565b90506001607f1b6144e38584615ce8565b6144ed9190615cb2565b915061450c82721c877dadc761dc272deb65d4b0000000000000615ce8565b6145169082615c75565b90506001607f1b6145278584615ce8565b6145319190615cb2565b9150614550827248178ece97479f33a77f2ad22a81b64406c000615ce8565b61455a9082615c75565b90506001607f1b61456b8584615ce8565b6145759190615cb2565b91506145948272b6ca8268b9d810fedf6695ef2f8a6c00000000615ce8565b61459e9082615c75565b90506001607f1b6145af8584615ce8565b6145b99190615cb2565b91506145d9827301d0e76631a5b05d007b8cb72a7c7f11ec36e000615ce8565b6145e39082615c75565b90506001607f1b6145f48584615ce8565b6145fe9190615cb2565b91506146178269094386f7b3f0bfb38d8f604f1b615ce8565b6146219082615c75565b90506001607f1b6146328584615ce8565b61463c9190615cb2565b915061465c82730bd8369f1b702bf491e2ebfcee08250313b65400615ce8565b6146669082615c75565b90506001607f1b6146778584615ce8565b6146819190615cb2565b91506146a182731e5c7c32a9f6c70ab2cb59d9225764d400000000615ce8565b6146ab9082615c75565b90506001607f1b6146bc8584615ce8565b6146c69190615cb2565b91506146e682734dff5820e165e910f95120a708e742496221e600615ce8565b6146f09082615c75565b90506001607f1b6147018584615ce8565b61470b9190615cb2565b9150614727826c064647b36d8fe769bc7728729b603d1b615ce8565b6147319082615c75565b90506001607f1b6147428584615ce8565b61474c9190615cb2565b915061476d82740205db8dffff45bfa2938f128f599dbf16eb11d880615ce8565b6147779082615c75565b90506001607f1b6147888584615ce8565b6147929190615cb2565b91506147b38274053a044ebd984351493e1786af38d39a0800000000615ce8565b6147bd9082615c75565b90506001607f1b6147ce8584615ce8565b6147d89190615cb2565b91506147f982740d86dae2a4cc0f47633a544479735869b487b59c40615ce8565b6148039082615c75565b90506001607f1b6148148584615ce8565b61481e9190615cb2565b915061482f82610231609c1b615ce8565b6148399082615c75565b90506001607f1b61484a8584615ce8565b6148549190615cb2565b915061487582745b0485a76f6646c2039db1507cdd51b08649680822615ce8565b61487f9082615c75565b90506001607f1b6148908584615ce8565b61489a9190615cb2565b91506148bb8274ec983c46c49545bc17efa6b5b0055e242200000000615ce8565b6148c59082615c75565b90506001607f1b846148e76fde1bc4d19efcac82445da75b0000000084615cb2565b6148f19190615c75565b6107189190615c75565b6000818161490d826001607f1b615d33565b614927906fde1bc4d19efcac82445da75b00000000615ce8565b90506001607f1b6149388584615ce8565b6149429190615cb2565b915061495f8270014d29a73a6e7b02c3668c7b0880000000615ce8565b6149699082615c75565b90506001607f1b61497a8584615ce8565b6149849190615cb2565b91506149a1827002504a0cd9a7f7215b60f9be4800000000615ce8565b6149ab9082615d33565b90506001607f1b6149bc8584615ce8565b6149c69190615cb2565b91506149e382700484d0a1191c0ead267967c7a4a0000000615ce8565b6149ed9082615c75565b90506001607f1b6149fe8584615ce8565b614a089190615cb2565b9150614a258270095ec580d7e8427a4baf26a90a00000000615ce8565b614a2f9082615d33565b90506001607f1b614a408584615ce8565b614a4a9190615cb2565b9150614a6782701440b0be1615a47dba6e5b3b1f10000000615ce8565b614a719082615c75565b90506001607f1b614a828584615ce8565b614a8c9190615cb2565b9150614aa982702d207601f46a99b4112418400000000000615ce8565b614ab39082615d33565b90506001607f1b614ac48584615ce8565b614ace9190615cb2565b9150614aeb827066ebaac4c37c622dd8288a7eb1b2000000615ce8565b614af59082615c75565b90506001607f1b614b068584615ce8565b614b109190615cb2565b9150614b2d8270ef17240135f7dbd43a1ba10cf200000000615ce8565b614b379082615d33565b90506001607f1b614b488584615ce8565b614b529190615cb2565b9150614b7082710233c33c676a5eb2416094a87b3657000000615ce8565b614b7a9082615c75565b90506001607f1b614b8b8584615ce8565b614b959190615cb2565b9150614bb382710541cde48bc0254bed49a9f8700000000000615ce8565b614bbd9082615d33565b90506001607f1b614bce8584615ce8565b614bd89190615cb2565b9150614bf682710cae1fad2cdd4d4cb8d73abca0d19a400000615ce8565b614c009082615c75565b90506001607f1b614c118584615ce8565b614c1b9190615cb2565b9150614c3982711edb2aa2f760d15c41ceedba956400000000615ce8565b614c439082615d33565b90506001607f1b614c548584615ce8565b614c5e9190615cb2565b9150614c7c82714ba8d20d2dabd386c9529659841a2e200000615ce8565b614c869082615c75565b90506001607f1b614c978584615ce8565b614ca19190615cb2565b9150614cb9826805d6042a35c33e6d51604d1b615ce8565b614cc39082615d33565b90506001607f1b614cd48584615ce8565b614cde9190615cb2565b9150614cfd827201cfa8e70c03625b9db76c8ebf5bbf24820000615ce8565b614d079082615c75565b90506001607f1b614d188584615ce8565b614d229190615cb2565b9150614d41827204851d99f82060df265f3309b26f8200000000615ce8565b614d4b9082615d33565b90506001607f1b614d5c8584615ce8565b614d669190615cb2565b9150614d8582720b550d19b129d270c44f6f55f027723cbb0000615ce8565b614d8f9082615c75565b90506001607f1b614da08584615ce8565b614daa9190615cb2565b9150614dc982721c877dadc761dc272deb65d4b0000000000000615ce8565b614dd39082615d33565b90506001607f1b614de48584615ce8565b614dee9190615cb2565b9150614e0d827248178ece97479f33a77f2ad22a81b64406c000615ce8565b614e179082615c75565b90506001607f1b614e288584615ce8565b614e329190615cb2565b9150614e518272b6ca8268b9d810fedf6695ef2f8a6c00000000615ce8565b614e5b9082615d33565b90506001607f1b614e6c8584615ce8565b614e769190615cb2565b9150614e96827301d0e76631a5b05d007b8cb72a7c7f11ec36e000615ce8565b614ea09082615c75565b90506001607f1b614eb18584615ce8565b614ebb9190615cb2565b9150614ed48269094386f7b3f0bfb38d8f604f1b615ce8565b614ede9082615d33565b90506001607f1b614eef8584615ce8565b614ef99190615cb2565b9150614f1982730bd8369f1b702bf491e2ebfcee08250313b65400615ce8565b614f239082615c75565b90506001607f1b614f348584615ce8565b614f3e9190615cb2565b9150614f5e82731e5c7c32a9f6c70ab2cb59d9225764d400000000615ce8565b614f689082615d33565b90506001607f1b614f798584615ce8565b614f839190615cb2565b9150614fa382734dff5820e165e910f95120a708e742496221e600615ce8565b614fad9082615c75565b90506001607f1b614fbe8584615ce8565b614fc89190615cb2565b9150614fe4826c064647b36d8fe769bc7728729b603d1b615ce8565b614fee9082615d33565b90506001607f1b614fff8584615ce8565b6150099190615cb2565b915061502a82740205db8dffff45bfa2938f128f599dbf16eb11d880615ce8565b6150349082615c75565b90506001607f1b6150458584615ce8565b61504f9190615cb2565b91506150708274053a044ebd984351493e1786af38d39a0800000000615ce8565b61507a9082615d33565b90506001607f1b61508b8584615ce8565b6150959190615cb2565b91506150b682740d86dae2a4cc0f47633a544479735869b487b59c40615ce8565b6150c09082615c75565b90506001607f1b6150d18584615ce8565b6150db9190615cb2565b91506150ec82610231609c1b615ce8565b6150f69082615d33565b90506001607f1b6151078584615ce8565b6151119190615cb2565b915061513282745b0485a76f6646c2039db1507cdd51b08649680822615ce8565b61513c9082615c75565b90506001607f1b61514d8584615ce8565b6151579190615cb2565b91506151788274ec983c46c49545bc17efa6b5b0055e242200000000615ce8565b6151829082615d33565b90506107186fde1bc4d19efcac82445da75b0000000082615cb2565b60008060016151bd6f2f16ac6c59de6f8d5d6f63c1482a7c8685615d33565b6151c79190615d33565b905060006151e56f03060c183060c183060c183060c1830683615cb2565b90506000615203826f03060c183060c183060c183060c18306615ce8565b90506000615212836001615c75565b61522c906f03060c183060c183060c183060c18306615ce8565b905060006080846080811061525157634e487b7160e01b600052603260045260246000fd5b0154905060006080615264866001615c75565b6080811061528257634e487b7160e01b600052603260045260246000fd5b015490506f03060c183060c183060c183060c183066152a18588615d33565b6152ab9083615ce8565b6152b58886615d33565b6152bf9085615ce8565b6152c99190615c75565b6152d39190615cb2565b98975050505050505050565b60008070015bf0a8b1457695355fb8ac404e7a79e383106153085761530383612e04565b615311565b6153118361288b565b9050600070015bf0a8b1457695355fb8ac404e7a79e3821061533b5761533682612e04565b615344565b6153448261288b565b9050836001607f1b836153578285615ce8565b6153619190615cb2565b61536b8486615d33565b6153759190615c75565b61537f9190615ce8565b6107189190615cb2565b80356001600160a01b038116811461060457600080fd5b803563ffffffff8116811461060457600080fd5b6000602082840312156153c5578081fd5b61283982615389565b600080604083850312156153e0578081fd5b6153e983615389565b91506153f760208401615389565b90509250929050565b600080600060608486031215615414578081fd5b61541d84615389565b925061542b60208501615389565b9150604084013590509250925092565b6000806040838503121561544d578182fd5b61545683615389565b946020939093013593505050565b600060208284031215615475578081fd5b815161283981615e05565b600060208284031215615491578081fd5b5035919050565b6000602082840312156154a9578081fd5b5051919050565b600080604083850312156154c2578182fd5b50508035926020909101359150565b600080600080600060a086880312156154e8578081fd5b85359450602086013593506040860135925060608601359150608086013561550f81615e05565b809150509295509295909350565b600080600080600060a08688031215615534578081fd5b505083359560208501359550604085013594606081013594506080013592509050565b6000806000806080858703121561556c578384fd5b8435935060208501359250615583604086016153a0565b9396929550929360600135925050565b600080600080608085870312156155a8578384fd5b84359350602085013592506155bf604086016153a0565b91506155cd606086016153a0565b905092959194509250565b600080600080600060a086880312156155ef578081fd5b853594506155ff602087016153a0565b935060408601359250615614606087016153a0565b949793965091946080013592915050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b039390931683526020830191909152604082015260600190565b901515815260200190565b6000602080835283518082850152825b818110156156e8578581018301518582016040015282016156cc565b818111156156f95783604083870101525b50601f01601f1916929092016040019392505050565b60208082526023908201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260408201526265737360e81b606082015260800190565b60208082526022908201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604082015261636560f01b606082015260800190565b6020808252601d908201527f4661696c656420746f207472616e736665722044616920746f6b656e73000000604082015260600190565b60208082526022908201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604082015261737360f01b606082015260800190565b60208082526019908201527f4465706f736974206d757374206265206e6f6e2d7a65726f2e00000000000000604082015260600190565b60208082526026908201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604082015265616c616e636560d01b606082015260800190565b6020808252601b908201527f4552525f494e56414c49445f524553455256455f42414c414e43450000000000604082015260600190565b6020808252601f908201527f4d75737420617070726f76652044414920746f2062757920746f6b656e732e00604082015260600190565b6020808252601290820152714552525f494e56414c49445f535550504c5960701b604082015260600190565b60208082526019908201527f4552525f494e56414c49445f524553455256455f524154494f00000000000000604082015260600190565b60208082526028908201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616040820152676c6c6f77616e636560c01b606082015260800190565b60208082526018908201527f4d75737420617070726f766520656e6f756768204441492e0000000000000000604082015260600190565b6020808252601c908201527f496e73756666696369656e7420746f6b656e7320746f206275726e2e00000000604082015260600190565b60208082526021908201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736040820152607360f81b606082015260800190565b60208082526025908201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604082015264647265737360d81b606082015260800190565b60208082526012908201527111549497d253959053125117d05353d5539560721b604082015260600190565b60208082526024908201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646040820152637265737360e01b606082015260800190565b60208082526018908201527f416d6f756e74206d757374206265206e6f6e2d7a65726f2e0000000000000000604082015260600190565b6020808252601a908201527f4552525f494e56414c49445f524553455256455f574549474854000000000000604082015260600190565b60208082526018908201527f4552525f494e56414c49445f524553455256455f524154450000000000000000604082015260600190565b60208082526025908201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604082015264207a65726f60d81b606082015260800190565b6020808252601f908201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604082015260600190565b90815260200190565b918252602082015260400190565b91825260ff16602082015260400190565b63ffffffff92831681529116602082015260400190565b60ff91909116815260200190565b60008219821115615c8857615c88615dd9565b500190565b600060ff821660ff84168060ff03821115615caa57615caa615dd9565b019392505050565b600082615cc157615cc1615def565b500490565b600060ff831680615cd957615cd9615def565b8060ff84160491505092915050565b6000816000190483118215151615615d0257615d02615dd9565b500290565b600063ffffffff80831681851681830481118215151615615d2a57615d2a615dd9565b02949350505050565b600082821015615d4557615d45615dd9565b500390565b600060ff821660ff841680821015615d6457615d64615dd9565b90039392505050565b600060ff821680615d8057615d80615dd9565b6000190192915050565b600281046001821680615d9e57607f821691505b60208210811415615dbf57634e487b7160e01b600052602260045260246000fd5b50919050565b600082615dd457615dd4615def565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b8015158114615e1357600080fd5b5056fea2646970667358221220ef5825b993891458b48a0f8873f3a2659e644e8dd9ceae4df4d96b2d60fe043b64736f6c63430008000033