false
false

Contract Address Details

0x4C6ecbD9728e4d6A867c2aE7e2D560B65DF9fEA7

Contract Name
FortOptions
Creator
0x0e2020–cfcfa4 at 0x9b042e–63b478
Balance
0 KCS
Tokens
Fetching tokens...
Transactions
0 Transactions
Transfers
0 Transfers
Gas Used
Fetching gas used...
Last Balance Update
51460177
Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
Contract name:
FortOptions




Optimization enabled
true
Compiler version
v0.8.13+commit.abaa5c0e




Optimization runs
888888
EVM Version
default




Verified at
2022-03-21T09:55:30.964770Z

Contract source code

// Sources flattened with hardhat v2.5.0 https://hardhat.org

// File contracts/libs/ABDKMath64x64.sol

// SPDX-License-Identifier: BSD-4-Clause
/*
 * ABDK Math 64.64 Smart Contract Library.  Copyright © 2019 by ABDK Consulting.
 * Author: Mikhail Vladimirov <[email protected]>
 */
pragma solidity ^0.8.0;

/**
 * Smart contract library of mathematical functions operating with signed
 * 64.64-bit fixed point numbers.  Signed 64.64-bit fixed point number is
 * basically a simple fraction whose numerator is signed 128-bit integer and
 * denominator is 2^64.  As long as denominator is always the same, there is no
 * need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
 * represented by int128 type holding only the numerator.
 */
library ABDKMath64x64 {
  /*
   * Minimum value signed 64.64-bit fixed point number may have. 
   */
  int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;

  /*
   * Maximum value signed 64.64-bit fixed point number may have. 
   */
  int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;

  /**
   * Convert signed 256-bit integer number into signed 64.64-bit fixed point
   * number.  Revert on overflow.
   *
   * @param x signed 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function fromInt (int256 x) internal pure returns (int128) {
    unchecked {
      require (x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF);
      return int128 (x << 64);
    }
  }

  /**
   * Convert signed 64.64 fixed point number into signed 64-bit integer number
   * rounding down.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64-bit integer number
   */
  function toInt (int128 x) internal pure returns (int64) {
    unchecked {
      return int64 (x >> 64);
    }
  }

  /**
   * Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
   * number.  Revert on overflow.
   *
   * @param x unsigned 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function fromUInt (uint256 x) internal pure returns (int128) {
    unchecked {
      require (x <= 0x7FFFFFFFFFFFFFFF);
      return int128 (int256 (x << 64));
    }
  }

  /**
   * Convert signed 64.64 fixed point number into unsigned 64-bit integer
   * number rounding down.  Revert on underflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return unsigned 64-bit integer number
   */
  function toUInt (int128 x) internal pure returns (uint64) {
    unchecked {
      require (x >= 0);
      return uint64 (uint128 (x >> 64));
    }
  }

  /**
   * Convert signed 128.128 fixed point number into signed 64.64-bit fixed point
   * number rounding down.  Revert on overflow.
   *
   * @param x signed 128.128-bin fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function from128x128 (int256 x) internal pure returns (int128) {
    unchecked {
      int256 result = x >> 64;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Convert signed 64.64 fixed point number into signed 128.128 fixed point
   * number.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 128.128 fixed point number
   */
  function to128x128 (int128 x) internal pure returns (int256) {
    unchecked {
      return int256 (x) << 64;
    }
  }

  /**
   * Calculate x + y.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function add (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) + y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x - y.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function sub (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) - y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x * y rounding down.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function mul (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 result = int256(x) * y >> 64;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x * y rounding towards zero, where x is signed 64.64 fixed point
   * number and y is signed 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y signed 256-bit integer number
   * @return signed 256-bit integer number
   */
  function muli (int128 x, int256 y) internal pure returns (int256) {
    unchecked {
      if (x == MIN_64x64) {
        require (y >= -0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF &&
          y <= 0x1000000000000000000000000000000000000000000000000);
        return -y << 63;
      } else {
        bool negativeResult = false;
        if (x < 0) {
          x = -x;
          negativeResult = true;
        }
        if (y < 0) {
          y = -y; // We rely on overflow behavior here
          negativeResult = !negativeResult;
        }
        uint256 absoluteResult = mulu (x, uint256 (y));
        if (negativeResult) {
          require (absoluteResult <=
            0x8000000000000000000000000000000000000000000000000000000000000000);
          return -int256 (absoluteResult); // We rely on overflow behavior here
        } else {
          require (absoluteResult <=
            0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
          return int256 (absoluteResult);
        }
      }
    }
  }

  /**
   * Calculate x * y rounding down, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64 fixed point number
   * @param y unsigned 256-bit integer number
   * @return unsigned 256-bit integer number
   */
  function mulu (int128 x, uint256 y) internal pure returns (uint256) {
    unchecked {
      if (y == 0) return 0;

      require (x >= 0);

      uint256 lo = (uint256 (int256 (x)) * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) >> 64;
      uint256 hi = uint256 (int256 (x)) * (y >> 128);

      require (hi <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
      hi <<= 64;

      require (hi <=
        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - lo);
      return hi + lo;
    }
  }

  /**
   * Calculate x / y rounding towards zero.  Revert on overflow or when y is
   * zero.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function div (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);
      int256 result = (int256 (x) << 64) / y;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are signed 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x signed 256-bit integer number
   * @param y signed 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function divi (int256 x, int256 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);

      bool negativeResult = false;
      if (x < 0) {
        x = -x; // We rely on overflow behavior here
        negativeResult = true;
      }
      if (y < 0) {
        y = -y; // We rely on overflow behavior here
        negativeResult = !negativeResult;
      }
      uint128 absoluteResult = divuu (uint256 (x), uint256 (y));
      if (negativeResult) {
        require (absoluteResult <= 0x80000000000000000000000000000000);
        return -int128 (absoluteResult); // We rely on overflow behavior here
      } else {
        require (absoluteResult <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
        return int128 (absoluteResult); // We rely on overflow behavior here
      }
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x unsigned 256-bit integer number
   * @param y unsigned 256-bit integer number
   * @return signed 64.64-bit fixed point number
   */
  function divu (uint256 x, uint256 y) internal pure returns (int128) {
    unchecked {
      require (y != 0);
      uint128 result = divuu (x, y);
      require (result <= uint128 (MAX_64x64));
      return int128 (result);
    }
  }

  /**
   * Calculate -x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function neg (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != MIN_64x64);
      return -x;
    }
  }

  /**
   * Calculate |x|.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function abs (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != MIN_64x64);
      return x < 0 ? -x : x;
    }
  }

  /**
   * Calculate 1 / x rounding towards zero.  Revert on overflow or when x is
   * zero.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function inv (int128 x) internal pure returns (int128) {
    unchecked {
      require (x != 0);
      int256 result = int256 (0x100000000000000000000000000000000) / x;
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate arithmetics average of x and y, i.e. (x + y) / 2 rounding down.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function avg (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      return int128 ((int256 (x) + int256 (y)) >> 1);
    }
  }

  /**
   * Calculate geometric average of x and y, i.e. sqrt (x * y) rounding down.
   * Revert on overflow or in case x * y is negative.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function gavg (int128 x, int128 y) internal pure returns (int128) {
    unchecked {
      int256 m = int256 (x) * int256 (y);
      require (m >= 0);
      require (m <
          0x4000000000000000000000000000000000000000000000000000000000000000);
      return int128 (sqrtu (uint256 (m)));
    }
  }

  /**
   * Calculate x^y assuming 0^0 is 1, where x is signed 64.64 fixed point number
   * and y is unsigned 256-bit integer number.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @param y uint256 value
   * @return signed 64.64-bit fixed point number
   */
  function pow (int128 x, uint256 y) internal pure returns (int128) {
    unchecked {
      bool negative = x < 0 && y & 1 == 1;

      uint256 absX = uint128 (x < 0 ? -x : x);
      uint256 absResult;
      absResult = 0x100000000000000000000000000000000;

      if (absX <= 0x10000000000000000) {
        absX <<= 63;
        while (y != 0) {
          if (y & 0x1 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x2 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x4 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          if (y & 0x8 != 0) {
            absResult = absResult * absX >> 127;
          }
          absX = absX * absX >> 127;

          y >>= 4;
        }

        absResult >>= 64;
      } else {
        uint256 absXShift = 63;
        if (absX < 0x1000000000000000000000000) { absX <<= 32; absXShift -= 32; }
        if (absX < 0x10000000000000000000000000000) { absX <<= 16; absXShift -= 16; }
        if (absX < 0x1000000000000000000000000000000) { absX <<= 8; absXShift -= 8; }
        if (absX < 0x10000000000000000000000000000000) { absX <<= 4; absXShift -= 4; }
        if (absX < 0x40000000000000000000000000000000) { absX <<= 2; absXShift -= 2; }
        if (absX < 0x80000000000000000000000000000000) { absX <<= 1; absXShift -= 1; }

        uint256 resultShift = 0;
        while (y != 0) {
          require (absXShift < 64);

          if (y & 0x1 != 0) {
            absResult = absResult * absX >> 127;
            resultShift += absXShift;
            if (absResult > 0x100000000000000000000000000000000) {
              absResult >>= 1;
              resultShift += 1;
            }
          }
          absX = absX * absX >> 127;
          absXShift <<= 1;
          if (absX >= 0x100000000000000000000000000000000) {
              absX >>= 1;
              absXShift += 1;
          }

          y >>= 1;
        }

        require (resultShift < 64);
        absResult >>= 64 - resultShift;
      }
      int256 result = negative ? -int256 (absResult) : int256 (absResult);
      require (result >= MIN_64x64 && result <= MAX_64x64);
      return int128 (result);
    }
  }

  /**
   * Calculate sqrt (x) rounding down.  Revert if x < 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function sqrt (int128 x) internal pure returns (int128) {
    unchecked {
      require (x >= 0);
      return int128 (sqrtu (uint256 (int256 (x)) << 64));
    }
  }

  /**
   * Calculate binary logarithm of x.  Revert if x <= 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function log_2 (int128 x) internal pure returns (int128) {
    unchecked {
      require (x > 0);

      int256 msb = 0;
      int256 xc = x;
      if (xc >= 0x10000000000000000) { xc >>= 64; msb += 64; }
      if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
      if (xc >= 0x10000) { xc >>= 16; msb += 16; }
      if (xc >= 0x100) { xc >>= 8; msb += 8; }
      if (xc >= 0x10) { xc >>= 4; msb += 4; }
      if (xc >= 0x4) { xc >>= 2; msb += 2; }
      if (xc >= 0x2) msb += 1;  // No need to shift xc anymore

      int256 result = msb - 64 << 64;
      uint256 ux = uint256 (int256 (x)) << uint256 (127 - msb);
      for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
        ux *= ux;
        uint256 b = ux >> 255;
        ux >>= 127 + b;
        result += bit * int256 (b);
      }

      return int128 (result);
    }
  }

  /**
   * Calculate natural logarithm of x.  Revert if x <= 0.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function ln (int128 x) internal pure returns (int128) {
    unchecked {
      require (x > 0);

      return int128 (int256 (
          uint256 (int256 (log_2 (x))) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF >> 128));
    }
  }

  /**
   * Calculate binary exponent of x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function exp_2 (int128 x) internal pure returns (int128) {
    unchecked {
      require (x < 0x400000000000000000); // Overflow

      if (x < -0x400000000000000000) return 0; // Underflow

      uint256 result = 0x80000000000000000000000000000000;

      if (x & 0x8000000000000000 > 0)
        result = result * 0x16A09E667F3BCC908B2FB1366EA957D3E >> 128;
      if (x & 0x4000000000000000 > 0)
        result = result * 0x1306FE0A31B7152DE8D5A46305C85EDEC >> 128;
      if (x & 0x2000000000000000 > 0)
        result = result * 0x1172B83C7D517ADCDF7C8C50EB14A791F >> 128;
      if (x & 0x1000000000000000 > 0)
        result = result * 0x10B5586CF9890F6298B92B71842A98363 >> 128;
      if (x & 0x800000000000000 > 0)
        result = result * 0x1059B0D31585743AE7C548EB68CA417FD >> 128;
      if (x & 0x400000000000000 > 0)
        result = result * 0x102C9A3E778060EE6F7CACA4F7A29BDE8 >> 128;
      if (x & 0x200000000000000 > 0)
        result = result * 0x10163DA9FB33356D84A66AE336DCDFA3F >> 128;
      if (x & 0x100000000000000 > 0)
        result = result * 0x100B1AFA5ABCBED6129AB13EC11DC9543 >> 128;
      if (x & 0x80000000000000 > 0)
        result = result * 0x10058C86DA1C09EA1FF19D294CF2F679B >> 128;
      if (x & 0x40000000000000 > 0)
        result = result * 0x1002C605E2E8CEC506D21BFC89A23A00F >> 128;
      if (x & 0x20000000000000 > 0)
        result = result * 0x100162F3904051FA128BCA9C55C31E5DF >> 128;
      if (x & 0x10000000000000 > 0)
        result = result * 0x1000B175EFFDC76BA38E31671CA939725 >> 128;
      if (x & 0x8000000000000 > 0)
        result = result * 0x100058BA01FB9F96D6CACD4B180917C3D >> 128;
      if (x & 0x4000000000000 > 0)
        result = result * 0x10002C5CC37DA9491D0985C348C68E7B3 >> 128;
      if (x & 0x2000000000000 > 0)
        result = result * 0x1000162E525EE054754457D5995292026 >> 128;
      if (x & 0x1000000000000 > 0)
        result = result * 0x10000B17255775C040618BF4A4ADE83FC >> 128;
      if (x & 0x800000000000 > 0)
        result = result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAB >> 128;
      if (x & 0x400000000000 > 0)
        result = result * 0x100002C5C89D5EC6CA4D7C8ACC017B7C9 >> 128;
      if (x & 0x200000000000 > 0)
        result = result * 0x10000162E43F4F831060E02D839A9D16D >> 128;
      if (x & 0x100000000000 > 0)
        result = result * 0x100000B1721BCFC99D9F890EA06911763 >> 128;
      if (x & 0x80000000000 > 0)
        result = result * 0x10000058B90CF1E6D97F9CA14DBCC1628 >> 128;
      if (x & 0x40000000000 > 0)
        result = result * 0x1000002C5C863B73F016468F6BAC5CA2B >> 128;
      if (x & 0x20000000000 > 0)
        result = result * 0x100000162E430E5A18F6119E3C02282A5 >> 128;
      if (x & 0x10000000000 > 0)
        result = result * 0x1000000B1721835514B86E6D96EFD1BFE >> 128;
      if (x & 0x8000000000 > 0)
        result = result * 0x100000058B90C0B48C6BE5DF846C5B2EF >> 128;
      if (x & 0x4000000000 > 0)
        result = result * 0x10000002C5C8601CC6B9E94213C72737A >> 128;
      if (x & 0x2000000000 > 0)
        result = result * 0x1000000162E42FFF037DF38AA2B219F06 >> 128;
      if (x & 0x1000000000 > 0)
        result = result * 0x10000000B17217FBA9C739AA5819F44F9 >> 128;
      if (x & 0x800000000 > 0)
        result = result * 0x1000000058B90BFCDEE5ACD3C1CEDC823 >> 128;
      if (x & 0x400000000 > 0)
        result = result * 0x100000002C5C85FE31F35A6A30DA1BE50 >> 128;
      if (x & 0x200000000 > 0)
        result = result * 0x10000000162E42FF0999CE3541B9FFFCF >> 128;
      if (x & 0x100000000 > 0)
        result = result * 0x100000000B17217F80F4EF5AADDA45554 >> 128;
      if (x & 0x80000000 > 0)
        result = result * 0x10000000058B90BFBF8479BD5A81B51AD >> 128;
      if (x & 0x40000000 > 0)
        result = result * 0x1000000002C5C85FDF84BD62AE30A74CC >> 128;
      if (x & 0x20000000 > 0)
        result = result * 0x100000000162E42FEFB2FED257559BDAA >> 128;
      if (x & 0x10000000 > 0)
        result = result * 0x1000000000B17217F7D5A7716BBA4A9AE >> 128;
      if (x & 0x8000000 > 0)
        result = result * 0x100000000058B90BFBE9DDBAC5E109CCE >> 128;
      if (x & 0x4000000 > 0)
        result = result * 0x10000000002C5C85FDF4B15DE6F17EB0D >> 128;
      if (x & 0x2000000 > 0)
        result = result * 0x1000000000162E42FEFA494F1478FDE05 >> 128;
      if (x & 0x1000000 > 0)
        result = result * 0x10000000000B17217F7D20CF927C8E94C >> 128;
      if (x & 0x800000 > 0)
        result = result * 0x1000000000058B90BFBE8F71CB4E4B33D >> 128;
      if (x & 0x400000 > 0)
        result = result * 0x100000000002C5C85FDF477B662B26945 >> 128;
      if (x & 0x200000 > 0)
        result = result * 0x10000000000162E42FEFA3AE53369388C >> 128;
      if (x & 0x100000 > 0)
        result = result * 0x100000000000B17217F7D1D351A389D40 >> 128;
      if (x & 0x80000 > 0)
        result = result * 0x10000000000058B90BFBE8E8B2D3D4EDE >> 128;
      if (x & 0x40000 > 0)
        result = result * 0x1000000000002C5C85FDF4741BEA6E77E >> 128;
      if (x & 0x20000 > 0)
        result = result * 0x100000000000162E42FEFA39FE95583C2 >> 128;
      if (x & 0x10000 > 0)
        result = result * 0x1000000000000B17217F7D1CFB72B45E1 >> 128;
      if (x & 0x8000 > 0)
        result = result * 0x100000000000058B90BFBE8E7CC35C3F0 >> 128;
      if (x & 0x4000 > 0)
        result = result * 0x10000000000002C5C85FDF473E242EA38 >> 128;
      if (x & 0x2000 > 0)
        result = result * 0x1000000000000162E42FEFA39F02B772C >> 128;
      if (x & 0x1000 > 0)
        result = result * 0x10000000000000B17217F7D1CF7D83C1A >> 128;
      if (x & 0x800 > 0)
        result = result * 0x1000000000000058B90BFBE8E7BDCBE2E >> 128;
      if (x & 0x400 > 0)
        result = result * 0x100000000000002C5C85FDF473DEA871F >> 128;
      if (x & 0x200 > 0)
        result = result * 0x10000000000000162E42FEFA39EF44D91 >> 128;
      if (x & 0x100 > 0)
        result = result * 0x100000000000000B17217F7D1CF79E949 >> 128;
      if (x & 0x80 > 0)
        result = result * 0x10000000000000058B90BFBE8E7BCE544 >> 128;
      if (x & 0x40 > 0)
        result = result * 0x1000000000000002C5C85FDF473DE6ECA >> 128;
      if (x & 0x20 > 0)
        result = result * 0x100000000000000162E42FEFA39EF366F >> 128;
      if (x & 0x10 > 0)
        result = result * 0x1000000000000000B17217F7D1CF79AFA >> 128;
      if (x & 0x8 > 0)
        result = result * 0x100000000000000058B90BFBE8E7BCD6D >> 128;
      if (x & 0x4 > 0)
        result = result * 0x10000000000000002C5C85FDF473DE6B2 >> 128;
      if (x & 0x2 > 0)
        result = result * 0x1000000000000000162E42FEFA39EF358 >> 128;
      if (x & 0x1 > 0)
        result = result * 0x10000000000000000B17217F7D1CF79AB >> 128;

      result >>= uint256 (int256 (63 - (x >> 64)));
      require (result <= uint256 (int256 (MAX_64x64)));

      return int128 (int256 (result));
    }
  }

  /**
   * Calculate natural exponent of x.  Revert on overflow.
   *
   * @param x signed 64.64-bit fixed point number
   * @return signed 64.64-bit fixed point number
   */
  function exp (int128 x) internal pure returns (int128) {
    unchecked {
      require (x < 0x400000000000000000); // Overflow

      if (x < -0x400000000000000000) return 0; // Underflow

      return exp_2 (
          int128 (int256 (x) * 0x171547652B82FE1777D0FFDA0D23A7D12 >> 128));
    }
  }

  /**
   * Calculate x / y rounding towards zero, where x and y are unsigned 256-bit
   * integer numbers.  Revert on overflow or when y is zero.
   *
   * @param x unsigned 256-bit integer number
   * @param y unsigned 256-bit integer number
   * @return unsigned 64.64-bit fixed point number
   */
  function divuu (uint256 x, uint256 y) private pure returns (uint128) {
    unchecked {
      require (y != 0);

      uint256 result;

      if (x <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
        result = (x << 64) / y;
      else {
        uint256 msb = 192;
        uint256 xc = x >> 192;
        if (xc >= 0x100000000) { xc >>= 32; msb += 32; }
        if (xc >= 0x10000) { xc >>= 16; msb += 16; }
        if (xc >= 0x100) { xc >>= 8; msb += 8; }
        if (xc >= 0x10) { xc >>= 4; msb += 4; }
        if (xc >= 0x4) { xc >>= 2; msb += 2; }
        if (xc >= 0x2) msb += 1;  // No need to shift xc anymore

        result = (x << 255 - msb) / ((y - 1 >> msb - 191) + 1);
        require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        uint256 hi = result * (y >> 128);
        uint256 lo = result * (y & 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);

        uint256 xh = x >> 192;
        uint256 xl = x << 64;

        if (xl < lo) xh -= 1;
        xl -= lo; // We rely on overflow behavior here
        lo = hi << 128;
        if (xl < lo) xh -= 1;
        xl -= lo; // We rely on overflow behavior here

        assert (xh == hi >> 128);

        result += xl / y;
      }

      require (result <= 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF);
      return uint128 (result);
    }
  }

  /**
   * Calculate sqrt (x) rounding down, where x is unsigned 256-bit integer
   * number.
   *
   * @param x unsigned 256-bit integer number
   * @return unsigned 128-bit integer number
   */
  function sqrtu (uint256 x) private pure returns (uint128) {
    unchecked {
      if (x == 0) return 0;
      else {
        uint256 xx = x;
        uint256 r = 1;
        if (xx >= 0x100000000000000000000000000000000) { xx >>= 128; r <<= 64; }
        if (xx >= 0x10000000000000000) { xx >>= 64; r <<= 32; }
        if (xx >= 0x100000000) { xx >>= 32; r <<= 16; }
        if (xx >= 0x10000) { xx >>= 16; r <<= 8; }
        if (xx >= 0x100) { xx >>= 8; r <<= 4; }
        if (xx >= 0x10) { xx >>= 4; r <<= 2; }
        if (xx >= 0x8) { r <<= 1; }
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1;
        r = (r + x / r) >> 1; // Seven iterations should be enough
        uint256 r1 = x / r;
        return uint128 (r < r1 ? r : r1);
      }
    }
  }
}


// File contracts/interfaces/IFortOptions.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;

/// @dev Define methods for european option
interface IFortOptions {
    
    /// @dev Option structure for view methods
    struct OptionView {
        uint index;
        address tokenAddress;
        uint strikePrice;
        bool orientation;
        uint exerciseBlock;
        uint balance;
        address owner;
    }
    
    /// @dev Option open event
    /// @param index Index of option
    /// @param dcuAmount Amount of paid DCU
    /// @param owner Owner of this option
    /// @param amount Amount of option
    event Open(
        uint index,
        uint dcuAmount,
        address owner,
        uint amount
    );

    /// @dev Option exercise event
    /// @param index Index of option
    /// @param amount Amount of option to exercise
    /// @param owner Owner of this option
    /// @param gain Amount of dcu gained
    event Exercise(uint index, uint amount, address owner, uint gain);
    
    /// @dev Option sell event
    /// @param index Index of option
    /// @param amount Amount of option to sell
    /// @param owner Owner of this option
    /// @param dcuAmount Amount of dcu acquired
    event Sell(uint index, uint amount, address owner, uint dcuAmount);

    /// @dev Returns the share of the specified option for target address
    /// @param index Index of the option
    /// @param addr Target address
    function balanceOf(uint index, address addr) external view returns (uint);

    /// @dev Find the options of the target address (in reverse order)
    /// @param start Find forward from the index corresponding to the given contract address 
    /// (excluding the record corresponding to start)
    /// @param count Maximum number of records returned
    /// @param maxFindCount Find records at most
    /// @param owner Target address
    /// @return optionArray Matched option array
    function find(
        uint start, 
        uint count, 
        uint maxFindCount, 
        address owner
    ) external view returns (OptionView[] memory optionArray);

    /// @dev List options
    /// @param offset Skip previous (offset) records
    /// @param count Return (count) records
    /// @param order Order. 0 reverse order, non-0 positive order
    /// @return optionArray Matched option array
    function list(uint offset, uint count, uint order) external view returns (OptionView[] memory optionArray);
    
    /// @dev Obtain the number of European options that have been opened
    /// @return Number of European options opened
    function getOptionCount() external view returns (uint);

    /// @dev Estimate the amount of option
    /// @param tokenAddress Target token address, 0 means eth
    /// @param oraclePrice Current price from oracle
    /// @param strikePrice The exercise price set by the user. During settlement, the system will compare the 
    /// current price of the subject matter with the exercise price to calculate the user's profit and loss
    /// @param orientation true: call, false: put
    /// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
    /// recorded in the system using the block number
    /// @param dcuAmount Amount of paid DCU
    /// @return amount Amount of option
    function estimate(
        address tokenAddress,
        uint oraclePrice,
        uint strikePrice,
        bool orientation,
        uint exerciseBlock,
        uint dcuAmount
    ) external view returns (uint amount);

    /// @dev Open option
    /// @param tokenAddress Target token address, 0 means eth
    /// @param strikePrice The exercise price set by the user. During settlement, the system will compare the 
    /// current price of the subject matter with the exercise price to calculate the user's profit and loss
    /// @param orientation true: call, false: put
    /// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
    /// recorded in the system using the block number
    /// @param dcuAmount Amount of paid DCU
    function open(
        address tokenAddress,
        uint strikePrice,
        bool orientation,
        uint exerciseBlock,
        uint dcuAmount
    ) external payable;

    /// @dev Exercise option
    /// @param index Index of option
    /// @param amount Amount of option to exercise
    function exercise(uint index, uint amount) external payable;

    /// @dev Sell option
    /// @param index Index of option
    /// @param amount Amount of option to sell
    function sell(uint index, uint amount) external payable;

    /// @dev Calculate option price
    /// @param oraclePrice Current price from oracle
    /// @param strikePrice The exercise price set by the user. During settlement, the system will compare the 
    /// current price of the subject matter with the exercise price to calculate the user's profit and loss
    /// @param orientation true: call, false: put
    /// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
    /// recorded in the system using the block number
    /// @return v Option price. Need to divide (USDT_BASE << 64)
    function calcV(
        address tokenAddress,
        uint oraclePrice,
        uint strikePrice,
        bool orientation,
        uint exerciseBlock
    ) external view returns (uint v);
}


// File contracts/custom/ChainParameter.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;

/// @dev Base contract of Fort
contract ChainParameter {

    // Block time. ethereum 14 seconds, BSC 3 seconds, polygon 2.2 seconds
    uint constant BLOCK_TIME = 3;
    
    // Minimal exercise block period. 840000
    // TODO:
    uint constant MIN_PERIOD = 10;
}


// File contracts/interfaces/IFortMapping.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;

/// @dev The interface defines methods for Fort builtin contract address mapping
interface IFortMapping {

    /// @dev Address updated event
    /// @param name Address name
    /// @param oldAddress Old address
    /// @param newAddress New address
    event AddressUpdated(string name, address oldAddress, address newAddress);

    /// @dev Set the built-in contract address of the system
    /// @param dcuToken Address of dcu token contract
    /// @param fortDAO IFortDAO implementation contract address
    /// @param fortOptions IFortOptions implementation contract address
    /// @param fortFutures IFortFutures implementation contract address
    /// @param fortVaultForStaking IFortVaultForStaking implementation contract address
    /// @param nestPriceFacade INestPriceFacade implementation contract address
    function setBuiltinAddress(
        address dcuToken,
        address fortDAO,
        address fortOptions,
        address fortFutures,
        address fortVaultForStaking,
        address nestPriceFacade
    ) external;

    /// @dev Get the built-in contract address of the system
    /// @return dcuToken Address of dcu token contract
    /// @return fortDAO IFortDAO implementation contract address
    /// @return fortOptions IFortOptions implementation contract address
    /// @return fortFutures IFortFutures implementation contract address
    /// @return fortVaultForStaking IFortVaultForStaking implementation contract address
    /// @return nestPriceFacade INestPriceFacade implementation contract address
    function getBuiltinAddress() external view returns (
        address dcuToken,
        address fortDAO,
        address fortOptions,
        address fortFutures,
        address fortVaultForStaking,
        address nestPriceFacade
    );

    /// @dev Get address of dcu token contract
    /// @return Address of dcu token contract
    function getDCUTokenAddress() external view returns (address);

    /// @dev Get IFortDAO implementation contract address
    /// @return IFortDAO implementation contract address
    function getFortDAOAddress() external view returns (address);

    /// @dev Get IFortOptions implementation contract address
    /// @return IFortOptions implementation contract address
    function getFortOptionsAddress() external view returns (address);

    /// @dev Get IFortFutures implementation contract address
    /// @return IFortFutures implementation contract address
    function getFortFuturesAddress() external view returns (address);

    /// @dev Get IFortVaultForStaking implementation contract address
    /// @return IFortVaultForStaking implementation contract address
    function getFortVaultForStakingAddress() external view returns (address);

    /// @dev Get INestPriceFacade implementation contract address
    /// @return INestPriceFacade implementation contract address
    function getNestPriceFacade() external view returns (address);

    /// @dev Registered address. The address registered here is the address accepted by Fort system
    /// @param key The key
    /// @param addr Destination address. 0 means to delete the registration information
    function registerAddress(string calldata key, address addr) external;

    /// @dev Get registered address
    /// @param key The key
    /// @return Destination address. 0 means empty
    function checkAddress(string calldata key) external view returns (address);
}


// File contracts/interfaces/IFortGovernance.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;
/// @dev This interface defines the governance methods
interface IFortGovernance is IFortMapping {

    /// @dev Governance flag changed event
    /// @param addr Target address
    /// @param oldFlag Old governance flag
    /// @param newFlag New governance flag
    event FlagChanged(address addr, uint oldFlag, uint newFlag);

    /// @dev Set governance authority
    /// @param addr Destination address
    /// @param flag Weight. 0 means to delete the governance permission of the target address. Weight is not 
    ///        implemented in the current system, only the difference between authorized and unauthorized. 
    ///        Here, a uint96 is used to represent the weight, which is only reserved for expansion
    function setGovernance(address addr, uint flag) external;

    /// @dev Get governance rights
    /// @param addr Destination address
    /// @return Weight. 0 means to delete the governance permission of the target address. Weight is not 
    ///        implemented in the current system, only the difference between authorized and unauthorized. 
    ///        Here, a uint96 is used to represent the weight, which is only reserved for expansion
    function getGovernance(address addr) external view returns (uint);

    /// @dev Check whether the target address has governance rights for the given target
    /// @param addr Destination address
    /// @param flag Permission weight. The permission of the target address must be greater than this weight 
    /// to pass the check
    /// @return True indicates permission
    function checkGovernance(address addr, uint flag) external view returns (bool);
}


// File contracts/FortBase.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;
/// @dev Base contract of Fort
contract FortBase {

    /// @dev Governance address changed event
    /// @param oldGovernance Old governance address
    /// @param newGovernance New governance address
    event GovernanceChanged(address oldGovernance, address newGovernance);

    /// @dev IFortGovernance implementation contract address
    address public _governance;

    /// @dev To support open-zeppelin/upgrades
    /// @param governance IFortGovernance implementation contract address
    function initialize(address governance) public virtual {
        require(_governance == address(0), "Fort:!initialize");
        emit GovernanceChanged(address(0), governance);
        _governance = governance;
    }

    /// @dev Rewritten in the implementation contract, for load other contract addresses. Call 
    ///      super.update(newGovernance) when overriding, and override method without onlyGovernance
    /// @param newGovernance IFortGovernance implementation contract address
    function update(address newGovernance) public virtual {

        address governance = _governance;
        require(governance == msg.sender || IFortGovernance(governance).checkGovernance(msg.sender, 0), "Fort:!gov");
        emit GovernanceChanged(governance, newGovernance);
        _governance = newGovernance;
    }

    //---------modifier------------

    modifier onlyGovernance() {
        require(IFortGovernance(_governance).checkGovernance(msg.sender, 0), "Fort:!gov");
        _;
    }
}


// File contracts/custom/FortFrequentlyUsed.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;
// /// @dev Base contract of Fort
// contract FortFrequentlyUsed is FortBase {

//     // Address of DCU contract
//     address constant DCU_TOKEN_ADDRESS = 0xf56c6eCE0C0d6Fbb9A53282C0DF71dBFaFA933eF;

//     // Address of NestOpenPrice contract
//     address constant NEST_OPEN_PRICE = 0x09CE0e021195BA2c1CDE62A8B187abf810951540;
    
//     // USDT base
//     uint constant USDT_BASE = 1 ether;
// }

// TODO:
/// @dev Base contract of Fort
contract FortFrequentlyUsed is FortBase {

    // Address of DCU contract
    //address constant DCU_TOKEN_ADDRESS = ;
    address DCU_TOKEN_ADDRESS;

    // Address of NestPriceFacade contract
    //address constant NEST_OPEN_PRICE = 0xB5D2890c061c321A5B6A4a4254bb1522425BAF0A;
    address NEST_OPEN_PRICE;

    // USDT base
    uint constant USDT_BASE = 1 ether;

    /// @dev Rewritten in the implementation contract, for load other contract addresses. Call 
    ///      super.update(newGovernance) when overriding, and override method without onlyGovernance
    /// @param newGovernance IFortGovernance implementation contract address
    function update(address newGovernance) public override {

        super.update(newGovernance);
        (
            DCU_TOKEN_ADDRESS,//address dcuToken,
            ,//address fortDAO,
            ,//address fortOptions,
            ,//address fortFutures,
            ,//address fortVaultForStaking,
            NEST_OPEN_PRICE //address nestPriceFacade
        ) = IFortGovernance(newGovernance).getBuiltinAddress();
    }
}


// File contracts/interfaces/INestBatchPrice2.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;

/// @dev This contract implemented the mining logic of nest
interface INestBatchPrice2 {

    /// @dev Get the latest trigger price
    /// @param channelId Target channelId
    /// @param pairIndices Array of pair indices
    /// @param payback Address to receive refund
    /// @return prices Price array, i * 2 is the block where the ith price is located, and i * 2 + 1 is the ith price
    function triggeredPrice(
        uint channelId,
        uint[] calldata pairIndices, 
        address payback
    ) external payable returns (uint[] memory prices);

    /// @dev Get the full information of latest trigger price
    /// @param channelId Target channelId
    /// @param pairIndices Array of pair indices
    /// @param payback Address to receive refund
    /// @return prices Price array, i * 4 is the block where the ith price is located, i * 4 + 1 is the ith price,
    /// i * 4 + 2 is the ith average price and i * 4 + 3 is the ith volatility
    function triggeredPriceInfo(
        uint channelId, 
        uint[] calldata pairIndices,
        address payback
    ) external payable returns (uint[] memory prices);

    /// @dev Find the price at block number
    /// @param channelId Target channelId
    /// @param pairIndices Array of pair indices
    /// @param height Destination block number
    /// @param payback Address to receive refund
    /// @return prices Price array, i * 2 is the block where the ith price is located, and i * 2 + 1 is the ith price
    function findPrice(
        uint channelId,
        uint[] calldata pairIndices, 
        uint height, 
        address payback
    ) external payable returns (uint[] memory prices);

    /// @dev Get the last (num) effective price
    /// @param channelId Target channelId
    /// @param pairIndices Array of pair indices
    /// @param count The number of prices that want to return
    /// @param payback Address to receive refund
    /// @return prices Result array, i * count * 2 to (i + 1) * count * 2 - 1 are 
    /// the price results of group i quotation pairs
    function lastPriceList(
        uint channelId, 
        uint[] calldata pairIndices, 
        uint count, 
        address payback
    ) external payable returns (uint[] memory prices);

    /// @dev Returns lastPriceList and triggered price info
    /// @param channelId Target channelId
    /// @param pairIndices Array of pair indices
    /// @param count The number of prices that want to return
    /// @param payback Address to receive refund
    /// @return prices result of group i quotation pair. Among them, the first two count * are the latest prices, 
    /// and the last four are: trigger price block number, trigger price, average price and volatility
    function lastPriceListAndTriggeredPriceInfo(
        uint channelId, 
        uint[] calldata pairIndices,
        uint count, 
        address payback
    ) external payable returns (uint[] memory prices);
}


// File contracts/custom/FortPriceAdapter.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;
/// @dev Base contract of Fort
contract FortPriceAdapter is FortFrequentlyUsed {
    
    // token configuration
    struct TokenConfig {
        // The channelId for call nest price
        uint16 channelId;
        // The pairIndex for call nest price
        uint16 pairIndex;

        // SigmaSQ for token
        uint64 sigmaSQ;
        // MIU_LONG for token
        uint64 miuLong;
        // MIU_SHORT for token
        uint64 miuShort;
    }

    // Post unit: 2000usd
    uint constant POST_UNIT = 2000 * USDT_BASE;

    function _pairIndices(uint pairIndex) private pure returns (uint[] memory pairIndices) {
        pairIndices = new uint[](1);
        pairIndices[0] = pairIndex;
    }

    // Query latest 2 price
    function _lastPriceList(
        TokenConfig memory tokenConfig, 
        uint fee, 
        address payback
    ) internal returns (uint[] memory prices) {
        prices = INestBatchPrice2(NEST_OPEN_PRICE).lastPriceList {
            value: fee
        } (uint(tokenConfig.channelId), _pairIndices(uint(tokenConfig.pairIndex)), 2, payback);

        prices[1] = _toUSDTPrice(prices[1]);
        prices[3] = _toUSDTPrice(prices[3]);
    }

    // Query latest price
    function _latestPrice(
        TokenConfig memory tokenConfig, 
        uint fee, 
        address payback
    ) internal returns (uint oraclePrice) {
        uint[] memory prices = INestBatchPrice2(NEST_OPEN_PRICE).lastPriceList {
            value: fee
        } (uint(tokenConfig.channelId), _pairIndices(uint(tokenConfig.pairIndex)), 1, payback);

        oraclePrice = _toUSDTPrice(prices[1]);
    }

    // Find price by blockNumber
    function _findPrice(
        TokenConfig memory tokenConfig, 
        uint blockNumber, 
        uint fee, 
        address payback
    ) internal returns (uint oraclePrice) {
        uint[] memory prices = INestBatchPrice2(NEST_OPEN_PRICE).findPrice {
            value: fee
        } (uint(tokenConfig.channelId), _pairIndices(uint(tokenConfig.pairIndex)), blockNumber, payback);

        oraclePrice = _toUSDTPrice(prices[1]);
    }

    // Convert to usdt based price
    function _toUSDTPrice(uint rawPrice) internal pure returns (uint) {
        return POST_UNIT * 1 ether / rawPrice;
    }
}


// File @openzeppelin/contracts/token/ERC20/[email protected]

// 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);
}


// File @openzeppelin/contracts/token/ERC20/extensions/[email protected]

// MIT

pragma solidity ^0.8.0;

/**
 * @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);
}


// File @openzeppelin/contracts/utils/[email protected]

// 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;
    }
}


// File @openzeppelin/contracts/token/ERC20/[email protected]

// MIT

pragma solidity ^0.8.0;



/**
 * @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 guidelines: functions revert instead
 * of 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 {}
}


// File contracts/DCU.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;
/// @dev DCU token
contract DCU is FortBase, ERC20("Decentralized Currency Unit", "DCU") {

    /// @dev Mining permission flag change event
    /// @param account Target address
    /// @param oldFlag Old flag
    /// @param newFlag New flag
    event MinterChanged(address account, uint oldFlag, uint newFlag);

    // Flags for account
    mapping(address=>uint) _flags;

    constructor() {
    }

    /// @dev Set mining permission flag
    /// @param account Target address
    /// @param flag Mining permission flag
    function setMinter(address account, uint flag) external onlyGovernance {
        emit MinterChanged(account, _flags[account], flag);
        _flags[account] = flag;
    }

    /// @dev Check mining permission flag
    /// @param account Target address
    /// @return flag Mining permission flag
    function checkMinter(address account) external view returns (uint) {
        return _flags[account];
    }

    /// @dev Mint DCU
    /// @param to Target address
    /// @param value Mint amount
    function mint(address to, uint value) external {
        require(_flags[msg.sender] & 0x01 == 0x01, "DCU:!mint");
        _mint(to, value);
    }

    /// @dev Burn DCU
    /// @param from Target address
    /// @param value Burn amount
    function burn(address from, uint value) external {
        require(_flags[msg.sender] & 0x02 == 0x02, "DCU:!burn");
        _burn(from, value);
    }
}


// File contracts/FortOptions.sol

// GPL-3.0-or-later

pragma solidity ^0.8.6;
/// @dev European option
contract FortOptions is ChainParameter, FortFrequentlyUsed, FortPriceAdapter, IFortOptions {

    /// @dev Option structure
    struct Option {
        uint32 owner;
        uint112 balance;
        uint16 tokenIndex;
        uint56 strikePrice;
        bool orientation;
        uint32 exerciseBlock;
    }

    // token registration information
    struct TokenRegistration {
        TokenConfig tokenConfig;
        address tokenAddress;
    }

    // 64bits 1
    int128 constant ONE = 0x10000000000000000;

    // 64bits 50000
    uint constant V50000 = 0x0C3500000000000000000;

    // Proportion of option selling value, 10000 basis. 9500
    uint constant SELL_RATE = 9500;

    // Option array
    Option[] _options;

    // Registered account address mapping
    mapping(address=>uint) _accountMapping;

    // Registered accounts
    address[] _accounts;

    // token to index mapping
    mapping(address=>uint) _tokenMapping;

    // token registration information array
    TokenRegistration[] _tokenRegistrations;

    constructor() {
    }

    /// @dev To support open-zeppelin/upgrades
    /// @param governance IFortGovernance implementation contract address
    function initialize(address governance) public override {
        super.initialize(governance);
        _accounts.push();
    }

    /// @dev Register token information
    /// @param tokenAddress Target token address, 0 means eth
    /// @param tokenConfig token configuration
    function register(address tokenAddress, TokenConfig calldata tokenConfig) external onlyGovernance {

        // Get index + 1 by tokenAddress
        uint index = _tokenMapping[tokenAddress];
        
        // index == 0 means token not registered, add
        if (index == 0) {
            // Add TokenRegistration to array
            _tokenRegistrations.push(TokenRegistration(tokenConfig, tokenAddress));
            // Record index + 1
            index = _tokenRegistrations.length;
            require(index < 0x10000, "FO:too much tokenRegistrations");
            _tokenMapping[tokenAddress] = index;
        } else {
            _tokenRegistrations[index - 1].tokenConfig = tokenConfig;
        }
    }

    /// @dev Returns the share of the specified option for target address
    /// @param index Index of the option
    /// @param addr Target address
    function balanceOf(uint index, address addr) external view override returns (uint) {
        Option memory option = _options[index];
        if (uint(option.owner) == getAccountIndex(addr)) {
            return uint(option.balance);
        }
        return 0;
    }

    /// @dev Find the options of the target address (in reverse order)
    /// @param start Find forward from the index corresponding to the given contract address 
    /// (excluding the record corresponding to start)
    /// @param count Maximum number of records returned
    /// @param maxFindCount Find records at most
    /// @param owner Target address
    /// @return optionArray Matched option array
    function find(
        uint start, 
        uint count, 
        uint maxFindCount, 
        address owner
    ) external view override returns (OptionView[] memory optionArray) {
        
        optionArray = new OptionView[](count);
        
        // Calculate search region
        Option[] storage options = _options;
        uint i = options.length;
        uint end = 0;
        if (start > 0) {
            i = start;
        }
        if (i > maxFindCount) {
            end = i - maxFindCount;
        }
        
        uint ownerIndex = getAccountIndex(owner);
        // Loop lookup to write qualified records to the buffer
        for (uint index = 0; index < count && i > end;) {
            Option storage option = options[--i];
            if (uint(option.owner) == ownerIndex) {
                optionArray[index++] = _toOptionView(option, i);
            }
        }
    }

    /// @dev List options
    /// @param offset Skip previous (offset) records
    /// @param count Return (count) records
    /// @param order Order. 0 reverse order, non-0 positive order
    /// @return optionArray Matched option array
    function list(
        uint offset, 
        uint count, 
        uint order
    ) external view override returns (OptionView[] memory optionArray) {

        // Load options
        Option[] storage options = _options;
        // Create result array
        optionArray = new OptionView[](count);
        uint length = options.length;
        uint i = 0;

        // Reverse order
        if (order == 0) {
            uint index = length - offset;
            uint end = index > count ? index - count : 0;
            while (index > end) {
                Option storage option = options[--index];
                optionArray[i++] = _toOptionView(option, index);
            }
        } 
        // Positive order
        else {
            uint index = offset;
            uint end = index + count;
            if (end > length) {
                end = length;
            }
            while (index < end) {
                optionArray[i++] = _toOptionView(options[index], index);
                ++index;
            }
        }
    }

    /// @dev Obtain the number of European options that have been opened
    /// @return Number of European options opened
    function getOptionCount() external view override returns (uint) {
        return _options.length;
    }

    /// @dev Open option
    /// @param tokenAddress Target token address, 0 means eth
    /// @param strikePrice The exercise price set by the user. During settlement, the system will compare the 
    /// current price of the subject matter with the exercise price to calculate the user's profit and loss
    /// @param orientation true: call, false: put
    /// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
    /// recorded in the system using the block number
    /// @param dcuAmount Amount of paid DCU
    function open(
        address tokenAddress,
        uint strikePrice,
        bool orientation,
        uint exerciseBlock,
        uint dcuAmount
    ) external payable override {

        // _tokenMapping[tokenAddress] > 0 means token registered
        uint tokenIndex = _tokenMapping[tokenAddress] - 1;
        TokenConfig memory tokenConfig = _tokenRegistrations[tokenIndex].tokenConfig;

        // 1. Query price from oracle
        uint oraclePrice = _latestPrice(tokenConfig, msg.value, msg.sender);

        // 2. Calculate the amount of option
        uint amount = estimate(tokenAddress, oraclePrice, strikePrice, orientation, exerciseBlock, dcuAmount);

        // 3. Open
        // Emit open event
        emit Open(_options.length, dcuAmount, msg.sender, amount);
        // Add option to array
        _options.push(Option(
            //uint32 owner;
            uint32(_addressIndex(msg.sender)),
            //uint112 balance;
            _toUInt112(amount),
            uint16(tokenIndex),
            //uint56 strikePrice;
            _encodeFloat(strikePrice),
            //bool orientation;
            orientation,
            //uint32 exerciseBlock;
            uint32(exerciseBlock)
        ));

        // 4. Burn DCU
        DCU(DCU_TOKEN_ADDRESS).burn(msg.sender, dcuAmount);
    }

    /// @dev Estimate the amount of option
    /// @param tokenAddress Target token address, 0 means eth
    /// @param oraclePrice Current price from oracle
    /// @param strikePrice The exercise price set by the user. During settlement, the system will compare the 
    /// current price of the subject matter with the exercise price to calculate the user's profit and loss
    /// @param orientation true: call, false: put
    /// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
    /// recorded in the system using the block number
    /// @param dcuAmount Amount of paid DCU
    /// @return amount Amount of option
    function estimate(
        address tokenAddress,
        uint oraclePrice,
        uint strikePrice,
        bool orientation,
        uint exerciseBlock,
        uint dcuAmount
    ) public view override returns (uint amount) {
        return _estimate(
            _tokenRegistrations[_tokenMapping[tokenAddress] - 1].tokenConfig,
            oraclePrice,
            strikePrice,
            orientation,
            exerciseBlock,
            dcuAmount
        );
    }
    
    /// @dev Exercise option
    /// @param index Index of option
    /// @param amount Amount of option to exercise
    function exercise(uint index, uint amount) external payable override {

        // 1. Load the option
        Option storage option = _options[index];
        address owner = _accounts[uint(option.owner)];
        uint strikePrice = _decodeFloat(option.strikePrice);
        bool orientation = option.orientation;
        uint exerciseBlock = uint(option.exerciseBlock);

        TokenConfig memory tokenConfig = _tokenRegistrations[option.tokenIndex].tokenConfig;

        // TODO:
        //require(block.number >= exerciseBlock, "FEO:at maturity");

        // 2. Deduct the specified amount
        option.balance = _toUInt112(uint(option.balance) - amount);

        // 3. Find the price by specified block from oracle
        uint oraclePrice = _findPrice(tokenConfig, exerciseBlock, msg.value, msg.sender);

        // 4. Calculate the number of DCU that can be obtained
        uint gain = 0;
        // Call option
        if (orientation) {
            // Win
            if (oraclePrice > strikePrice) {
                gain = amount * (oraclePrice - strikePrice) / USDT_BASE;
            }
        } 
        // Put option
        else {
            // Win
            if (oraclePrice < strikePrice) {
                gain = amount * (strikePrice - oraclePrice) / USDT_BASE;
            }
        }

        // 5. If win, mint DCU to user
        if (gain > 0) {
            DCU(DCU_TOKEN_ADDRESS).mint(owner, gain);
        }

        // emit Exercise event
        emit Exercise(index, amount, owner, gain);
    }

    /// @dev Sell option
    /// @param index Index of option
    /// @param amount Amount of option to sell
    function sell(uint index, uint amount) external payable override {
        // Sell formula: vt=Max(ct(T,K)*0.95, 0). 
        // ct(K,T) Is the price of option present
        // Note: No less than 1% condition

        // 1. Load the option
        Option storage option = _options[index];
        address owner = _accounts[uint(option.owner)];
        require(owner == msg.sender, "FO:not owner");
        uint strikePrice = _decodeFloat(option.strikePrice);
        bool orientation = option.orientation;
        uint exerciseBlock = uint(option.exerciseBlock);

        TokenConfig memory tokenConfig = _tokenRegistrations[option.tokenIndex].tokenConfig;

        // 2. Deduct the specified amount
        option.balance = _toUInt112(uint(option.balance) - amount);

        // 3. Query price from oracle
        uint oraclePrice = _latestPrice(tokenConfig, msg.value, msg.sender);

        // 4. Calculate option price
        uint dcuAmount = amount * _calcV(
            tokenConfig, 
            oraclePrice,
            strikePrice,
            orientation,
            exerciseBlock
        ) * SELL_RATE / (USDT_BASE * 0x27100000000000000000); 
        //(USDT_BASE * 10000 << 64);

        if (dcuAmount > 0) {
            DCU(DCU_TOKEN_ADDRESS).mint(msg.sender, dcuAmount);
        }

        // emit Sell event
        emit Sell(index, amount, msg.sender, dcuAmount);
    }

    /// @dev Calculate option price
    /// @param oraclePrice Current price from oracle
    /// @param strikePrice The exercise price set by the user. During settlement, the system will compare the 
    /// current price of the subject matter with the exercise price to calculate the user's profit and loss
    /// @param orientation true: call, false: put
    /// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
    /// recorded in the system using the block number
    /// @return v Option price. Need to divide (USDT_BASE << 64)
    function calcV(
        address tokenAddress,
        uint oraclePrice,
        uint strikePrice,
        bool orientation,
        uint exerciseBlock
    ) public view override returns (uint v) {
        return _calcV(
            _tokenRegistrations[_tokenMapping[tokenAddress] - 1].tokenConfig,
            oraclePrice,
            strikePrice,
            orientation,
            exerciseBlock
        );
    }

    /// @dev Gets the address corresponding to the given index number
    /// @param index The index number of the specified address
    /// @return The address corresponding to the given index number
    function indexAddress(uint index) public view returns (address) {
        return _accounts[index];
    }

    /// @dev Gets the registration index number of the specified address
    /// @param addr Destination address
    /// @return 0 means nonexistent, non-0 means index number
    function getAccountIndex(address addr) public view returns (uint) {
        return _accountMapping[addr];
    }

    /// @dev Get the length of registered account array
    /// @return The length of registered account array
    function getAccountCount() external view returns (uint) {
        return _accounts.length;
    }

    // Convert to OptionView
    function _toOptionView(
        Option storage option, 
        uint index
    ) private view returns (OptionView memory) {
        return OptionView(
            index,
            _tokenRegistrations[option.tokenIndex].tokenAddress,
            _decodeFloat(option.strikePrice),
            option.orientation,
            uint(option.exerciseBlock),
            option.balance,
            _accounts[uint(option.owner)]
        );
    }

    // Convert 18 decimal points to 64 binary points
    function _d18TOb64(uint v) private pure returns (int128) {
        require(v < 0x6F05B59D3B200000000000000000000, "FEO:can't convert to 64bits");
        return int128(int((v << 64) / 1 ether));
    }

    // Convert uint to int128
    function _toInt128(uint v) private pure returns (int128) {
        require(v < 0x80000000000000000000000000000000, "FEO:can't convert to int128");
        return int128(int(v));
    }

    // Convert int128 to uint
    function _toUInt(int128 v) private pure returns (uint) {
        require(v >= 0, "FEO:can't convert to uint");
        return uint(int(v));
    }

    // Convert uint to uint112
    function _toUInt112(uint v) private pure returns (uint112) {
        require(v < 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF,"HO:can't convert to uint112");
        return uint112(v);
    }

    // Calculate standard normal distribution by table
    function _snd(int128 x) private pure returns (int128) {
        uint[28] memory table = [
            /* */ ///////////////////// STANDARD NORMAL TABLE //////////////////////////
            /* */ 0x174A15BF143412A8111C0F8F0E020C740AE6095807CA063B04AD031E018F0000, //
            ///// 0x2F8C2E0F2C912B1229922811268F250B23872202207D1EF61D6F1BE61A5D18D8, //
            /* */ 0x2F8C2E0F2C912B1229922811268F250B23872202207D1EF61D6F1BE61A5D18D4, //
            /* */ 0x46A2453C43D4426B41003F943E263CB63B4539D3385F36EA357333FB32823108, //
            /* */ 0x5C0D5AC5597B582F56E05590543E52EA5194503C4EE24D874C294ACA49694807, //
            /* */ 0x6F6C6E466D1F6BF56AC9699B686A6738660364CC6392625761195FD95E975D53, //
            /* */ 0x807E7F7F7E7D7D797C737B6A7A5F79517841772F761A750373E972CD71AF708E, //
            /* */ 0x8F2A8E518D768C998BB98AD789F2890B88218736864785568463836E8276817B, //
            /* */ 0x9B749AC19A0B9953989997DD971E965D959A94D4940C9342927591A690D49000, //
            ///// 0xA57CA4ECA459A3C4A32EA295A1FAA15CA0BDA01C9F789ED29E2A9D809CD39C25, //
            /* */ 0xA57CA4ECA459A3C4A32EA295A1FAA15DA0BDA01C9F789ED29E2A9D809CD39C25, //
            /* */ 0xAD78AD07AC93AC1EABA7AB2EAAB3AA36A9B8A937A8B5A830A7AAA721A697A60B, //
            /* */ 0xB3AAB353B2FAB2A0B245B1E7B189B128B0C6B062AFFDAF96AF2DAEC2AE56ADE8, //
            /* */ 0xB859B818B7D6B793B74EB708B6C0B678B62EB5E2B595B547B4F7B4A6B454B400, //
            /* */ 0xBBCDBB9EBB6EBB3CBB0ABAD7BAA2BA6DBA36B9FFB9C6B98CB951B915B8D8B899, //
            /* */ 0xBE49BE27BE05BDE2BDBEBD99BD74BD4DBD26BCFEBCD5BCACBC81BC56BC29BBFC, //
            /* */ 0xC006BFEEBFD7BFBEBFA5BF8CBF72BF57BF3CBF20BF03BEE6BEC8BEA9BE8ABE69, //
            /* */ 0xC135C126C116C105C0F4C0E3C0D1C0BFC0ACC099C086C072C05DC048C032C01C, //
            /* */ 0xC200C1F5C1EBC1E0C1D5C1C9C1BEC1B1C1A5C198C18BC17EC170C162C154C145, //
            /* */ 0xC283C27CC275C26EC267C260C258C250C248C240C238C22FC226C21DC213C20A, //
            /* */ 0xC2D6C2D2C2CDC2C9C2C5C2C0C2BBC2B6C2B1C2ACC2A7C2A1C29BC295C28FC289, //
            /* */ 0xC309C306C304C301C2FEC2FCC2F9C2F6C2F2C2EFC2ECC2E8C2E5C2E1C2DEC2DA, //
            /* */ 0xC328C326C325C323C321C320C31EC31CC31AC318C316C314C312C310C30EC30B, //
            /* */ 0xC33AC339C338C337C336C335C334C333C332C331C330C32EC32DC32CC32AC329, //
            /* */ 0xC344C343C343C342C342C341C341C340C33FC33FC33EC33DC33DC33CC33BC33A, //
            /* */ 0xC34AC349C349C349C348C348C348C348C347C347C346C346C346C345C345C344, //
            /* */ 0xC34DC34DC34CC34CC34CC34CC34CC34CC34BC34BC34BC34BC34BC34AC34AC34A, //
            /* */ 0xC34EC34EC34EC34EC34EC34EC34EC34EC34EC34EC34DC34DC34DC34DC34DC34D, //
            /* */ 0xC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34FC34EC34E, //
            /* */ 0xC350C350C350C350C350C350C34FC34FC34FC34FC34FC34FC34FC34FC34FC34F  //
            /* */ //////////////////// MADE IN CHINA 2021-08-24 ////////////////////////
        ];

        uint ux = uint(int(x < 0 ? -x : x)) * 100;
        uint i = ux >> 64;
        uint v = V50000;

        if (i < 447) {
            v = uint((table[i >> 4] >> ((i & 0xF) << 4)) & 0xFFFF) << 64;
            v = (
                    (
                        (
                            (uint((table[(i + 1) >> 4] >> (((i + 1) & 0xF) << 4)) & 0xFFFF) << 64)
                            - v
                        ) * (ux & 0xFFFFFFFFFFFFFFFF) //(ux - (i << 64))
                    ) >> 64
                ) + v;
        }

        if (x > 0) {
            v = V50000 + v;
        } else {
            v = V50000 - v;
        }

        return int128(int(v / 100000));
    }

    /// @dev Estimate the amount of option
    /// @param oraclePrice Current price from oracle
    /// @param strikePrice The exercise price set by the user. During settlement, the system will compare the 
    /// current price of the subject matter with the exercise price to calculate the user's profit and loss
    /// @param orientation true: call, false: put
    /// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
    /// recorded in the system using the block number
    /// @param dcuAmount Amount of paid DCU
    /// @return amount Amount of option
    function _estimate(
        TokenConfig memory tokenConfig,
        uint oraclePrice,
        uint strikePrice,
        bool orientation,
        uint exerciseBlock,
        uint dcuAmount
    ) private view returns (uint amount) {

        require(exerciseBlock > block.number + MIN_PERIOD, "FEO:exerciseBlock too small");

        // 1. Calculate option price
        uint v = _calcV(
            tokenConfig, 
            oraclePrice,
            strikePrice,
            orientation,
            exerciseBlock
        );

        // 2. Correct option price
        if (orientation) {
            //v = _calcVc(config, oraclePrice, T, strikePrice);
            // Vc>=S0*1%; Vp>=K*1%
            // require(v * 100 >> 64 >= oraclePrice, "FEO:vc must greater than S0*1%");
            if (v * 100 >> 64 < oraclePrice) {
                v = oraclePrice * 0x10000000000000000 / 100;
            }
        } else {
            //v = _calcVp(config, oraclePrice, T, strikePrice);
            // Vc>=S0*1%; Vp>=K*1%
            // require(v * 100 >> 64 >= strikePrice, "FEO:vp must greater than K*1%");
            if (v * 100 >> 64 < strikePrice) {
                v = strikePrice * 0x10000000000000000 / 100;
            }
        }

        // 3. Calculate the amount of option
        amount = (USDT_BASE << 64) * dcuAmount / v;
    }

    /// @dev Calculate option price
    /// @param oraclePrice Current price from oracle
    /// @param strikePrice The exercise price set by the user. During settlement, the system will compare the 
    /// current price of the subject matter with the exercise price to calculate the user's profit and loss
    /// @param orientation true: call, false: put
    /// @param exerciseBlock After reaching this block, the user will exercise manually, and the block will be
    /// recorded in the system using the block number
    /// @return v Option price. Need to divide (USDT_BASE << 64)
    function _calcV(
        TokenConfig memory tokenConfig,
        uint oraclePrice,
        uint strikePrice,
        bool orientation,
        uint exerciseBlock
    ) private view returns (uint v) {

        // Convert the total time according to the average block out time
        uint T = (exerciseBlock - block.number) * BLOCK_TIME;
        v = orientation 
            ? _calcVc(tokenConfig, oraclePrice, T, strikePrice) 
            : _calcVp(tokenConfig, oraclePrice, T, strikePrice);
    }

    // Calculate option price for call
    function _calcVc(TokenConfig memory tokenConfig, uint S0, uint T, uint K) private pure returns (uint vc) {

        int128 sigmaSQ_T = _d18TOb64(uint(tokenConfig.sigmaSQ) * T);
        int128 miu_T = _toInt128(uint(tokenConfig.miuLong) * T);
        int128 sigma_t = ABDKMath64x64.sqrt(sigmaSQ_T);
        int128 D1 = _D1(S0, K, sigmaSQ_T, miu_T);
        int128 d = ABDKMath64x64.div(D1, sigma_t);

        uint left = _toUInt(ABDKMath64x64.mul(
            //ABDKMath64x64.exp(miu_T), 
            // Use approximate calculation method: x*(1+rt)
            // by chenf 2021-12-28 15:27
            miu_T + ONE,
            ABDKMath64x64.sub(
                ONE,
                _snd(ABDKMath64x64.sub(d, sigma_t))
            )
        )) * S0;
        uint right = _toUInt(ABDKMath64x64.sub(ONE, _snd(d))) * K;
        
        vc = left > right ? left - right : 0;
    }

    // Calculate option price for put
    function _calcVp(TokenConfig memory tokenConfig, uint S0, uint T, uint K) private pure returns (uint vp) {

        int128 sigmaSQ_T = _d18TOb64(uint(tokenConfig.sigmaSQ) * T);
        int128 miu_T = _toInt128(uint(tokenConfig.miuShort) * T);
        int128 sigma_t = ABDKMath64x64.sqrt(sigmaSQ_T);
        int128 D1 = _D1(S0, K, sigmaSQ_T, miu_T);
        int128 d = ABDKMath64x64.div(D1, sigma_t);

        uint left = _toUInt(_snd(d)) * K;
        uint right = _toUInt(ABDKMath64x64.mul(
            //ABDKMath64x64.exp(miu_T), 
            // Use approximate calculation method: x*(1+rt)
            // by chenf 2021-12-28 15:27
            miu_T + ONE,
            _snd(ABDKMath64x64.sub(d, sigma_t))
        )) * S0;

        vp = left > right ? left - right : 0;
    }

    // d1 in formula, Because didn't divide by σ, So it's named D1
    function _D1(uint S0, uint K, int128 sigmaSQ_T, int128 miu_T) private pure returns (int128) {

        //require(K < 0x1000000000000000000000000000000000000000000000000, "FEO:K can't ROL 64bits");
        return
            ABDKMath64x64.sub(
                ABDKMath64x64.add(
                    ABDKMath64x64.ln(_toInt128(K * 0x10000000000000000 / S0)),
                    sigmaSQ_T >> 1
                ),
                miu_T
            );
    }
    
    /// @dev Encode the uint value as a floating-point representation in the form of fraction * 16 ^ exponent
    /// @param value Destination uint value
    /// @return float format
    function _encodeFloat(uint value) private pure returns (uint56) {

        uint exponent = 0; 
        while (value > 0x3FFFFFFFFFFFF) {
            value >>= 4;
            ++exponent;
        }
        return uint56((value << 6) | exponent);
    }

    /// @dev Decode the floating-point representation of fraction * 16 ^ exponent to uint
    /// @param floatValue fraction value
    /// @return decode format
    function _decodeFloat(uint56 floatValue) private pure returns (uint) {
        return (uint(floatValue) >> 6) << ((uint(floatValue) & 0x3F) << 2);
    }
    
    /// @dev Gets the index number of the specified address. If it does not exist, register
    /// @param addr Destination address
    /// @return The index number of the specified address
    function _addressIndex(address addr) private returns (uint) {

        uint index = _accountMapping[addr];
        if (index == 0) {
            // If it exceeds the maximum number that 32 bits can store, you can't continue to register a new account.
            // If you need to support a new account, you need to update the contract
            require((_accountMapping[addr] = index = _accounts.length) < 0x100000000, "HO:!accounts");
            _accounts.push(addr);
        }

        return index;
    }
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"Exercise","inputs":[{"type":"uint256","name":"index","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"address","name":"owner","internalType":"address","indexed":false},{"type":"uint256","name":"gain","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"GovernanceChanged","inputs":[{"type":"address","name":"oldGovernance","internalType":"address","indexed":false},{"type":"address","name":"newGovernance","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"Open","inputs":[{"type":"uint256","name":"index","internalType":"uint256","indexed":false},{"type":"uint256","name":"dcuAmount","internalType":"uint256","indexed":false},{"type":"address","name":"owner","internalType":"address","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Sell","inputs":[{"type":"uint256","name":"index","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"address","name":"owner","internalType":"address","indexed":false},{"type":"uint256","name":"dcuAmount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"_governance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"v","internalType":"uint256"}],"name":"calcV","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"oraclePrice","internalType":"uint256"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"}],"name":"estimate","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"oraclePrice","internalType":"uint256"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"},{"type":"uint256","name":"dcuAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"exercise","inputs":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"optionArray","internalType":"struct IFortOptions.OptionView[]","components":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"},{"type":"uint256","name":"balance","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]}],"name":"find","inputs":[{"type":"uint256","name":"start","internalType":"uint256"},{"type":"uint256","name":"count","internalType":"uint256"},{"type":"uint256","name":"maxFindCount","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAccountCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getAccountIndex","inputs":[{"type":"address","name":"addr","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getOptionCount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"indexAddress","inputs":[{"type":"uint256","name":"index","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[{"type":"address","name":"governance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple[]","name":"optionArray","internalType":"struct IFortOptions.OptionView[]","components":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"},{"type":"uint256","name":"balance","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"}]}],"name":"list","inputs":[{"type":"uint256","name":"offset","internalType":"uint256"},{"type":"uint256","name":"count","internalType":"uint256"},{"type":"uint256","name":"order","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"open","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"uint256","name":"strikePrice","internalType":"uint256"},{"type":"bool","name":"orientation","internalType":"bool"},{"type":"uint256","name":"exerciseBlock","internalType":"uint256"},{"type":"uint256","name":"dcuAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"register","inputs":[{"type":"address","name":"tokenAddress","internalType":"address"},{"type":"tuple","name":"tokenConfig","internalType":"struct FortPriceAdapter.TokenConfig","components":[{"type":"uint16","name":"channelId","internalType":"uint16"},{"type":"uint16","name":"pairIndex","internalType":"uint16"},{"type":"uint64","name":"sigmaSQ","internalType":"uint64"},{"type":"uint64","name":"miuLong","internalType":"uint64"},{"type":"uint64","name":"miuShort","internalType":"uint64"}]}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"sell","inputs":[{"type":"uint256","name":"index","internalType":"uint256"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"update","inputs":[{"type":"address","name":"newGovernance","internalType":"address"}]}]
              

Contract Creation Code

0x608060405234801561001057600080fd5b50613b7d806100206000396000f3fe6080604052600436106100f35760003560e01c80639ba63e9e1161008a578063c4d66de811610059578063c4d66de8146102cc578063d79875eb146102ec578063e983b85c146102ff578063ee1ca9601461031f57600080fd5b80639ba63e9e1461024c578063a6bd54271461028f578063a86265bb146102a4578063a98e4e77146102b757600080fd5b80632fee0638116100c65780632fee0638146101cc5780633656eec2146101ec57806375e5da181461020c578063860506801461022c57600080fd5b806301b5ff8f146100f857806319aeb4901461012b5780631c1b8772146101585780631c2f3e3d1461017a575b600080fd5b34801561010457600080fd5b50610118610113366004613179565b610332565b6040519081526020015b60405180910390f35b34801561013757600080fd5b5061014b6101463660046131cb565b610414565b60405161012291906131f7565b34801561016457600080fd5b50610178610173366004613294565b610609565b005b34801561018657600080fd5b506000546101a79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610122565b3480156101d857600080fd5b506101786101e73660046132b1565b6106dd565b3480156101f857600080fd5b50610118610207366004613310565b610a85565b34801561021857600080fd5b50610118610227366004613340565b610bc7565b34801561023857600080fd5b506101a761024736600461339c565b610cab565b34801561025857600080fd5b50610118610267366004613294565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b34801561029b57600080fd5b50600354610118565b6101786102b23660046133b5565b610ce8565b3480156102c357600080fd5b50600554610118565b3480156102d857600080fd5b506101786102e7366004613294565b611076565b6101786102fa3660046133b5565b61108e565b34801561030b57600080fd5b5061014b61031a3660046133d7565b61142d565b61017861032d366004613418565b6115ce565b73ffffffffffffffffffffffffffffffffffffffff851660009081526006602052604081205461040a9060079061036b90600190613499565b8154811061037b5761037b6134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff64010000000084048116918301919091526c010000000000000000000000008304811660608301527401000000000000000000000000000000000000000090920490911660808201528686868661199e565b9695505050505050565b606060038367ffffffffffffffff811115610431576104316134df565b6040519080825280602002602001820160405280156104d057816020015b6104bd6040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b81526020019060019003908161044f5790505b508154909250600084810361057c5760006104eb8884613499565b905060008782116104fd576000610507565b6105078883613499565b90505b808211156105755760008561051e8461350e565b93508381548110610531576105316134b0565b90600052602060002001905061054781846119db565b878561055281613543565b965081518110610564576105646134b0565b60200260200101819052505061050a565b50506105ff565b866000610589888361357b565b9050838111156105965750825b808210156105fc576105c38583815481106105b3576105b36134b0565b90600052602060002001836119db565b86846105ce81613543565b9550815181106105e0576105e06134b0565b6020026020010181905250816105f590613543565b9150610596565b50505b5050509392505050565b61061281611bab565b8073ffffffffffffffffffffffffffffffffffffffff1663746b56f96040518163ffffffff1660e01b815260040160c060405180830381865afa15801561065d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106819190613593565b6002805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560018054979092169616959095179094555050505050565b600080546040517f91e1472b000000000000000000000000000000000000000000000000000000008152336004820152602481019290925273ffffffffffffffffffffffffffffffffffffffff16906391e1472b90604401602060405180830381865afa158015610752573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610776919061361a565b6107e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f466f72743a21676f76000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081526006602052604081205490819003610a4657600760405180604001604052808480360381019061082d91906136ac565b815273ffffffffffffffffffffffffffffffffffffffff8681166020928301528354600180820186556000958652948390208451805160029093029091018054828601516040840151606085015160809095015161ffff9687167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009094169390931762010000969092168602919091177fffffffffffffffffffffffff00000000000000000000000000000000ffffffff1664010000000067ffffffffffffffff928316027fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff16176c0100000000000000000000000094821694909402939093177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000939091169290920291909117815593909201519290930180547fffffffffffffffffffffffff000000000000000000000000000000000000000016929093169190911790915560075491508110610a19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f464f3a746f6f206d75636820746f6b656e526567697374726174696f6e73000060448201526064016107d8565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600660205260409020819055505050565b816007610a54600184613499565b81548110610a6457610a646134b0565b60009182526020909120600290910201610a7e8282613751565b5050505050565b60008060038481548110610a9b57610a9b6134b0565b600091825260208083206040805160c081018252939091015463ffffffff808216855264010000000082046dffffffffffffffffffffffffffff16858501527201000000000000000000000000000000000000820461ffff168584015274010000000000000000000000000000000000000000820466ffffffffffffff1660608601527b01000000000000000000000000000000000000000000000000000000820460ff16151560808601527c01000000000000000000000000000000000000000000000000000000009091041660a084015273ffffffffffffffffffffffffffffffffffffffff87168452600490915290912054909150815163ffffffff1603610bbb57602001516dffffffffffffffffffffffffffff169050610bc1565b60009150505b92915050565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260066020526040812054610ca090600790610c0090600190613499565b81548110610c1057610c106134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff64010000000084048116918301919091526c010000000000000000000000008304811660608301527401000000000000000000000000000000000000000090920490911660808201528787878787611d60565b979650505050505050565b600060058281548110610cc057610cc06134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1692915050565b600060038381548110610cfd57610cfd6134b0565b600091825260208220018054600580549294509163ffffffff909116908110610d2857610d286134b0565b6000918252602082200154835473ffffffffffffffffffffffffffffffffffffffff9091169250740100000000000000000000000000000000000000009004600681901c6603ffffffffffff1660029190911b60fc161b8354600780549293507b01000000000000000000000000000000000000000000000000000000820460ff16927c0100000000000000000000000000000000000000000000000000000000830463ffffffff169260009291720100000000000000000000000000000000000090910461ffff16908110610e0057610e006134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff6401000000008085048216928401929092526c010000000000000000000000008404811660608401527401000000000000000000000000000000000000000090930490921660808201528754909250610eb291610ead918a916dffffffffffffffffffffffffffff910416613499565b611e92565b86546dffffffffffffffffffffffffffff91909116640100000000027fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161786556000610f0382843433611f0f565b905060008415610f445785821115610f3f57670de0b6b3a7640000610f288784613499565b610f32908b6138d7565b610f3c9190613943565b90505b610f76565b85821015610f7657670de0b6b3a7640000610f5f8388613499565b610f69908b6138d7565b610f739190613943565b90505b8015611009576001546040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff898116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b158015610ff057600080fd5b505af1158015611004573d6000803e3d6000fd5b505050505b604080518b8152602081018b905273ffffffffffffffffffffffffffffffffffffffff891691810191909152606081018290527f72db4df7c54a4e3502d596213c9116f06a327af3a139e03e5fb36fa5a09ecb9e906080015b60405180910390a150505050505050505050565b61107f81611ffb565b50600580546001018155600052565b6000600383815481106110a3576110a36134b0565b600091825260208220018054600580549294509163ffffffff9091169081106110ce576110ce6134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905033811461115b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f464f3a6e6f74206f776e6572000000000000000000000000000000000000000060448201526064016107d8565b815460078054740100000000000000000000000000000000000000008304600681901c6603ffffffffffff1660029190911b60fc161b927b01000000000000000000000000000000000000000000000000000000810460ff16927c0100000000000000000000000000000000000000000000000000000000820463ffffffff16926000927201000000000000000000000000000000000000900461ffff16908110611208576112086134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff6401000000008085048216928401929092526c0100000000000000000000000084048116606084015274010000000000000000000000000000000000000000909304909216608082015287549092506112b591610ead918a916dffffffffffffffffffffffffffff910416613499565b86546dffffffffffffffffffffffffffff91909116640100000000027fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161786556000611305823433612111565b90506000611325670de0b6b3a764000069271000000000000000006138d7565b61251c61133585858a8a8a61199e565b61133f908c6138d7565b61134991906138d7565b6113539190613943565b905080156113e6576001546040517f40c10f190000000000000000000000000000000000000000000000000000000081523360048201526024810183905273ffffffffffffffffffffffffffffffffffffffff909116906340c10f1990604401600060405180830381600087803b1580156113cd57600080fd5b505af11580156113e1573d6000803e3d6000fd5b505050505b604080518b8152602081018b90523391810191909152606081018290527fb69e24112cb4483e933dc3bda2d474e8d511e1d7058a983fe98a7d5d78fb974390608001611062565b60608367ffffffffffffffff811115611448576114486134df565b6040519080825280602002602001820160405280156114e757816020015b6114d46040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b8152602001906001900390816114665790505b506003805491925090600087156114fc578791505b858211156115115761150e8683613499565b90505b73ffffffffffffffffffffffffffffffffffffffff8516600090815260046020526040812054905b888110801561154757508284115b156115c1576000856115588661350e565b9550858154811061156b5761156b6134b0565b6000918252602090912001805490915063ffffffff168390036115bb5761159281866119db565b878361159d81613543565b9450815181106115af576115af6134b0565b60200260200101819052505b50611539565b5050505050949350505050565b73ffffffffffffffffffffffffffffffffffffffff851660009081526006602052604081205461160090600190613499565b9050600060078281548110611617576116176134b0565b600091825260208083206040805160a08101825260029094029091015461ffff8082168552620100008204169284019290925267ffffffffffffffff64010000000083048116918401919091526c010000000000000000000000008204811660608401527401000000000000000000000000000000000000000090910416608082015291506116a7823433612111565b905060006116b989838a8a8a8a610bc7565b60035460408051918252602082018890523390820152606081018290529091507f39b02417d6826eadf5421ac690db0dd00065bb988a2f8cd32341c68d74d36beb9060800160405180910390a160036040518060c0016040528061171c336121fa565b63ffffffff16815260200161173084611e92565b6dffffffffffffffffffffffffffff1681526020018661ffff1681526020016117588b612337565b66ffffffffffffff90811682528a151560208084019190915263ffffffff8b81166040948501528554600180820188556000978852968390208651910180549387015187870151606089015160808a015160a0909a015186167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9a15157b01000000000000000000000000000000000000000000000000000000029a909a167affffffffffffffffffffffffffffffffffffffffffffffffffffff9190981674010000000000000000000000000000000000000000027fffffffffff00000000000000ffffffffffffffffffffffffffffffffffffffff61ffff909316720100000000000000000000000000000000000002929092167fffffffffff000000000000000000ffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff909416640100000000027fffffffffffffffffffffffffffff000000000000000000000000000000000000909816959096169490941795909517169290921792909217919091169190911792909217909155905490517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201526024810187905273ffffffffffffffffffffffffffffffffffffffff90911690639dc29fac90604401600060405180830381600087803b15801561197b57600080fd5b505af115801561198f573d6000803e3d6000fd5b50505050505050505050505050565b60008060036119ad4385613499565b6119b791906138d7565b9050836119cf576119ca8787838861236f565b610ca0565b610ca08787838861246e565b611a496040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6040805160e0810190915282815283546007805460208401927201000000000000000000000000000000000000900461ffff16908110611a8b57611a8b6134b0565b60009182526020918290206002918202016001015473ffffffffffffffffffffffffffffffffffffffff16835286549290910191740100000000000000000000000000000000000000009004600681901c6603ffffffffffff16911b60fc161b8152845460ff7b010000000000000000000000000000000000000000000000000000008204161515602083015263ffffffff7c01000000000000000000000000000000000000000000000000000000008204811660408401526dffffffffffffffffffffffffffff640100000000830416606084015260058054608090940193909291909116908110611b8057611b806134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1690529392505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633811480611c6257506040517f91e1472b0000000000000000000000000000000000000000000000000000000081523360048201526000602482015273ffffffffffffffffffffffffffffffffffffffff8216906391e1472b90604401602060405180830381865afa158015611c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c62919061361a565b611cc8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f466f72743a21676f76000000000000000000000000000000000000000000000060448201526064016107d8565b6040805173ffffffffffffffffffffffffffffffffffffffff8084168252841660208201527f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd2910160405180910390a150600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000611d6d600a4361357b565b8311611dd5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a6578657263697365426c6f636b20746f6f20736d616c6c000000000060448201526064016107d8565b6000611de4888888888861199e565b90508415611e2957866040611dfa8360646138d7565b901c1015611e24576064611e1788680100000000000000006138d7565b611e219190613943565b90505b611e61565b856040611e378360646138d7565b901c1015611e61576064611e5487680100000000000000006138d7565b611e5e9190613943565b90505b80611e7c846f0de0b6b3a764000000000000000000006138d7565b611e869190613943565b98975050505050505050565b60006dffffffffffffffffffffffffffff8210611f0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f484f3a63616e277420636f6e7665727420746f2075696e74313132000000000060448201526064016107d8565b5090565b60025484516020860151600092839273ffffffffffffffffffffffffffffffffffffffff9091169163594e25b591879161ffff91821691611f50911661253f565b89886040518663ffffffff1660e01b8152600401611f7194939291906139b9565b60006040518083038185885af1158015611f8f573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611fd691908101906139ff565b905061040a81600181518110611fee57611fee6134b0565b6020026020010151612586565b60005473ffffffffffffffffffffffffffffffffffffffff161561207b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f466f72743a21696e697469616c697a650000000000000000000000000000000060448201526064016107d8565b604080516000815273ffffffffffffffffffffffffffffffffffffffff831660208201527f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd2910160405180910390a1600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60025483516020850151600092839273ffffffffffffffffffffffffffffffffffffffff909116916354a80f0491879161ffff91821691612152911661253f565b6001886040518663ffffffff1660e01b815260040161217494939291906139b9565b60006040518083038185885af1158015612192573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526121d991908101906139ff565b90506121f181600181518110611fee57611fee6134b0565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260046020526040812054808203610bc1575060055473ffffffffffffffffffffffffffffffffffffffff8316600090815260046020526040902081905564010000000081106122c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f484f3a216163636f756e7473000000000000000000000000000000000000000060448201526064016107d8565b600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851617905592915050565b6000805b6603ffffffffffff8311156123615760049290921c9161235a81613543565b905061233b565b60069290921b909117919050565b60008061239484876040015167ffffffffffffffff1661238f91906138d7565b6125b9565b905060006123ba85886080015167ffffffffffffffff166123b591906138d7565b61264a565b905060006123c7836126c5565b905060006123d7888786866126e7565b905060006123e5828461272b565b90506000876123fb6123f6846127ab565b612d12565b61240591906138d7565b905060008a61243a6123f6612423680100000000000000008a613a99565b612435612430888b612d88565b6127ab565b612ddc565b61244491906138d7565b905080821161245457600061245e565b61245e8183613499565b9c9b505050505050505050505050565b60008061248e84876040015167ffffffffffffffff1661238f91906138d7565b905060006124af85886060015167ffffffffffffffff166123b591906138d7565b905060006124bc836126c5565b905060006124cc888786866126e7565b905060006124da828461272b565b90506000896125176123f66124f86801000000000000000089613a99565b61243568010000000000000000612512612430898c612d88565b612d88565b61252191906138d7565b905060008861243a6123f668010000000000000000612512876127ab565b604080516001808252818301909252606091602080830190803683370190505090508181600081518110612575576125756134b0565b602002602001018181525050919050565b60008161259d670de0b6b3a76400006107d06138d7565b6125af90670de0b6b3a76400006138d7565b610bc19190613943565b60006f06f05b59d3b2000000000000000000008210612634576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a63616e277420636f6e7665727420746f20363462697473000000000060448201526064016107d8565b610bc1670de0b6b3a7640000604084901b613943565b60006f800000000000000000000000000000008210611f0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a63616e277420636f6e7665727420746f20696e74313238000000000060448201526064016107d8565b60008082600f0b12156126d757600080fd5b610bc1604083600f0b901b612e33565b60006121f16127256127186127138861270989680100000000000000006138d7565b6123b59190613943565b612fb6565b600186600f0b901d612ff0565b83612d88565b600081600f0b60000361273d57600080fd5b600082600f0b604085600f0b901b8161275857612758613914565b0590507fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113155b6127a457600080fd5b9392505050565b6000806040518061038001604052807f174a15bf143412a8111c0f8f0e020c740ae6095807ca063b04ad031e018f000081526020017f2f8c2e0f2c912b1229922811268f250b23872202207d1ef61d6f1be61a5d18d481526020017f46a2453c43d4426b41003f943e263cb63b4539d3385f36ea357333fb3282310881526020017f5c0d5ac5597b582f56e05590543e52ea5194503c4ee24d874c294aca4969480781526020017f6f6c6e466d1f6bf56ac9699b686a6738660364cc6392625761195fd95e975d5381526020017f807e7f7f7e7d7d797c737b6a7a5f79517841772f761a750373e972cd71af708e81526020017f8f2a8e518d768c998bb98ad789f2890b88218736864785568463836e8276817b81526020017f9b749ac19a0b9953989997dd971e965d959a94d4940c9342927591a690d4900081526020017fa57ca4eca459a3c4a32ea295a1faa15da0bda01c9f789ed29e2a9d809cd39c2581526020017fad78ad07ac93ac1eaba7ab2eaab3aa36a9b8a937a8b5a830a7aaa721a697a60b81526020017fb3aab353b2fab2a0b245b1e7b189b128b0c6b062affdaf96af2daec2ae56ade881526020017fb859b818b7d6b793b74eb708b6c0b678b62eb5e2b595b547b4f7b4a6b454b40081526020017fbbcdbb9ebb6ebb3cbb0abad7baa2ba6dba36b9ffb9c6b98cb951b915b8d8b89981526020017fbe49be27be05bde2bdbebd99bd74bd4dbd26bcfebcd5bcacbc81bc56bc29bbfc81526020017fc006bfeebfd7bfbebfa5bf8cbf72bf57bf3cbf20bf03bee6bec8bea9be8abe6981526020017fc135c126c116c105c0f4c0e3c0d1c0bfc0acc099c086c072c05dc048c032c01c81526020017fc200c1f5c1ebc1e0c1d5c1c9c1bec1b1c1a5c198c18bc17ec170c162c154c14581526020017fc283c27cc275c26ec267c260c258c250c248c240c238c22fc226c21dc213c20a81526020017fc2d6c2d2c2cdc2c9c2c5c2c0c2bbc2b6c2b1c2acc2a7c2a1c29bc295c28fc28981526020017fc309c306c304c301c2fec2fcc2f9c2f6c2f2c2efc2ecc2e8c2e5c2e1c2dec2da81526020017fc328c326c325c323c321c320c31ec31cc31ac318c316c314c312c310c30ec30b81526020017fc33ac339c338c337c336c335c334c333c332c331c330c32ec32dc32cc32ac32981526020017fc344c343c343c342c342c341c341c340c33fc33fc33ec33dc33dc33cc33bc33a81526020017fc34ac349c349c349c348c348c348c348c347c347c346c346c346c345c345c34481526020017fc34dc34dc34cc34cc34cc34cc34cc34cc34bc34bc34bc34bc34bc34ac34ac34a81526020017fc34ec34ec34ec34ec34ec34ec34ec34ec34ec34ec34dc34dc34dc34dc34dc34d81526020017fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34ec34e81526020017fc350c350c350c350c350c350c34fc34fc34fc34fc34fc34fc34fc34fc34fc34f815250905060008084600f0b12612bf45783612bfd565b612bfd84613b09565b612c0b90600f0b60646138d7565b9050604081901c69c35000000000000000006101bf821015612cc7576040600483600f16901b85600485901c601c8110612c4757612c476134b0565b602002015161ffff911c16901b905080604067ffffffffffffffff851682826004612c7388600161357b565b600f16901b896004612c868a600161357b565b901c601c8110612c9857612c986134b0565b6020020151901c61ffff16901b612caf9190613499565b612cb991906138d7565b612cc492911c61357b565b90505b600086600f0b1315612cee57612ce78169c350000000000000000061357b565b9050612d05565b612d028169c3500000000000000000613499565b90505b61040a620186a082613943565b60008082600f0b1215612d81576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f46454f3a63616e277420636f6e7665727420746f2075696e740000000000000060448201526064016107d8565b50600f0b90565b6000600f82810b9084900b037fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b6000600f83810b9083900b0260401d7fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b600081600003612e4557506000919050565b8160017001000000000000000000000000000000008210612e6b5760809190911c9060401b5b680100000000000000008210612e865760409190911c9060201b5b6401000000008210612e9d5760209190911c9060101b5b620100008210612eb25760109190911c9060081b5b6101008210612ec65760089190911c9060041b5b60108210612ed95760049190911c9060021b5b60088210612ee55760011b5b6001818581612ef657612ef6613914565b048201901c90506001818581612f0e57612f0e613914565b048201901c90506001818581612f2657612f26613914565b048201901c90506001818581612f3e57612f3e613914565b048201901c90506001818581612f5657612f56613914565b048201901c90506001818581612f6e57612f6e613914565b048201901c90506001818581612f8657612f86613914565b048201901c90506000818581612f9e57612f9e613914565b049050808210612fae57806121f1565b509392505050565b60008082600f0b13612fc757600080fd5b6080612fd283613044565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b6000600f83810b9083900b017fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b60008082600f0b1361305557600080fd5b6000600f83900b680100000000000000008112613074576040918201911d5b6401000000008112613088576020918201911d5b62010000811261309a576010918201911d5b61010081126130ab576008918201911d5b601081126130bb576004918201911d5b600481126130cb576002918201911d5b600281126130da576001820191505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820160401b600f85900b607f8490031b6780000000000000005b600081131561313b5790800260ff81901c8281029390930192607f011c9060011d613115565b509095945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461316857600080fd5b50565b801515811461316857600080fd5b600080600080600060a0868803121561319157600080fd5b853561319c81613146565b9450602086013593506040860135925060608601356131ba8161316b565b949793965091946080013592915050565b6000806000606084860312156131e057600080fd5b505081359360208301359350604090920135919050565b602080825282518282018190526000919060409081850190868401855b82811015613287578151805185528681015173ffffffffffffffffffffffffffffffffffffffff9081168887015286820151878701526060808301511515908701526080808301519087015260a0808301519087015260c091820151169085015260e09093019290850190600101613214565b5091979650505050505050565b6000602082840312156132a657600080fd5b81356127a481613146565b60008082840360c08112156132c557600080fd5b83356132d081613146565b925060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561330257600080fd5b506020830190509250929050565b6000806040838503121561332357600080fd5b82359150602083013561333581613146565b809150509250929050565b60008060008060008060c0878903121561335957600080fd5b863561336481613146565b9550602087013594506040870135935060608701356133828161316b565b9598949750929560808101359460a0909101359350915050565b6000602082840312156133ae57600080fd5b5035919050565b600080604083850312156133c857600080fd5b50508035926020909101359150565b600080600080608085870312156133ed57600080fd5b843593506020850135925060408501359150606085013561340d81613146565b939692955090935050565b600080600080600060a0868803121561343057600080fd5b853561343b81613146565b94506020860135935060408601356134528161316b565b94979396509394606081013594506080013592915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156134ab576134ab61346a565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008161351d5761351d61346a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036135745761357461346a565b5060010190565b6000821982111561358e5761358e61346a565b500190565b60008060008060008060c087890312156135ac57600080fd5b86516135b781613146565b60208801519096506135c881613146565b60408801519095506135d981613146565b60608801519094506135ea81613146565b60808801519093506135fb81613146565b60a088015190925061360c81613146565b809150509295509295509295565b60006020828403121561362c57600080fd5b81516127a48161316b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561367e5761367e6134df565b604052919050565b61ffff8116811461316857600080fd5b67ffffffffffffffff8116811461316857600080fd5b600060a082840312156136be57600080fd5b60405160a0810181811067ffffffffffffffff821117156136e1576136e16134df565b60405282356136ef81613686565b815260208301356136ff81613686565b6020820152604083013561371281613696565b6040820152606083013561372581613696565b6060820152608083013561373881613696565b60808201529392505050565b60008135610bc181613696565b813561375c81613686565b61ffff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00008216178355602084013561379a81613686565b63ffff00008160101b16905080837fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000084161717845560408501356137dd81613696565b6bffffffffffffffff000000008160201b169050837fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008416179350808483171785556060860135925061382f83613696565b73ffffffffffffffff0000000000000000000000008360601b167fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff851683178217178555505050506138d361388660808401613744565b82547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b7bffffffffffffffff000000000000000000000000000000000000000016178255565b5050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561390f5761390f61346a565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082613979577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600081518084526020808501945080840160005b838110156139ae57815187529582019590820190600101613992565b509495945050505050565b8481526080602082015260006139d2608083018661397e565b905083604083015273ffffffffffffffffffffffffffffffffffffffff8316606083015295945050505050565b60006020808385031215613a1257600080fd5b825167ffffffffffffffff80821115613a2a57600080fd5b818501915085601f830112613a3e57600080fd5b815181811115613a5057613a506134df565b8060051b9150613a61848301613637565b8181529183018401918481019088841115613a7b57600080fd5b938501935b83851015611e8657845182529385019390850190613a80565b600081600f0b83600f0b60008212826f7fffffffffffffffffffffffffffffff03821381151615613acc57613acc61346a565b827fffffffffffffffffffffffffffffffff80000000000000000000000000000000038212811615613b0057613b0061346a565b50019392505050565b600081600f0b7fffffffffffffffffffffffffffffffff800000000000000000000000000000008103613b3e57613b3e61346a565b6000039291505056fea26469706673582212202e5d1bffb2d1f4cc06b752e3336fdf4c4c7540782e640328bfdc72297c30cc3a64736f6c634300080d0033

Deployed ByteCode

0x6080604052600436106100f35760003560e01c80639ba63e9e1161008a578063c4d66de811610059578063c4d66de8146102cc578063d79875eb146102ec578063e983b85c146102ff578063ee1ca9601461031f57600080fd5b80639ba63e9e1461024c578063a6bd54271461028f578063a86265bb146102a4578063a98e4e77146102b757600080fd5b80632fee0638116100c65780632fee0638146101cc5780633656eec2146101ec57806375e5da181461020c578063860506801461022c57600080fd5b806301b5ff8f146100f857806319aeb4901461012b5780631c1b8772146101585780631c2f3e3d1461017a575b600080fd5b34801561010457600080fd5b50610118610113366004613179565b610332565b6040519081526020015b60405180910390f35b34801561013757600080fd5b5061014b6101463660046131cb565b610414565b60405161012291906131f7565b34801561016457600080fd5b50610178610173366004613294565b610609565b005b34801561018657600080fd5b506000546101a79073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610122565b3480156101d857600080fd5b506101786101e73660046132b1565b6106dd565b3480156101f857600080fd5b50610118610207366004613310565b610a85565b34801561021857600080fd5b50610118610227366004613340565b610bc7565b34801561023857600080fd5b506101a761024736600461339c565b610cab565b34801561025857600080fd5b50610118610267366004613294565b73ffffffffffffffffffffffffffffffffffffffff1660009081526004602052604090205490565b34801561029b57600080fd5b50600354610118565b6101786102b23660046133b5565b610ce8565b3480156102c357600080fd5b50600554610118565b3480156102d857600080fd5b506101786102e7366004613294565b611076565b6101786102fa3660046133b5565b61108e565b34801561030b57600080fd5b5061014b61031a3660046133d7565b61142d565b61017861032d366004613418565b6115ce565b73ffffffffffffffffffffffffffffffffffffffff851660009081526006602052604081205461040a9060079061036b90600190613499565b8154811061037b5761037b6134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff64010000000084048116918301919091526c010000000000000000000000008304811660608301527401000000000000000000000000000000000000000090920490911660808201528686868661199e565b9695505050505050565b606060038367ffffffffffffffff811115610431576104316134df565b6040519080825280602002602001820160405280156104d057816020015b6104bd6040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b81526020019060019003908161044f5790505b508154909250600084810361057c5760006104eb8884613499565b905060008782116104fd576000610507565b6105078883613499565b90505b808211156105755760008561051e8461350e565b93508381548110610531576105316134b0565b90600052602060002001905061054781846119db565b878561055281613543565b965081518110610564576105646134b0565b60200260200101819052505061050a565b50506105ff565b866000610589888361357b565b9050838111156105965750825b808210156105fc576105c38583815481106105b3576105b36134b0565b90600052602060002001836119db565b86846105ce81613543565b9550815181106105e0576105e06134b0565b6020026020010181905250816105f590613543565b9150610596565b50505b5050509392505050565b61061281611bab565b8073ffffffffffffffffffffffffffffffffffffffff1663746b56f96040518163ffffffff1660e01b815260040160c060405180830381865afa15801561065d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106819190613593565b6002805473ffffffffffffffffffffffffffffffffffffffff9283167fffffffffffffffffffffffff00000000000000000000000000000000000000009182161790915560018054979092169616959095179094555050505050565b600080546040517f91e1472b000000000000000000000000000000000000000000000000000000008152336004820152602481019290925273ffffffffffffffffffffffffffffffffffffffff16906391e1472b90604401602060405180830381865afa158015610752573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610776919061361a565b6107e1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f466f72743a21676f76000000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821660009081526006602052604081205490819003610a4657600760405180604001604052808480360381019061082d91906136ac565b815273ffffffffffffffffffffffffffffffffffffffff8681166020928301528354600180820186556000958652948390208451805160029093029091018054828601516040840151606085015160809095015161ffff9687167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009094169390931762010000969092168602919091177fffffffffffffffffffffffff00000000000000000000000000000000ffffffff1664010000000067ffffffffffffffff928316027fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff16176c0100000000000000000000000094821694909402939093177fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000939091169290920291909117815593909201519290930180547fffffffffffffffffffffffff000000000000000000000000000000000000000016929093169190911790915560075491508110610a19576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601e60248201527f464f3a746f6f206d75636820746f6b656e526567697374726174696f6e73000060448201526064016107d8565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600660205260409020819055505050565b816007610a54600184613499565b81548110610a6457610a646134b0565b60009182526020909120600290910201610a7e8282613751565b5050505050565b60008060038481548110610a9b57610a9b6134b0565b600091825260208083206040805160c081018252939091015463ffffffff808216855264010000000082046dffffffffffffffffffffffffffff16858501527201000000000000000000000000000000000000820461ffff168584015274010000000000000000000000000000000000000000820466ffffffffffffff1660608601527b01000000000000000000000000000000000000000000000000000000820460ff16151560808601527c01000000000000000000000000000000000000000000000000000000009091041660a084015273ffffffffffffffffffffffffffffffffffffffff87168452600490915290912054909150815163ffffffff1603610bbb57602001516dffffffffffffffffffffffffffff169050610bc1565b60009150505b92915050565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260066020526040812054610ca090600790610c0090600190613499565b81548110610c1057610c106134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff64010000000084048116918301919091526c010000000000000000000000008304811660608301527401000000000000000000000000000000000000000090920490911660808201528787878787611d60565b979650505050505050565b600060058281548110610cc057610cc06134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1692915050565b600060038381548110610cfd57610cfd6134b0565b600091825260208220018054600580549294509163ffffffff909116908110610d2857610d286134b0565b6000918252602082200154835473ffffffffffffffffffffffffffffffffffffffff9091169250740100000000000000000000000000000000000000009004600681901c6603ffffffffffff1660029190911b60fc161b8354600780549293507b01000000000000000000000000000000000000000000000000000000820460ff16927c0100000000000000000000000000000000000000000000000000000000830463ffffffff169260009291720100000000000000000000000000000000000090910461ffff16908110610e0057610e006134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff6401000000008085048216928401929092526c010000000000000000000000008404811660608401527401000000000000000000000000000000000000000090930490921660808201528754909250610eb291610ead918a916dffffffffffffffffffffffffffff910416613499565b611e92565b86546dffffffffffffffffffffffffffff91909116640100000000027fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161786556000610f0382843433611f0f565b905060008415610f445785821115610f3f57670de0b6b3a7640000610f288784613499565b610f32908b6138d7565b610f3c9190613943565b90505b610f76565b85821015610f7657670de0b6b3a7640000610f5f8388613499565b610f69908b6138d7565b610f739190613943565b90505b8015611009576001546040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff898116600483015260248201849052909116906340c10f1990604401600060405180830381600087803b158015610ff057600080fd5b505af1158015611004573d6000803e3d6000fd5b505050505b604080518b8152602081018b905273ffffffffffffffffffffffffffffffffffffffff891691810191909152606081018290527f72db4df7c54a4e3502d596213c9116f06a327af3a139e03e5fb36fa5a09ecb9e906080015b60405180910390a150505050505050505050565b61107f81611ffb565b50600580546001018155600052565b6000600383815481106110a3576110a36134b0565b600091825260208220018054600580549294509163ffffffff9091169081106110ce576110ce6134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff16905033811461115b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f464f3a6e6f74206f776e6572000000000000000000000000000000000000000060448201526064016107d8565b815460078054740100000000000000000000000000000000000000008304600681901c6603ffffffffffff1660029190911b60fc161b927b01000000000000000000000000000000000000000000000000000000810460ff16927c0100000000000000000000000000000000000000000000000000000000820463ffffffff16926000927201000000000000000000000000000000000000900461ffff16908110611208576112086134b0565b60009182526020918290206040805160a08101825260029093029091015461ffff8082168452620100008204169383019390935267ffffffffffffffff6401000000008085048216928401929092526c0100000000000000000000000084048116606084015274010000000000000000000000000000000000000000909304909216608082015287549092506112b591610ead918a916dffffffffffffffffffffffffffff910416613499565b86546dffffffffffffffffffffffffffff91909116640100000000027fffffffffffffffffffffffffffff0000000000000000000000000000ffffffff9091161786556000611305823433612111565b90506000611325670de0b6b3a764000069271000000000000000006138d7565b61251c61133585858a8a8a61199e565b61133f908c6138d7565b61134991906138d7565b6113539190613943565b905080156113e6576001546040517f40c10f190000000000000000000000000000000000000000000000000000000081523360048201526024810183905273ffffffffffffffffffffffffffffffffffffffff909116906340c10f1990604401600060405180830381600087803b1580156113cd57600080fd5b505af11580156113e1573d6000803e3d6000fd5b505050505b604080518b8152602081018b90523391810191909152606081018290527fb69e24112cb4483e933dc3bda2d474e8d511e1d7058a983fe98a7d5d78fb974390608001611062565b60608367ffffffffffffffff811115611448576114486134df565b6040519080825280602002602001820160405280156114e757816020015b6114d46040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b8152602001906001900390816114665790505b506003805491925090600087156114fc578791505b858211156115115761150e8683613499565b90505b73ffffffffffffffffffffffffffffffffffffffff8516600090815260046020526040812054905b888110801561154757508284115b156115c1576000856115588661350e565b9550858154811061156b5761156b6134b0565b6000918252602090912001805490915063ffffffff168390036115bb5761159281866119db565b878361159d81613543565b9450815181106115af576115af6134b0565b60200260200101819052505b50611539565b5050505050949350505050565b73ffffffffffffffffffffffffffffffffffffffff851660009081526006602052604081205461160090600190613499565b9050600060078281548110611617576116176134b0565b600091825260208083206040805160a08101825260029094029091015461ffff8082168552620100008204169284019290925267ffffffffffffffff64010000000083048116918401919091526c010000000000000000000000008204811660608401527401000000000000000000000000000000000000000090910416608082015291506116a7823433612111565b905060006116b989838a8a8a8a610bc7565b60035460408051918252602082018890523390820152606081018290529091507f39b02417d6826eadf5421ac690db0dd00065bb988a2f8cd32341c68d74d36beb9060800160405180910390a160036040518060c0016040528061171c336121fa565b63ffffffff16815260200161173084611e92565b6dffffffffffffffffffffffffffff1681526020018661ffff1681526020016117588b612337565b66ffffffffffffff90811682528a151560208084019190915263ffffffff8b81166040948501528554600180820188556000978852968390208651910180549387015187870151606089015160808a015160a0909a015186167c0100000000000000000000000000000000000000000000000000000000027bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9a15157b01000000000000000000000000000000000000000000000000000000029a909a167affffffffffffffffffffffffffffffffffffffffffffffffffffff9190981674010000000000000000000000000000000000000000027fffffffffff00000000000000ffffffffffffffffffffffffffffffffffffffff61ffff909316720100000000000000000000000000000000000002929092167fffffffffff000000000000000000ffffffffffffffffffffffffffffffffffff6dffffffffffffffffffffffffffff909416640100000000027fffffffffffffffffffffffffffff000000000000000000000000000000000000909816959096169490941795909517169290921792909217919091169190911792909217909155905490517f9dc29fac0000000000000000000000000000000000000000000000000000000081523360048201526024810187905273ffffffffffffffffffffffffffffffffffffffff90911690639dc29fac90604401600060405180830381600087803b15801561197b57600080fd5b505af115801561198f573d6000803e3d6000fd5b50505050505050505050505050565b60008060036119ad4385613499565b6119b791906138d7565b9050836119cf576119ca8787838861236f565b610ca0565b610ca08787838861246e565b611a496040518060e0016040528060008152602001600073ffffffffffffffffffffffffffffffffffffffff168152602001600081526020016000151581526020016000815260200160008152602001600073ffffffffffffffffffffffffffffffffffffffff1681525090565b6040805160e0810190915282815283546007805460208401927201000000000000000000000000000000000000900461ffff16908110611a8b57611a8b6134b0565b60009182526020918290206002918202016001015473ffffffffffffffffffffffffffffffffffffffff16835286549290910191740100000000000000000000000000000000000000009004600681901c6603ffffffffffff16911b60fc161b8152845460ff7b010000000000000000000000000000000000000000000000000000008204161515602083015263ffffffff7c01000000000000000000000000000000000000000000000000000000008204811660408401526dffffffffffffffffffffffffffff640100000000830416606084015260058054608090940193909291909116908110611b8057611b806134b0565b60009182526020909120015473ffffffffffffffffffffffffffffffffffffffff1690529392505050565b60005473ffffffffffffffffffffffffffffffffffffffff1633811480611c6257506040517f91e1472b0000000000000000000000000000000000000000000000000000000081523360048201526000602482015273ffffffffffffffffffffffffffffffffffffffff8216906391e1472b90604401602060405180830381865afa158015611c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c62919061361a565b611cc8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600960248201527f466f72743a21676f76000000000000000000000000000000000000000000000060448201526064016107d8565b6040805173ffffffffffffffffffffffffffffffffffffffff8084168252841660208201527f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd2910160405180910390a150600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000611d6d600a4361357b565b8311611dd5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a6578657263697365426c6f636b20746f6f20736d616c6c000000000060448201526064016107d8565b6000611de4888888888861199e565b90508415611e2957866040611dfa8360646138d7565b901c1015611e24576064611e1788680100000000000000006138d7565b611e219190613943565b90505b611e61565b856040611e378360646138d7565b901c1015611e61576064611e5487680100000000000000006138d7565b611e5e9190613943565b90505b80611e7c846f0de0b6b3a764000000000000000000006138d7565b611e869190613943565b98975050505050505050565b60006dffffffffffffffffffffffffffff8210611f0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f484f3a63616e277420636f6e7665727420746f2075696e74313132000000000060448201526064016107d8565b5090565b60025484516020860151600092839273ffffffffffffffffffffffffffffffffffffffff9091169163594e25b591879161ffff91821691611f50911661253f565b89886040518663ffffffff1660e01b8152600401611f7194939291906139b9565b60006040518083038185885af1158015611f8f573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611fd691908101906139ff565b905061040a81600181518110611fee57611fee6134b0565b6020026020010151612586565b60005473ffffffffffffffffffffffffffffffffffffffff161561207b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f466f72743a21696e697469616c697a650000000000000000000000000000000060448201526064016107d8565b604080516000815273ffffffffffffffffffffffffffffffffffffffff831660208201527f3aaaebeb4821d6a7e5c77ece53cff0afcc56c82add2c978dbbb7f73e84cbcfd2910160405180910390a1600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60025483516020850151600092839273ffffffffffffffffffffffffffffffffffffffff909116916354a80f0491879161ffff91821691612152911661253f565b6001886040518663ffffffff1660e01b815260040161217494939291906139b9565b60006040518083038185885af1158015612192573d6000803e3d6000fd5b50505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526121d991908101906139ff565b90506121f181600181518110611fee57611fee6134b0565b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116600090815260046020526040812054808203610bc1575060055473ffffffffffffffffffffffffffffffffffffffff8316600090815260046020526040902081905564010000000081106122c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f484f3a216163636f756e7473000000000000000000000000000000000000000060448201526064016107d8565b600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff851617905592915050565b6000805b6603ffffffffffff8311156123615760049290921c9161235a81613543565b905061233b565b60069290921b909117919050565b60008061239484876040015167ffffffffffffffff1661238f91906138d7565b6125b9565b905060006123ba85886080015167ffffffffffffffff166123b591906138d7565b61264a565b905060006123c7836126c5565b905060006123d7888786866126e7565b905060006123e5828461272b565b90506000876123fb6123f6846127ab565b612d12565b61240591906138d7565b905060008a61243a6123f6612423680100000000000000008a613a99565b612435612430888b612d88565b6127ab565b612ddc565b61244491906138d7565b905080821161245457600061245e565b61245e8183613499565b9c9b505050505050505050505050565b60008061248e84876040015167ffffffffffffffff1661238f91906138d7565b905060006124af85886060015167ffffffffffffffff166123b591906138d7565b905060006124bc836126c5565b905060006124cc888786866126e7565b905060006124da828461272b565b90506000896125176123f66124f86801000000000000000089613a99565b61243568010000000000000000612512612430898c612d88565b612d88565b61252191906138d7565b905060008861243a6123f668010000000000000000612512876127ab565b604080516001808252818301909252606091602080830190803683370190505090508181600081518110612575576125756134b0565b602002602001018181525050919050565b60008161259d670de0b6b3a76400006107d06138d7565b6125af90670de0b6b3a76400006138d7565b610bc19190613943565b60006f06f05b59d3b2000000000000000000008210612634576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a63616e277420636f6e7665727420746f20363462697473000000000060448201526064016107d8565b610bc1670de0b6b3a7640000604084901b613943565b60006f800000000000000000000000000000008210611f0b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f46454f3a63616e277420636f6e7665727420746f20696e74313238000000000060448201526064016107d8565b60008082600f0b12156126d757600080fd5b610bc1604083600f0b901b612e33565b60006121f16127256127186127138861270989680100000000000000006138d7565b6123b59190613943565b612fb6565b600186600f0b901d612ff0565b83612d88565b600081600f0b60000361273d57600080fd5b600082600f0b604085600f0b901b8161275857612758613914565b0590507fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113155b6127a457600080fd5b9392505050565b6000806040518061038001604052807f174a15bf143412a8111c0f8f0e020c740ae6095807ca063b04ad031e018f000081526020017f2f8c2e0f2c912b1229922811268f250b23872202207d1ef61d6f1be61a5d18d481526020017f46a2453c43d4426b41003f943e263cb63b4539d3385f36ea357333fb3282310881526020017f5c0d5ac5597b582f56e05590543e52ea5194503c4ee24d874c294aca4969480781526020017f6f6c6e466d1f6bf56ac9699b686a6738660364cc6392625761195fd95e975d5381526020017f807e7f7f7e7d7d797c737b6a7a5f79517841772f761a750373e972cd71af708e81526020017f8f2a8e518d768c998bb98ad789f2890b88218736864785568463836e8276817b81526020017f9b749ac19a0b9953989997dd971e965d959a94d4940c9342927591a690d4900081526020017fa57ca4eca459a3c4a32ea295a1faa15da0bda01c9f789ed29e2a9d809cd39c2581526020017fad78ad07ac93ac1eaba7ab2eaab3aa36a9b8a937a8b5a830a7aaa721a697a60b81526020017fb3aab353b2fab2a0b245b1e7b189b128b0c6b062affdaf96af2daec2ae56ade881526020017fb859b818b7d6b793b74eb708b6c0b678b62eb5e2b595b547b4f7b4a6b454b40081526020017fbbcdbb9ebb6ebb3cbb0abad7baa2ba6dba36b9ffb9c6b98cb951b915b8d8b89981526020017fbe49be27be05bde2bdbebd99bd74bd4dbd26bcfebcd5bcacbc81bc56bc29bbfc81526020017fc006bfeebfd7bfbebfa5bf8cbf72bf57bf3cbf20bf03bee6bec8bea9be8abe6981526020017fc135c126c116c105c0f4c0e3c0d1c0bfc0acc099c086c072c05dc048c032c01c81526020017fc200c1f5c1ebc1e0c1d5c1c9c1bec1b1c1a5c198c18bc17ec170c162c154c14581526020017fc283c27cc275c26ec267c260c258c250c248c240c238c22fc226c21dc213c20a81526020017fc2d6c2d2c2cdc2c9c2c5c2c0c2bbc2b6c2b1c2acc2a7c2a1c29bc295c28fc28981526020017fc309c306c304c301c2fec2fcc2f9c2f6c2f2c2efc2ecc2e8c2e5c2e1c2dec2da81526020017fc328c326c325c323c321c320c31ec31cc31ac318c316c314c312c310c30ec30b81526020017fc33ac339c338c337c336c335c334c333c332c331c330c32ec32dc32cc32ac32981526020017fc344c343c343c342c342c341c341c340c33fc33fc33ec33dc33dc33cc33bc33a81526020017fc34ac349c349c349c348c348c348c348c347c347c346c346c346c345c345c34481526020017fc34dc34dc34cc34cc34cc34cc34cc34cc34bc34bc34bc34bc34bc34ac34ac34a81526020017fc34ec34ec34ec34ec34ec34ec34ec34ec34ec34ec34dc34dc34dc34dc34dc34d81526020017fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34fc34ec34e81526020017fc350c350c350c350c350c350c34fc34fc34fc34fc34fc34fc34fc34fc34fc34f815250905060008084600f0b12612bf45783612bfd565b612bfd84613b09565b612c0b90600f0b60646138d7565b9050604081901c69c35000000000000000006101bf821015612cc7576040600483600f16901b85600485901c601c8110612c4757612c476134b0565b602002015161ffff911c16901b905080604067ffffffffffffffff851682826004612c7388600161357b565b600f16901b896004612c868a600161357b565b901c601c8110612c9857612c986134b0565b6020020151901c61ffff16901b612caf9190613499565b612cb991906138d7565b612cc492911c61357b565b90505b600086600f0b1315612cee57612ce78169c350000000000000000061357b565b9050612d05565b612d028169c3500000000000000000613499565b90505b61040a620186a082613943565b60008082600f0b1215612d81576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f46454f3a63616e277420636f6e7665727420746f2075696e740000000000000060448201526064016107d8565b50600f0b90565b6000600f82810b9084900b037fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b6000600f83810b9083900b0260401d7fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b600081600003612e4557506000919050565b8160017001000000000000000000000000000000008210612e6b5760809190911c9060401b5b680100000000000000008210612e865760409190911c9060201b5b6401000000008210612e9d5760209190911c9060101b5b620100008210612eb25760109190911c9060081b5b6101008210612ec65760089190911c9060041b5b60108210612ed95760049190911c9060021b5b60088210612ee55760011b5b6001818581612ef657612ef6613914565b048201901c90506001818581612f0e57612f0e613914565b048201901c90506001818581612f2657612f26613914565b048201901c90506001818581612f3e57612f3e613914565b048201901c90506001818581612f5657612f56613914565b048201901c90506001818581612f6e57612f6e613914565b048201901c90506001818581612f8657612f86613914565b048201901c90506000818581612f9e57612f9e613914565b049050808210612fae57806121f1565b509392505050565b60008082600f0b13612fc757600080fd5b6080612fd283613044565b600f0b6fb17217f7d1cf79abc9e3b39803f2f6af02901c9050919050565b6000600f83810b9083900b017fffffffffffffffffffffffffffffffff80000000000000000000000000000000811280159061279b57506f7fffffffffffffffffffffffffffffff8113156127a457600080fd5b60008082600f0b1361305557600080fd5b6000600f83900b680100000000000000008112613074576040918201911d5b6401000000008112613088576020918201911d5b62010000811261309a576010918201911d5b61010081126130ab576008918201911d5b601081126130bb576004918201911d5b600481126130cb576002918201911d5b600281126130da576001820191505b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820160401b600f85900b607f8490031b6780000000000000005b600081131561313b5790800260ff81901c8281029390930192607f011c9060011d613115565b509095945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461316857600080fd5b50565b801515811461316857600080fd5b600080600080600060a0868803121561319157600080fd5b853561319c81613146565b9450602086013593506040860135925060608601356131ba8161316b565b949793965091946080013592915050565b6000806000606084860312156131e057600080fd5b505081359360208301359350604090920135919050565b602080825282518282018190526000919060409081850190868401855b82811015613287578151805185528681015173ffffffffffffffffffffffffffffffffffffffff9081168887015286820151878701526060808301511515908701526080808301519087015260a0808301519087015260c091820151169085015260e09093019290850190600101613214565b5091979650505050505050565b6000602082840312156132a657600080fd5b81356127a481613146565b60008082840360c08112156132c557600080fd5b83356132d081613146565b925060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08201121561330257600080fd5b506020830190509250929050565b6000806040838503121561332357600080fd5b82359150602083013561333581613146565b809150509250929050565b60008060008060008060c0878903121561335957600080fd5b863561336481613146565b9550602087013594506040870135935060608701356133828161316b565b9598949750929560808101359460a0909101359350915050565b6000602082840312156133ae57600080fd5b5035919050565b600080604083850312156133c857600080fd5b50508035926020909101359150565b600080600080608085870312156133ed57600080fd5b843593506020850135925060408501359150606085013561340d81613146565b939692955090935050565b600080600080600060a0868803121561343057600080fd5b853561343b81613146565b94506020860135935060408601356134528161316b565b94979396509394606081013594506080013592915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000828210156134ab576134ab61346a565b500390565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008161351d5761351d61346a565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036135745761357461346a565b5060010190565b6000821982111561358e5761358e61346a565b500190565b60008060008060008060c087890312156135ac57600080fd5b86516135b781613146565b60208801519096506135c881613146565b60408801519095506135d981613146565b60608801519094506135ea81613146565b60808801519093506135fb81613146565b60a088015190925061360c81613146565b809150509295509295509295565b60006020828403121561362c57600080fd5b81516127a48161316b565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561367e5761367e6134df565b604052919050565b61ffff8116811461316857600080fd5b67ffffffffffffffff8116811461316857600080fd5b600060a082840312156136be57600080fd5b60405160a0810181811067ffffffffffffffff821117156136e1576136e16134df565b60405282356136ef81613686565b815260208301356136ff81613686565b6020820152604083013561371281613696565b6040820152606083013561372581613696565b6060820152608083013561373881613696565b60808201529392505050565b60008135610bc181613696565b813561375c81613686565b61ffff811690508154817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00008216178355602084013561379a81613686565b63ffff00008160101b16905080837fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000084161717845560408501356137dd81613696565b6bffffffffffffffff000000008160201b169050837fffffffffffffffffffffffffffffffffffffffff0000000000000000000000008416179350808483171785556060860135925061382f83613696565b73ffffffffffffffff0000000000000000000000008360601b167fffffffffffffffffffffffff0000000000000000ffffffffffffffffffffffff851683178217178555505050506138d361388660808401613744565b82547fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b7bffffffffffffffff000000000000000000000000000000000000000016178255565b5050565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561390f5761390f61346a565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082613979577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b600081518084526020808501945080840160005b838110156139ae57815187529582019590820190600101613992565b509495945050505050565b8481526080602082015260006139d2608083018661397e565b905083604083015273ffffffffffffffffffffffffffffffffffffffff8316606083015295945050505050565b60006020808385031215613a1257600080fd5b825167ffffffffffffffff80821115613a2a57600080fd5b818501915085601f830112613a3e57600080fd5b815181811115613a5057613a506134df565b8060051b9150613a61848301613637565b8181529183018401918481019088841115613a7b57600080fd5b938501935b83851015611e8657845182529385019390850190613a80565b600081600f0b83600f0b60008212826f7fffffffffffffffffffffffffffffff03821381151615613acc57613acc61346a565b827fffffffffffffffffffffffffffffffff80000000000000000000000000000000038212811615613b0057613b0061346a565b50019392505050565b600081600f0b7fffffffffffffffffffffffffffffffff800000000000000000000000000000008103613b3e57613b3e61346a565b6000039291505056fea26469706673582212202e5d1bffb2d1f4cc06b752e3336fdf4c4c7540782e640328bfdc72297c30cc3a64736f6c634300080d0033