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

/**
 * @title MonetaryFormulas
 * @notice Classical + GRU monetary formulas (canonical library — promoted from archive).
 * @dev Formulas MUST match vault COMPLIANCE_REQUIREMENTS.md and GRU_Formulas.md crosswalk.
 *
 * Classical:
 * - M = C + D; M = MB × m
 * - V = PQ / M
 * - m = 1 / r; m = (1 + c) / (r + c)
 *
 * GRU layer fan-out (policy): 1 M00 = 5 M0 = 25 M1 face units
 */
library MonetaryFormulas {
    uint256 internal constant BP = 10_000;
    uint256 internal constant WAD = 1e18;

    /// @notice M = C + D
    function moneySupplyCD(uint256 currency, uint256 deposits) internal pure returns (uint256) {
        return currency + deposits;
    }

    /// @notice M = MB × m (m in 18 decimals)
    function moneySupplyFromBase(uint256 monetaryBase, uint256 moneyMultiplierWad) internal pure returns (uint256) {
        return (monetaryBase * moneyMultiplierWad) / WAD;
    }

    /// @notice V = PQ / M
    function velocityPQOverM(uint256 priceLevel, uint256 quantity, uint256 moneySupply) internal pure returns (uint256) {
        if (moneySupply == 0) return 0;
        return (priceLevel * quantity) / moneySupply;
    }

    /// @notice m = 1 / r (r in basis points)
    function simpleMultiplier(uint256 reserveRatioBps) internal pure returns (uint256) {
        require(reserveRatioBps > 0 && reserveRatioBps <= BP, "MonetaryFormulas: r");
        return (WAD * BP) / reserveRatioBps;
    }

    /// @notice m = (1 + c) / (r + c)
    function multiplierWithCurrency(uint256 reserveRatioBps, uint256 currencyRatioBps) internal pure returns (uint256) {
        require(reserveRatioBps > 0 && reserveRatioBps <= BP, "MonetaryFormulas: r");
        require(currencyRatioBps <= BP, "MonetaryFormulas: c");
        uint256 num = WAD * (BP + currencyRatioBps);
        uint256 den = reserveRatioBps + currencyRatioBps;
        require(den > 0, "MonetaryFormulas: den");
        return num / den;
    }

    /// @notice Coverage ratio in bps: reserveValue / circulatingValue
    function coverageRatioBps(uint256 reserveValue, uint256 circulatingValue) internal pure returns (uint256) {
        if (circulatingValue == 0) return type(uint256).max;
        return (reserveValue * BP) / circulatingValue;
    }

    /// @notice Effective GRU layer multiplier M00→M1 (25 face units per M00)
    function gruM00ToM1Fanout() internal pure returns (uint256) {
        return 25;
    }

    /// @notice 7:10 atomic issuance factor as WAD (10/7)
    function gruAtomicIssuanceFactorWad() internal pure returns (uint256) {
        return (10 * WAD) / 7;
    }

    /// @notice v_cov = v_adj × (coverageBps / 12000)
    function coverageWeightedVelocity(uint256 vAdjWad, uint256 coverageBps) internal pure returns (uint256) {
        return (vAdjWad * coverageBps) / 12_000;
    }
}
