contract CoverRate is SafeCast {
    using SafeMath for uint256;

    // ============ Constants ============

    uint128 constant PERCENT = 100;

    uint128 constant BASE = 10 ** 18;

    uint128 constant SECONDS_IN_A_YEAR = 60 * 60 * 24 * 365;

    uint128 constant BYTE = 8;

    // ============ Storage ============

    uint64 rate_storage;

    // ============ Constructor ============

    function intialize_rate(
        uint64 coefficients
    )
        internal
    {
        // verify that all coefficients add up to 100%
        uint256 sumOfCoefficients = 0;
        for (
            uint256 actual_coefficients = coefficients;
            actual_coefficients != 0;
            actual_coefficients >>= BYTE
        ) {
            sumOfCoefficients += actual_coefficients % 256;
        }
        require(
            sumOfCoefficients == PERCENT,
            "must sum to 100"
        );

        // store the params
        rate_storage = coefficients;
    }

    // ============ Public Functions ============

    /**
     * Get the interest rate given some utilized and total amounts. The interest function is a
     * polynomial function of the utilization (utilized / total) of the market.
     *
     *   - If both are zero, then the utilization is considered to be equal to 0.
     *
     * @return The interest rate per second (times 10 ** 18)
     */
    function getInterestRate(
        uint128 utilized,
        uint128 total
    )
        public
        view
        returns (uint128)
    {
        if (utilized == 0) {
            return 0;
        }
        if (utilized > total) {
            return BASE;
        }

        // process the first coefficient
        uint256 coefficients = rate_storage;
        uint256 result = uint8(coefficients) * BASE;
        coefficients >>= BYTE;

        // initialize polynomial as the utilization
        // no safeDiv since total must be non-zero at this point
        uint256 polynomial = uint256(BASE).mul(utilized) / total;

        // for each non-zero coefficient...
        while (true) {
            // gets the lowest-order byte
            uint256 coefficient = uint256(uint8(coefficients));

            // if non-zero, add to result
            if (coefficient != 0) {
                // no safeAdd since there are at most 16 coefficients
                // no safeMul since (coefficient < 256 && polynomial <= 10**18)
                result += coefficient * polynomial;

                // break if this is the last non-zero coefficient
                if (coefficient == coefficients) {
                    break;
                }
            }

            // double the order of the polynomial term
            // no safeMul since polynomial <= 10^18
            // no safeDiv since the divisor is a non-zero constant
            polynomial = polynomial * polynomial / BASE;

            // move to next coefficient
            coefficients >>= BYTE;
        }

        // normalize the result
        // no safeDiv since the divisor is a non-zero constant
        return uint128(result / (SECONDS_IN_A_YEAR * PERCENT));
    }

    /**
     * Get all of the coefficients of the interest calculation, starting from the coefficient for
     * the first-order utilization variable.
     *
     * @return The coefficients
     */
    function getCoefficients()
        public
        view
        returns (uint128[] memory)
    {
        // allocate new array with maximum of 16 coefficients
        uint128[] memory result = new uint128[](8);

        // add the coefficients to the array
        uint128 numCoefficients = 0;
        for (
            uint128 coefficients = rate_storage;
            coefficients != 0;
            coefficients >>= BYTE
        ) {
            result[numCoefficients] = coefficients % 256;
            numCoefficients++;
        }

        // modify result.length to match numCoefficients
        assembly {
            mstore(result, numCoefficients)
        }

        return result;
    }
}
/**
 * @title CoverRate
 * @author Yam Finance
 *
 * Interest setter that sets interest based on a polynomial of the usage percentage of the market.
 * Interest = C_0 + C_1 * U^(2^0) + C_2 * U^(2^1) + C_3 * U^(2^2) ... C_8 * U^(2^7)
 * i.e.: coefs = [0, 20, 10, 60, 0, 10] = 0 + 20 * util^0 + 10 * util^2 +
 */
function getInterestRate(
    uint128 utilized,
    uint128 total
)
    public
    view
    returns (uint128)
{
    if (utilized == 0) {
        return 0;
    }
    if (utilized > total) {
        return BASE;
    }

    // process the first coefficient
    uint256 coefficients = rate_storage;
    uint256 result = uint8(coefficients) * BASE;
    coefficients >>= BYTE;

    // initialize polynomial as the utilization
    // no safeDiv since total must be non-zero at this point
    uint256 polynomial = uint256(BASE).mul(utilized) / total;

    // for each non-zero coefficient...
    while (true) {
        // gets the lowest-order byte
        uint256 coefficient = uint256(uint8(coefficients));

        // if non-zero, add to result
        if (coefficient != 0) {
            // no safeAdd since there are at most 16 coefficients
            // no safeMul since (coefficient < 256 && polynomial <= 10**18)
            result += coefficient * polynomial;

            // break if this is the last non-zero coefficient
            if (coefficient == coefficients) {
                break;
            }
        }

        // double the order of the polynomial term
        // no safeMul since polynomial <= 10^18
        // no safeDiv since the divisor is a non-zero constant
        polynomial = polynomial * polynomial / BASE;

        // move to next coefficient
        coefficients >>= BYTE;
    }

    // normalize the result
    // no safeDiv since the divisor is a non-zero constant
    return uint128(result / (SECONDS_IN_A_YEAR * PERCENT));
}

/**
 * Get the interest rate given some utilized and total amounts. The interest function is a
 * polynomial function of the utilization (utilized / total) of the market.
 *
 *   - If both are zero, then the utilization is considered to be equal to 0.
 *
 * @return The interest rate per second (times 10 ** 18)
 */