library LibCobbDouglas {

    /// @dev The cobb-douglas function used to compute fee-based rewards for
    ///      staking pools in a given epoch. This function does not perform
    ///      bounds checking on the inputs, but the following conditions
    ///      need to be true:
    ///         0 <= fees / totalFees <= 1
    ///         0 <= stake / totalStake <= 1
    ///         0 <= alphaNumerator / alphaDenominator <= 1
    /// @param totalRewards collected over an epoch.
    /// @param fees Fees attributed to the the staking pool.
    /// @param totalFees Total fees collected across all pools that earned rewards.
    /// @param stake Stake attributed to the staking pool.
    /// @param totalStake Total stake across all pools that earned rewards.
    /// @param alphaNumerator Numerator of `alpha` in the cobb-douglas function.
    /// @param alphaDenominator Denominator of `alpha` in the cobb-douglas
    ///        function.
    /// @return rewards Rewards owed to the staking pool.
    function cobbDouglas(
        uint256 totalRewards,
        uint256 fees,
        uint256 totalFees,
        uint256 stake,
        uint256 totalStake,
        uint32 alphaNumerator,
        uint32 alphaDenominator
    )
        internal
        pure
        returns (uint256 rewards)
    {
        int256 feeRatio = LibFixedMath.toFixed(fees, totalFees);
        int256 stakeRatio = LibFixedMath.toFixed(stake, totalStake);
        if (feeRatio == 0 || stakeRatio == 0) {
            return rewards = 0;
        }
        // The cobb-doublas function has the form:
        // `totalRewards * feeRatio ^ alpha * stakeRatio ^ (1-alpha)`
        // This is equivalent to:
        // `totalRewards * stakeRatio * e^(alpha * (ln(feeRatio / stakeRatio)))`
        // However, because `ln(x)` has the domain of `0 < x < 1`
        // and `exp(x)` has the domain of `x < 0`,
        // and fixed-point math easily overflows with multiplication,
        // we will choose the following if `stakeRatio > feeRatio`:
        // `totalRewards * stakeRatio / e^(alpha * (ln(stakeRatio / feeRatio)))`

        // Compute
        // `e^(alpha * ln(feeRatio/stakeRatio))` if feeRatio <= stakeRatio
        // or
        // `e^(alpa * ln(stakeRatio/feeRatio))` if feeRatio > stakeRatio
        int256 n = feeRatio <= stakeRatio ?
            LibFixedMath.div(feeRatio, stakeRatio) :
            LibFixedMath.div(stakeRatio, feeRatio);
        n = LibFixedMath.exp(
            LibFixedMath.mulDiv(
                LibFixedMath.ln(n),
                int256(alphaNumerator),
                int256(alphaDenominator)
            )
        );
        // Compute
        // `totalRewards * n` if feeRatio <= stakeRatio
        // or
        // `totalRewards / n` if stakeRatio > feeRatio
        // depending on the choice we made earlier.
        n = feeRatio <= stakeRatio ?
            LibFixedMath.mul(stakeRatio, n) :
            LibFixedMath.div(stakeRatio, n);
        // Multiply the above with totalRewards.
        rewards = LibFixedMath.uintMul(n, totalRewards);
    }
}
function cobbDouglas(
    uint256 totalRewards,
    uint256 fees,
    uint256 totalFees,
    uint256 stake,
    uint256 totalStake,
    uint32 alphaNumerator,
    uint32 alphaDenominator
)
    internal
    pure
    returns (uint256 rewards)
{
    int256 feeRatio = LibFixedMath.toFixed(fees, totalFees);
    int256 stakeRatio = LibFixedMath.toFixed(stake, totalStake);
    if (feeRatio == 0 || stakeRatio == 0) {
        return rewards = 0;
    }
    // The cobb-doublas function has the form:
    // `totalRewards * feeRatio ^ alpha * stakeRatio ^ (1-alpha)`
    // This is equivalent to:
    // `totalRewards * stakeRatio * e^(alpha * (ln(feeRatio / stakeRatio)))`
    // However, because `ln(x)` has the domain of `0 < x < 1`
    // and `exp(x)` has the domain of `x < 0`,
    // and fixed-point math easily overflows with multiplication,
    // we will choose the following if `stakeRatio > feeRatio`:
    // `totalRewards * stakeRatio / e^(alpha * (ln(stakeRatio / feeRatio)))`

    // Compute
    // `e^(alpha * ln(feeRatio/stakeRatio))` if feeRatio <= stakeRatio
    // or
    // `e^(alpa * ln(stakeRatio/feeRatio))` if feeRatio > stakeRatio
    int256 n = feeRatio <= stakeRatio ?
        LibFixedMath.div(feeRatio, stakeRatio) :
        LibFixedMath.div(stakeRatio, feeRatio);
    n = LibFixedMath.exp(
        LibFixedMath.mulDiv(
            LibFixedMath.ln(n),
            int256(alphaNumerator),
            int256(alphaDenominator)
        )
    );
    // Compute
    // `totalRewards * n` if feeRatio <= stakeRatio
    // or
    // `totalRewards / n` if stakeRatio > feeRatio
    // depending on the choice we made earlier.
    n = feeRatio <= stakeRatio ?
        LibFixedMath.mul(stakeRatio, n) :
        LibFixedMath.div(stakeRatio, n);
    // Multiply the above with totalRewards.
    rewards = LibFixedMath.uintMul(n, totalRewards);
}

/// @dev The cobb-douglas function used to compute fee-based rewards for
///      staking pools in a given epoch. This function does not perform
///      bounds checking on the inputs, but the following conditions
///      need to be true:
///         0 <= fees / totalFees <= 1
///         0 <= stake / totalStake <= 1
///         0 <= alphaNumerator / alphaDenominator <= 1
/// @param totalRewards collected over an epoch.
/// @param fees Fees attributed to the the staking pool.
/// @param totalFees Total fees collected across all pools that earned rewards.
/// @param stake Stake attributed to the staking pool.
/// @param totalStake Total stake across all pools that earned rewards.
/// @param alphaNumerator Numerator of `alpha` in the cobb-douglas function.
/// @param alphaDenominator Denominator of `alpha` in the cobb-douglas
///        function.
/// @return rewards Rewards owed to the staking pool.