library OLib {
  using Arrays for uint256[];
  using OLib for OLib.Investor;

  // State transition per Vault. Just linear transitions.
  enum State {Inactive, Deposit, Live, Withdraw}

  // Only supports 2 tranches for now
  enum Tranche {Senior, Junior}

  struct VaultParams {
    address seniorAsset;
    address juniorAsset;
    address strategist;
    address strategy;
    uint256 hurdleRate;
    uint256 startTime;
    uint256 enrollment;
    uint256 duration;
    string seniorName;
    string seniorSym;
    string juniorName;
    string juniorSym;
    uint256 seniorTrancheCap;
    uint256 seniorUserCap;
    uint256 juniorTrancheCap;
    uint256 juniorUserCap;
  }

  struct RolloverParams {
    VaultParams vault;
    address strategist;
    string seniorName;
    string seniorSym;
    string juniorName;
    string juniorSym;
  }

  bytes32 public constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
  bytes32 public constant PANIC_ROLE = keccak256("PANIC_ROLE");
  bytes32 public constant GUARDIAN_ROLE = keccak256("GUARDIAN_ROLE");
  bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE");
  bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE");
  bytes32 public constant STRATEGIST_ROLE = keccak256("STRATEGIST_ROLE");
  bytes32 public constant VAULT_ROLE = keccak256("VAULT_ROLE");
  bytes32 public constant ROLLOVER_ROLE = keccak256("ROLLOVER_ROLE");
  bytes32 public constant STRATEGY_ROLE = keccak256("STRATEGY_ROLE");

  // Both sums are running sums. If a user deposits [$1, $5, $3], then
  // userSums would be [$1, $6, $9]. You can figure out the deposit
  // amount be subtracting userSums[i]-userSum[i-1].

  // prefixSums is the total deposited for all investors + this
  // investors deposit at the time this deposit is made. So at
  // prefixSum[0], it would be $1 + totalDeposits, where totalDeposits
  // could be $1000 because other investors have put in money.
  struct Investor {
    uint256[] userSums;
    uint256[] prefixSums;
    bool claimed;
    bool withdrawn;
  }

  /**
   * @dev Given the total amount invested by the Vault, we want to find
   *   out how many of this investor's deposits were actually
   *   used. Use findUpperBound on the prefixSum to find the point
   *   where total deposits were accepted. For example, if $2000 was
   *   deposited by all investors and $1000 was invested, then some
   *   position in the prefixSum splits the array into deposits that
   *   got in, and deposits that didn't get in. That same position
   *   maps to userSums. This is the user's deposits that got
   *   in. Since we are keeping track of the sums, we know at that
   *   position the total deposits for a user was $15, even if it was
   *   15 $1 deposits. And we know the amount that didn't get in is
   *   the last value in userSum - the amount that got it.

   * @param investor A specific investor
   * @param invested The total amount invested by this Vault
   */
  function getInvestedAndExcess(Investor storage investor, uint256 invested)
    internal
    view
    returns (uint256 userInvested, uint256 excess)
  {
    uint256[] storage prefixSums_ = investor.prefixSums;
    uint256 length = prefixSums_.length;
    if (length == 0) {
      // There were no deposits. Return 0, 0.
      return (userInvested, excess);
    }
    uint256 leastUpperBound = prefixSums_.findUpperBound(invested);
    if (length == leastUpperBound) {
      // All deposits got in, no excess. Return total deposits, 0
      userInvested = investor.userSums[length - 1];
      return (userInvested, excess);
    }
    uint256 prefixSum = prefixSums_[leastUpperBound];
    if (prefixSum == invested) {
      // Not all deposits got in, but there are no partial deposits
      userInvested = investor.userSums[leastUpperBound];
      excess = investor.userSums[length - 1] - userInvested;
    } else {
      // Let's say some of my deposits got in. The last deposit,
      // however, was $100 and only $30 got in. Need to split that
      // deposit so $30 got in, $70 is excess.
      userInvested = leastUpperBound > 0
        ? investor.userSums[leastUpperBound - 1]
        : 0;
      uint256 depositAmount = investor.userSums[leastUpperBound] - userInvested;
      if (prefixSum - depositAmount < invested) {
        userInvested += (depositAmount + invested - prefixSum);
        excess = investor.userSums[length - 1] - userInvested;
      } else {
        excess = investor.userSums[length - 1] - userInvested;
      }
    }
  }
}
/**
 * @title Helper functions
 */
function getInvestedAndExcess(Investor storage investor, uint256 invested)
  internal
  view
  returns (uint256 userInvested, uint256 excess)
{
  uint256[] storage prefixSums_ = investor.prefixSums;
  uint256 length = prefixSums_.length;
  if (length == 0) {
    // There were no deposits. Return 0, 0.
    return (userInvested, excess);
  }
  uint256 leastUpperBound = prefixSums_.findUpperBound(invested);
  if (length == leastUpperBound) {
    // All deposits got in, no excess. Return total deposits, 0
    userInvested = investor.userSums[length - 1];
    return (userInvested, excess);
  }
  uint256 prefixSum = prefixSums_[leastUpperBound];
  if (prefixSum == invested) {
    // Not all deposits got in, but there are no partial deposits
    userInvested = investor.userSums[leastUpperBound];
    excess = investor.userSums[length - 1] - userInvested;
  } else {
    // Let's say some of my deposits got in. The last deposit,
    // however, was $100 and only $30 got in. Need to split that
    // deposit so $30 got in, $70 is excess.
    userInvested = leastUpperBound > 0
      ? investor.userSums[leastUpperBound - 1]
      : 0;
    uint256 depositAmount = investor.userSums[leastUpperBound] - userInvested;
    if (prefixSum - depositAmount < invested) {
      userInvested += (depositAmount + invested - prefixSum);
      excess = investor.userSums[length - 1] - userInvested;
    } else {
      excess = investor.userSums[length - 1] - userInvested;
    }
  }
}

/**
 * @dev Given the total amount invested by the Vault, we want to find
 *   out how many of this investor's deposits were actually
 *   used. Use findUpperBound on the prefixSum to find the point
 *   where total deposits were accepted. For example, if $2000 was
 *   deposited by all investors and $1000 was invested, then some
 *   position in the prefixSum splits the array into deposits that
 *   got in, and deposits that didn't get in. That same position
 *   maps to userSums. This is the user's deposits that got
 *   in. Since we are keeping track of the sums, we know at that
 *   position the total deposits for a user was $15, even if it was
 *   15 $1 deposits. And we know the amount that didn't get in is
 *   the last value in userSum - the amount that got it.
 
 * @param investor A specific investor
 * @param invested The total amount invested by this Vault
 */