// File: @openzeppelin/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

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/utils/math/Math.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

// File: @openzeppelin/contracts/utils/Strings.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

// File: contracts/interfaces/IParams.sol



// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.15;

interface IParams {
  function getCheckPosition() external view returns (string memory checkPositition);
  function getColors() external view returns (string[] memory colors);
  function getNogglePaths() external view returns (string[] memory nogglePaths);
  function getGridPositions() external view returns (string[] memory gridPositions);
}
// File: contracts/library/Base64.sol



// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @title Base64
/// @author Brecht Devos - <brecht@loopring.org>
/// @notice Provides a function for encoding some bytes in base64
library Base64 {
    string internal constant TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    function encode(string memory _data) internal pure returns (string memory) {
        return encode(bytes(_data));
    }

    function encode(bytes memory _data) internal pure returns (string memory) {
        if (_data.length == 0) return "";

        // load the table into memory
        string memory table = TABLE;

        // multiply by 4/3 rounded up
        uint256 encodedLen = 4 * ((_data.length + 2) / 3);

        // add some extra buffer at the end required for the writing
        string memory result = new string(encodedLen + 32);

        assembly {
            // set the actual output length
            mstore(result, encodedLen)

            // prepare the lookup table
            let tablePtr := add(table, 1)

            // input ptr
            let dataPtr := _data
            let endPtr := add(dataPtr, mload(_data))

            // result ptr, jump over length
            let resultPtr := add(result, 32)

            // run over the input, 3 bytes at a time
            for {

            } lt(dataPtr, endPtr) {

            } {
                dataPtr := add(dataPtr, 3)

                // read 3 bytes
                let input := mload(dataPtr)

                // write 4 characters
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(18, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(12, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(shr(6, input), 0x3F)))))
                resultPtr := add(resultPtr, 1)
                mstore(resultPtr, shl(248, mload(add(tablePtr, and(input, 0x3F)))))
                resultPtr := add(resultPtr, 1)
            }

            // padding with '='
            switch mod(mload(_data), 3)
            case 1 {
                mstore(sub(resultPtr, 2), shl(240, 0x3d3d))
            }
            case 2 {
                mstore(sub(resultPtr, 1), shl(248, 0x3d))
            }
        }

        return result;
    }
}
// File: contracts/library/Builder.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "./ColorLib.sol";

library Builder {  
  function buildPixelNoggles(bytes[5] memory colors, string[] memory gridPositions, uint256 tokenId) internal pure returns (string memory) {
        bytes memory list;
        for (uint i = 0; i < gridPositions.length; ++i) {
            uint256 randomColor = uint256(tokenId * 77 * i) % 4;
            list = abi.encodePacked(
                list,
                string(
                    abi.encodePacked(
                    '<rect width="29.6" height="29.6" ',
                    gridPositions[i],
                    ' fill="',
                    colors[randomColor],
                    '" />')));
        }
        return string( 
            abi.encodePacked(
                
                list
            )
        );
    }

    function buildNoggles(uint256 tokenId, string memory nogglesColor, uint256 noggleTypeIndex, string[] memory checkColors, string[] memory gridPositions, string[] memory nogglePaths, uint staticBlockNum) internal pure returns (string memory) {
        string memory blendingMode = "";
        string memory noggles = "";
        string memory shapeRendering = "";
        bytes[5] memory colors = ColorLib.gradientFromHex(nogglesColor);
        if (keccak256(abi.encodePacked(nogglesColor)) == keccak256(abi.encodePacked("ffffff")) || random(tokenId, checkColors.length - 1, staticBlockNum * 999) < (checkColors.length / 3)) {
            blendingMode = 'style="mix-blend-mode:screen"';
        }

        if (noggleTypeIndex < 2) {
            noggles = string( 
                abi.encodePacked('<path ',
                        nogglePaths[noggleTypeIndex],
                    ' />'));
        }

        if (noggleTypeIndex == 2) {
            string memory pixelNoggles = buildPixelNoggles(colors, gridPositions, tokenId);
            noggles = pixelNoggles;
            shapeRendering = 'shape-rendering="crispEdges" ';
        }        

        return string( 
            abi.encodePacked(
                
                '<g ', 
                    shapeRendering,
                    blendingMode,
                    ' fill="',
                    colors[2],
                    '">',
                    '<path fill="',
                    colors[2],
                    '" d="M56.9 150.159H42.1v14.8h14.8v-14.8Zm0 14.8H42.1v14.8h14.8v-14.8Zm29.74-29.46-44.54-.14v14.8l44.54.14v-14.8Zm103.76-.14h-15.36v14.8h15.36v-14.8Z" />',
                    noggles,
                    '</g>'
            )
        );
    }

    function random(uint256 input, uint256 max, uint256 randomNum) internal pure returns (uint256) {
        return (uint256(keccak256(abi.encodePacked((input * randomNum) + (input * max) + input))) % max);
    }
}
// File: contracts/library/ColorLib.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;
import "./StringUtils.sol";

/**
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BBB#RROOOOOOOOOOOOOOORR#BBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BBROOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZOORBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BB#ROOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZORB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@B#ROOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZORB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@BBRRROOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZO#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@B#RRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZRB@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@B#RRRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOB@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@B#RRRRRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOB@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@B#RRRRRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZRB@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@B###RRRRRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ#@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@BB####RRRRRRRROOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZO@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@BB#####RRRRRRRROOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOB@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@BB######RRRRRRRROOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZO#@@@@@@@@@@@@@@@
@@@@@@@@@@@@BBB######RRRRRRRROOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZO#@@@@@@@@@@@@@@
@@@@@@@@@@@BBBBB#####RRRRRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZR@@@@@@@@@@@@@
@@@@@@@@@@BBBBBB#####RRRRRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZO#@@@@@@@@@@@@
@@@@@@@@@BBBBBBB#####RRRRRRRRROOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOB@@@@@@@@@@@
@@@@@@@@BBBBBBBB######RRRRRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOB@@@@@@@@@@
@@@@@@@@BBBBBBBBB#####RRRRRRRRROOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOR@@@@@@@@@@
@@@@@@@BBBBBBBBBB######RRRRRRRROOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOB@@@@@@@@@
@@@@@@@BBBBBBBBBBB#####RRRRRRRRROOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOOOR@@@@@@@@@
@@@@@@@BBBBBBBBBBB######RRRRRRRRROOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOOOOOB@@@@@@@@
@@@@@@BBBBBBBBBBBBB######RRRRRRRRROOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOOOOOOB@@@@@@@@
@@@@@@BBBBBBBBBBBBBB######RRRRRRRRROOOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOOOOOOOO#@@@@@@@@
@@@@@@BBBBBBBBBBBBBBB######RRRRRRRRROOOOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOOOOOOOOOO#@@@@@@@@
@@@@@@BBBBBBBBBBBBBBB######RRRRRRRRRROOOOOOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOOOOOOOOOOOOR@@@@@@@@
@@@@@@BBBBBBBBBBBBBBBB#######RRRRRRRRRROOOOOOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOOOOOOOOOOOOOOO#@@@@@@@@
@@@@@@BBBBBBBBBBBBBBBBB#######RRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZOOOOOOOOOOOOOOOOOOOO#@@@@@@@@
@@@@@@BBBBBBBBBBBBBBBBBBB######RRRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOOOOZZZZZZZZZZZZZZZZZZZZOOOOOOOOOOOOOOOOOOOOOOOOORB@@@@@@@@
@@@@@@BBBBBBBBBBBBBBBBBBBB#######RRRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORB@@@@@@@@
@@@@@@@BBBBBBBBBBBBBBBBBBBBB#######RRRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORRRR@@@@@@@@@
@@@@@@@BBBBBBBBBBBBBBBBBBBBBB########RRRRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORRRRRB@@@@@@@@@
@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBB########RRRRRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORRRRRRR#@@@@@@@@@@
@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBB########RRRRRRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORRRRRRRRRRB@@@@@@@@@@
@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBB########RRRRRRRRRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOORRRRRRRRRRRRRRB@@@@@@@@@@@
@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBB#########RRRRRRRRRRRRRRRRRRROOOOOOOOOOOOOOOOOOOOOOOOOORRRRRRRRRRRRRRRRRR##@@@@@@@@@@@@
@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBB#########RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR###B@@@@@@@@@@@@
@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB###########RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR######B@@@@@@@@@@@@@
@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB#############RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR########BB@@@@@@@@@@@@@@
@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB###############RRRRRRRRRRRRRRRRRRRRRRRRRRR#############BB@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB#################################################BBB@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB#######################################BBBBBBB@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB########################BBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BBBBBBBBBBBBBBBBBBBBBBBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
*/

/// Color lib is a custom library for handling the math functions required to generate the gradient step colors
/// Originally written in javascript, this is a solidity port.
library ColorLib {
    using StringUtils for string;
    struct HSL {
        uint256 h;
        uint256 s;
        uint256 l;
    }

    /// Lookup table for cubicinout range 0-99
    function cubicInOut(uint16 p) internal pure returns (int256) {
        if (p < 13) {
            return 0;
        }
        if (p < 17) {
            return 1;
        }
        if (p < 19) {
            return 2;
        }
        if (p < 21) {
            return 3;
        }
        if (p < 23) {
            return 4;
        }
        if (p < 24) {
            return 5;
        }
        if (p < 25) {
            return 6;
        }
        if (p < 27) {
            return 7;
        }
        if (p < 28) {
            return 8;
        }
        if (p < 29) {
            return 9;
        }
        if (p < 30) {
            return 10;
        }
        if (p < 31) {
            return 11;
        }
        if (p < 32) {
            return 13;
        }
        if (p < 33) {
            return 14;
        }
        if (p < 34) {
            return 15;
        }
        if (p < 35) {
            return 17;
        }
        if (p < 36) {
            return 18;
        }
        if (p < 37) {
            return 20;
        }
        if (p < 38) {
            return 21;
        }
        if (p < 39) {
            return 23;
        }
        if (p < 40) {
            return 25;
        }
        if (p < 41) {
            return 27;
        }
        if (p < 42) {
            return 29;
        }
        if (p < 43) {
            return 31;
        }
        if (p < 44) {
            return 34;
        }
        if (p < 45) {
            return 36;
        }
        if (p < 46) {
            return 38;
        }
        if (p < 47) {
            return 41;
        }
        if (p < 48) {
            return 44;
        }
        if (p < 49) {
            return 47;
        }
        if (p < 50) {
            return 50;
        }
        if (p < 51) {
            return 52;
        }
        if (p < 52) {
            return 55;
        }
        if (p < 53) {
            return 58;
        }
        if (p < 54) {
            return 61;
        }
        if (p < 55) {
            return 63;
        }
        if (p < 56) {
            return 65;
        }
        if (p < 57) {
            return 68;
        }
        if (p < 58) {
            return 70;
        }
        if (p < 59) {
            return 72;
        }
        if (p < 60) {
            return 74;
        }
        if (p < 61) {
            return 76;
        }
        if (p < 62) {
            return 78;
        }
        if (p < 63) {
            return 79;
        }
        if (p < 64) {
            return 81;
        }
        if (p < 65) {
            return 82;
        }
        if (p < 66) {
            return 84;
        }
        if (p < 67) {
            return 85;
        }
        if (p < 68) {
            return 86;
        }
        if (p < 69) {
            return 88;
        }
        if (p < 70) {
            return 89;
        }
        if (p < 71) {
            return 90;
        }
        if (p < 72) {
            return 91;
        }
        if (p < 74) {
            return 92;
        }
        if (p < 75) {
            return 93;
        }
        if (p < 76) {
            return 94;
        }
        if (p < 78) {
            return 95;
        }
        if (p < 80) {
            return 96;
        }
        if (p < 82) {
            return 97;
        }
        if (p < 86) {
            return 98;
        }
        return 99;
    }

    /// Lookup table for cubicid range 0-99
    function cubicIn(uint256 p) internal pure returns (uint8) {
        if (p < 22) {
            return 0;
        }
        if (p < 28) {
            return 1;
        }
        if (p < 32) {
            return 2;
        }
        if (p < 32) {
            return 3;
        }
        if (p < 34) {
            return 3;
        }
        if (p < 36) {
            return 4;
        }
        if (p < 39) {
            return 5;
        }
        if (p < 41) {
            return 6;
        }
        if (p < 43) {
            return 7;
        }
        if (p < 46) {
            return 9;
        }
        if (p < 47) {
            return 10;
        }
        if (p < 49) {
            return 11;
        }
        if (p < 50) {
            return 12;
        }
        if (p < 51) {
            return 13;
        }
        if (p < 53) {
            return 14;
        }
        if (p < 54) {
            return 15;
        }
        if (p < 55) {
            return 16;
        }
        if (p < 56) {
            return 17;
        }
        if (p < 57) {
            return 18;
        }
        if (p < 58) {
            return 19;
        }
        if (p < 59) {
            return 20;
        }
        if (p < 60) {
            return 21;
        }
        if (p < 61) {
            return 22;
        }
        if (p < 62) {
            return 23;
        }
        if (p < 63) {
            return 25;
        }
        if (p < 64) {
            return 26;
        }
        if (p < 65) {
            return 27;
        }
        if (p < 66) {
            return 28;
        }
        if (p < 67) {
            return 30;
        }
        if (p < 68) {
            return 31;
        }
        if (p < 69) {
            return 32;
        }
        if (p < 70) {
            return 34;
        }
        if (p < 71) {
            return 35;
        }
        if (p < 72) {
            return 37;
        }
        if (p < 73) {
            return 38;
        }
        if (p < 74) {
            return 40;
        }
        if (p < 75) {
            return 42;
        }
        if (p < 76) {
            return 43;
        }
        if (p < 77) {
            return 45;
        }
        if (p < 78) {
            return 47;
        }
        if (p < 79) {
            return 49;
        }
        if (p < 80) {
            return 51;
        }
        if (p < 81) {
            return 53;
        }
        if (p < 82) {
            return 55;
        }
        if (p < 83) {
            return 57;
        }
        if (p < 84) {
            return 59;
        }
        if (p < 85) {
            return 61;
        }
        if (p < 86) {
            return 63;
        }
        if (p < 87) {
            return 65;
        }
        if (p < 88) {
            return 68;
        }
        if (p < 89) {
            return 70;
        }
        if (p < 90) {
            return 72;
        }
        if (p < 91) {
            return 75;
        }
        if (p < 92) {
            return 77;
        }
        if (p < 93) {
            return 80;
        }
        if (p < 94) {
            return 83;
        }
        if (p < 95) {
            return 85;
        }
        if (p < 96) {
            return 88;
        }
        if (p < 97) {
            return 91;
        }
        if (p < 98) {
            return 94;
        }
        return 97;
    }

    /// Lookup table for quintin range 0-99
    function quintIn(uint256 p) internal pure returns (uint8) {
        if (p < 39) {
            return 0;
        }
        if (p < 45) {
            return 1;
        }
        if (p < 49) {
            return 2;
        }
        if (p < 52) {
            return 3;
        }
        if (p < 53) {
            return 4;
        }
        if (p < 54) {
            return 4;
        }
        if (p < 55) {
            return 5;
        }
        if (p < 56) {
            return 5;
        }
        if (p < 57) {
            return 6;
        }
        if (p < 58) {
            return 6;
        }
        if (p < 59) {
            return 7;
        }
        if (p < 60) {
            return 7;
        }
        if (p < 61) {
            return 8;
        }
        if (p < 62) {
            return 9;
        }
        if (p < 63) {
            return 9;
        }
        if (p < 64) {
            return 10;
        }
        if (p < 65) {
            return 11;
        }
        if (p < 66) {
            return 12;
        }
        if (p < 67) {
            return 13;
        }
        if (p < 68) {
            return 14;
        }
        if (p < 69) {
            return 15;
        }
        if (p < 70) {
            return 16;
        }
        if (p < 71) {
            return 18;
        }
        if (p < 72) {
            return 19;
        }
        if (p < 73) {
            return 20;
        }
        if (p < 74) {
            return 22;
        }
        if (p < 75) {
            return 23;
        }
        if (p < 76) {
            return 25;
        }
        if (p < 77) {
            return 27;
        }
        if (p < 78) {
            return 28;
        }
        if (p < 79) {
            return 30;
        }
        if (p < 80) {
            return 32;
        }
        if (p < 81) {
            return 34;
        }
        if (p < 82) {
            return 37;
        }
        if (p < 83) {
            return 39;
        }
        if (p < 84) {
            return 41;
        }
        if (p < 85) {
            return 44;
        }
        if (p < 86) {
            return 47;
        }
        if (p < 87) {
            return 49;
        }
        if (p < 88) {
            return 52;
        }
        if (p < 89) {
            return 55;
        }
        if (p < 90) {
            return 59;
        }
        if (p < 91) {
            return 62;
        }
        if (p < 92) {
            return 65;
        }
        if (p < 93) {
            return 69;
        }
        if (p < 94) {
            return 73;
        }
        if (p < 95) {
            return 77;
        }
        if (p < 96) {
            return 81;
        }
        if (p < 97) {
            return 85;
        }
        if (p < 98) {
            return 90;
        }
        return 95;
    }

    // Util for keeping hue range in 0-360 positive
    function clampHue(int256 h) internal pure returns (uint256) {
        unchecked {
            h /= 100;
            if (h >= 0) {
                return uint256(h) % 360;
            } else {
                return (uint256(-1 * h) % 360);
            }
        }
    }

    /// find hue within range
    function lerpHue(
        uint8 optionNum,
        uint256 direction,
        uint256 uhue,
        uint8 pct
    ) internal pure returns (uint256) {
        // unchecked {
        uint256 option = optionNum % 4;
        int256 hue = int256(uhue);

        if (option == 0) {
            return
                clampHue(
                    (((100 - int256(uint256(pct))) * hue) +
                        (int256(uint256(pct)) *
                            (direction == 0 ? hue - 10 : hue + 10)))
                );
        }
        if (option == 1) {
            return
                clampHue(
                    (((100 - int256(uint256(pct))) * hue) +
                        (int256(uint256(pct)) *
                            (direction == 0 ? hue - 30 : hue + 30)))
                );
        }
        if (option == 2) {
            return
                clampHue(
                    (
                        (((100 - cubicInOut(pct)) * hue) +
                            (cubicInOut(pct) *
                                (direction == 0 ? hue - 50 : hue + 50)))
                    )
                );
        }

        return
            clampHue(
                ((100 - cubicInOut(pct)) * hue) +
                    (cubicInOut(pct) *
                        int256(
                            hue +
                                ((direction == 0 ? int256(-60) : int256(60)) *
                                    int256(uint256(optionNum > 128 ? 1 : 0))) +
                                30
                        ))
            );
        // }
    }

    /// find lightness within range
    function lerpLightness(
        uint8 optionNum,
        uint256 start,
        uint256 end,
        uint256 pct
    ) internal pure returns (uint256) {
        uint256 lerpPercent;
        if (optionNum == 0) {
            lerpPercent = quintIn(pct);
        } else {
            lerpPercent = cubicIn(pct);
        }
        return
            1 + (((100.0 - lerpPercent) * start + (lerpPercent * end)) / 100);
    }

    /// find saturation within range
    function lerpSaturation(
        uint8 optionNum,
        uint256 start,
        uint256 end,
        uint256 pct
    ) internal pure returns (uint256) {
        unchecked {
            uint256 lerpPercent;
            if (optionNum == 0) {
                lerpPercent = quintIn(pct);
                return
                    1 +
                    (((100.0 - lerpPercent) * start + lerpPercent * end) / 100);
            }
            lerpPercent = pct;
            return ((100.0 - lerpPercent) * start + lerpPercent * end) / 100;
        }
    }

    /// encode a color string
    function encodeStr(
        uint256 h,
        uint256 s,
        uint256 l
    ) internal pure returns (bytes memory) {
        return
            abi.encodePacked(
                "hsl(",
                StringUtils.toString(h),
                ", ",
                StringUtils.toString(s),
                "%, ",
                StringUtils.toString(l),
                "%)"
            );
    }

    

function st2num(string memory numString) public pure returns(uint) {
    uint  val=0;
    bytes   memory stringBytes = bytes(numString);
    for (uint  i =  0; i<stringBytes.length; i++) {
        uint exp = stringBytes.length - i;
        bytes1 ival = stringBytes[i];
        uint8 uval = uint8(ival);
        uint jval = uval - uint(0x30);

        val +=  (uint(jval) * (10**(exp-1))); 
    }
    return val;
}
function gradientFromHex(
        string memory color
    ) internal pure returns (bytes[5] memory) {
        unchecked {
            // bytes32 addrBytes = bytes32(uint256(uint160(st2num(color))));
            bytes32 addrBytes = bytes32(uint256(keccak256(abi.encodePacked(color))));
            uint256 startHue = (uint256(uint8(addrBytes[31 - 12])) * 24) / 17; // 255 - 360
            uint256 startLightness = (uint256(uint8(addrBytes[31 - 2])) * 5) /
                34 +
                32; // 255 => 37.5 + 32 (32, 69.5)
            uint256 endLightness = 97;
            endLightness += (((uint256(uint8(addrBytes[31 - 8])) * 5) / 51) +
                72); // 72-97
            endLightness /= 2;

            uint256 startSaturation = uint256(uint8(addrBytes[31 - 7])) /
                16 +
                81; // 0-16 + 72

            uint256 endSaturation = uint256(uint8(addrBytes[31 - 10]) * 11) /
                128 +
                70; // 0-22 + 70
            if (endSaturation > startSaturation - 10) {
                endSaturation = startSaturation - 10;
            }

            return [
                // 0
                encodeStr(
                    lerpHue(
                        uint8(addrBytes[31 - 3]),
                        uint8(addrBytes[31 - 6]) % 2,
                        startHue,
                        0
                    ),
                    lerpSaturation(
                        uint8(addrBytes[31 - 3]) % 2,
                        startSaturation,
                        endSaturation,
                        100
                    ),
                    lerpLightness(
                        uint8(addrBytes[31 - 5]) % 2,
                        startLightness,
                        endLightness,
                        100
                    )
                ),
                // 1
                encodeStr(
                    lerpHue(
                        uint8(addrBytes[31 - 3]),
                        uint8(addrBytes[31 - 6]) % 2,
                        startHue,
                        10
                    ),
                    lerpSaturation(
                        uint8(addrBytes[31 - 3]) % 2,
                        startSaturation,
                        endSaturation,
                        90
                    ),
                    lerpLightness(
                        uint8(addrBytes[31 - 5]) % 2,
                        startLightness,
                        endLightness,
                        90
                    )
                ),
                // 2
                encodeStr(
                    lerpHue(
                        uint8(addrBytes[31 - 3]),
                        uint8(addrBytes[31 - 6]) % 2,
                        startHue,
                        70
                    ),
                    lerpSaturation(
                        uint8(addrBytes[31 - 3]) % 2,
                        startSaturation,
                        endSaturation,
                        70
                    ),
                    lerpLightness(
                        uint8(addrBytes[31 - 5]) % 2,
                        startLightness,
                        endLightness,
                        70
                    )
                ),
                // 3
                encodeStr(
                    lerpHue(
                        uint8(addrBytes[31 - 3]),
                        uint8(addrBytes[31 - 6]) % 2,
                        startHue,
                        90
                    ),
                    lerpSaturation(
                        uint8(addrBytes[31 - 3]) % 2,
                        startSaturation,
                        endSaturation,
                        20
                    ),
                    lerpLightness(
                        uint8(addrBytes[31 - 5]) % 2,
                        startLightness,
                        endLightness,
                        20
                    )
                ),
                // 4
                encodeStr(
                    lerpHue(
                        uint8(addrBytes[31 - 3]),
                        uint8(addrBytes[31 - 6]) % 2,
                        startHue,
                        100
                    ),
                    lerpSaturation(
                        uint8(addrBytes[31 - 3]) % 2,
                        startSaturation,
                        endSaturation,
                        0
                    ),
                    startLightness
                )
            ];
        }
    }
}

// File: contracts/library/StringUtils.sol



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

/**
 * Strings Library
 *
 * In summary this is a simple library of string functions which make simple
 * string operations less tedious in solidity.
 *
 * Please be aware these functions can be quite gas heavy so use them only when
 * necessary not to clog the blockchain with expensive transactions.
 *
 * @author James Lockhart <james@n3tw0rk.co.uk>
 */

library StringUtils {
    function toString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }

    /**
     * Concat (High gas cost)
     *
     * Appends two strings together and returns a new value
     *
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string which will be the concatenated
     *              prefix
     * @param _value The value to be the concatenated suffix
     * @return string The resulting string from combinging the base and value
     */
    function concat(
        string memory _base,
        string memory _value
    ) internal pure returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        assert(_valueBytes.length > 0);

        string memory _tmpValue = new string(
            _baseBytes.length + _valueBytes.length
        );
        bytes memory _newValue = bytes(_tmpValue);

        uint i;
        uint j;

        for (i = 0; i < _baseBytes.length; i++) {
            _newValue[j++] = _baseBytes[i];
        }

        for (i = 0; i < _valueBytes.length; i++) {
            _newValue[j++] = _valueBytes[i];
        }

        return string(_newValue);
    }

    /**
     * Index Of
     *
     * Locates and returns the position of a character within a string
     *
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string acting as the haystack to be
     *              searched
     * @param _value The needle to search for, at present this is currently
     *               limited to one character
     * @return int The position of the needle starting from 0 and returning -1
     *             in the case of no matches found
     */
    function indexOf(
        string memory _base,
        string memory _value
    ) internal pure returns (int) {
        return _indexOf(_base, _value, 0);
    }

    /**
     * Index Of
     *
     * Locates and returns the position of a character within a string starting
     * from a defined offset
     *
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string acting as the haystack to be
     *              searched
     * @param _value The needle to search for, at present this is currently
     *               limited to one character
     * @param _offset The starting point to start searching from which can start
     *                from 0, but must not exceed the length of the string
     * @return int The position of the needle starting from 0 and returning -1
     *             in the case of no matches found
     */
    function _indexOf(
        string memory _base,
        string memory _value,
        uint _offset
    ) internal pure returns (int) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        assert(_valueBytes.length == 1);

        for (uint i = _offset; i < _baseBytes.length; i++) {
            if (_baseBytes[i] == _valueBytes[0]) {
                return int(i);
            }
        }

        return -1;
    }

    /**
     * Length
     *
     * Returns the length of the specified string
     *
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string to be measured
     * @return uint The length of the passed string
     */
    function length(string memory _base) internal pure returns (uint) {
        bytes memory _baseBytes = bytes(_base);
        return _baseBytes.length;
    }

    /**
     * Sub String
     *
     * Extracts the beginning part of a string based on the desired length
     *
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string that will be used for
     *              extracting the sub string from
     * @param _length The length of the sub string to be extracted from the base
     * @return string The extracted sub string
     */
    function substring(
        string memory _base,
        int _length
    ) internal pure returns (string memory) {
        return _substring(_base, _length, 0);
    }

    /**
     * Sub String
     *
     * Extracts the part of a string based on the desired length and offset. The
     * offset and length must not exceed the lenth of the base string.
     *
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string that will be used for
     *              extracting the sub string from
     * @param _length The length of the sub string to be extracted from the base
     * @param _offset The starting point to extract the sub string from
     * @return string The extracted sub string
     */
    function _substring(
        string memory _base,
        int _length,
        int _offset
    ) internal pure returns (string memory) {
        bytes memory _baseBytes = bytes(_base);

        assert(uint(_offset + _length) <= _baseBytes.length);

        string memory _tmp = new string(uint(_length));
        bytes memory _tmpBytes = bytes(_tmp);

        uint j = 0;
        for (uint i = uint(_offset); i < uint(_offset + _length); i++) {
            _tmpBytes[j++] = _baseBytes[i];
        }

        return string(_tmpBytes);
    }

    function split(
        string memory _base,
        string memory _value
    ) internal pure returns (string[] memory splitArr) {
        bytes memory _baseBytes = bytes(_base);

        uint _offset = 0;
        uint _splitsCount = 1;
        while (_offset < _baseBytes.length - 1) {
            int _limit = _indexOf(_base, _value, _offset);
            if (_limit == -1) break;
            else {
                _splitsCount++;
                _offset = uint(_limit) + 1;
            }
        }

        splitArr = new string[](_splitsCount);

        _offset = 0;
        _splitsCount = 0;
        while (_offset < _baseBytes.length - 1) {
            int _limit = _indexOf(_base, _value, _offset);
            if (_limit == -1) {
                _limit = int(_baseBytes.length);
            }

            string memory _tmp = new string(uint(_limit) - _offset);
            bytes memory _tmpBytes = bytes(_tmp);

            uint j = 0;
            for (uint i = _offset; i < uint(_limit); i++) {
                _tmpBytes[j++] = _baseBytes[i];
            }
            _offset = uint(_limit) + 1;
            splitArr[_splitsCount++] = string(_tmpBytes);
        }
        return splitArr;
    }

    /**
     * Compare To
     *
     * Compares the characters of two strings, to ensure that they have an
     * identical footprint
     *
     * @param _base When being used for a data type this is the extended object
     *               otherwise this is the string base to compare against
     * @param _value The string the base is being compared to
     * @return bool Simply notates if the two string have an equivalent
     */
    function compareTo(
        string memory _base,
        string memory _value
    ) internal pure returns (bool) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        if (_baseBytes.length != _valueBytes.length) {
            return false;
        }

        for (uint i = 0; i < _baseBytes.length; i++) {
            if (_baseBytes[i] != _valueBytes[i]) {
                return false;
            }
        }

        return true;
    }

    /**
     * Compare To Ignore Case (High gas cost)
     *
     * Compares the characters of two strings, converting them to the same case
     * where applicable to alphabetic characters to distinguish if the values
     * match.
     *
     * @param _base When being used for a data type this is the extended object
     *               otherwise this is the string base to compare against
     * @param _value The string the base is being compared to
     * @return bool Simply notates if the two string have an equivalent value
     *              discarding case
     */
    function compareToIgnoreCase(
        string memory _base,
        string memory _value
    ) internal pure returns (bool) {
        bytes memory _baseBytes = bytes(_base);
        bytes memory _valueBytes = bytes(_value);

        if (_baseBytes.length != _valueBytes.length) {
            return false;
        }

        for (uint i = 0; i < _baseBytes.length; i++) {
            if (
                _baseBytes[i] != _valueBytes[i] &&
                _upper(_baseBytes[i]) != _upper(_valueBytes[i])
            ) {
                return false;
            }
        }

        return true;
    }

    /**
     * Upper
     *
     * Converts all the values of a string to their corresponding upper case
     * value.
     *
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string base to convert to upper case
     * @return string
     */
    function upper(string memory _base) internal pure returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        for (uint i = 0; i < _baseBytes.length; i++) {
            _baseBytes[i] = _upper(_baseBytes[i]);
        }
        return string(_baseBytes);
    }

    /**
     * Lower
     *
     * Converts all the values of a string to their corresponding lower case
     * value.
     *
     * @param _base When being used for a data type this is the extended object
     *              otherwise this is the string base to convert to lower case
     * @return string
     */
    function lower(string memory _base) internal pure returns (string memory) {
        bytes memory _baseBytes = bytes(_base);
        for (uint i = 0; i < _baseBytes.length; i++) {
            _baseBytes[i] = _lower(_baseBytes[i]);
        }
        return string(_baseBytes);
    }

    /**
     * Upper
     *
     * Convert an alphabetic character to upper case and return the original
     * value when not alphabetic
     *
     * @param _b1 The byte to be converted to upper case
     * @return bytes1 The converted value if the passed value was alphabetic
     *                and in a lower case otherwise returns the original value
     */
    function _upper(bytes1 _b1) private pure returns (bytes1) {
        if (_b1 >= 0x61 && _b1 <= 0x7A) {
            return bytes1(uint8(_b1) - 32);
        }

        return _b1;
    }

    /**
     * Lower
     *
     * Convert an alphabetic character to lower case and return the original
     * value when not alphabetic
     *
     * @param _b1 The byte to be converted to lower case
     * @return bytes1 The converted value if the passed value was alphabetic
     *                and in a upper case otherwise returns the original value
     */
    function _lower(bytes1 _b1) private pure returns (bytes1) {
        if (_b1 >= 0x41 && _b1 <= 0x5A) {
            return bytes1(uint8(_b1) + 32);
        }

        return _b1;
    }
}

// File: contracts/OpepenZorggles.sol



// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.15;
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "erc721a-upgradeable/contracts/interfaces/IERC721AUpgradeable.sol";
import "./library/Base64.sol";
import './Params.sol';
import './interfaces/IParams.sol';
import "./library/Builder.sol";

contract OpepenZorgglesRenderer is Ownable {
    using Strings for uint256;
    using Strings for uint160;
    using StringUtils for string;

    IParams public params;    
    IERC721AUpgradeable public tokenContract;

    /* 
    ⌐◨—————————————————————————————————————————————————————————————◨
                                set state
    ⌐◨—————————————————————————————————————————————————————————————◨ */
    address private _owner;
    string public description = "opepfp loading...";
    string public name = "Opepen Zorggles";
    uint256 staticBlockNum = 16799960;
    string public contractImage = "ipfs://QmfZj3LDCV1aCwerTj42wgbaPk4iY6aUe6MgYvM3Sayq8u";
    string public animationUrl = "";
    string public sellerFeeBasisPoints = "250";
    address public sellerFeeRecipient = _owner;
    string[] internal noggleTypes = ["Circle", "Square", "Pixel"];
    event ParamsUpdated(IParams params);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {_owner = msg.sender;}

    function contractURI() public view
      returns (string memory) {   
        return string(
            abi.encodePacked(
                "data:application/json;base64,",
                Base64.encode(
                    bytes(
                        string(
                            abi.encodePacked(
                                '{"name": "',
                                name,
                                '", "description": "',
                                description,
                                '", "image": "',
                                contractImage,
                                '", "seller_fee_basis_points": "',
                                sellerFeeBasisPoints,
                                '", "seller_fee_recipient": "',
                                _owner,
                                '", "animation_url": "',
                                animationUrl,
                                '"}'
                            )
                        )
                    )
                )
            )
        );
    }

    function tokenURI(uint256 tokenId) public view returns (string memory) {   
        require (tokenContract.totalSupply() >= tokenId, "Check your head. This token does't exist.");
        return constructTokenURI(tokenId);
    }

    function constructTokenURI(
        uint256 tokenId
    ) internal view returns (string memory) {                    
        uint256 colorsLength = params.getColors().length - 1;
        uint256[4] memory tokenDNA = [
            Builder.random(tokenId, colorsLength, staticBlockNum * 555), // circle color            
            Builder.random(tokenId, colorsLength, staticBlockNum * 777), // noggle frames colors
            chooseNoggleType(tokenId), // noggle type
            Builder.random(tokenId, colorsLength, staticBlockNum * 999) // check color
        ];
        string memory image = string(
            abi.encodePacked(Base64.encode(bytes(getTokenIdSvg(tokenId, tokenDNA))))
        );
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(
                        bytes(
                            string(
                                abi.encodePacked(
                                    '{"name": "',
                                    name,
                                    ' #',
                                    abi.encodePacked(
                                        string(tokenId.toString())
                                    ),
                                    '","image": "data:image/svg+xml;base64,',
                                    abi.encodePacked(string(image)),
                                    '","attributes": ',
                                    getTokenIdMetadata(tokenDNA),
                                    '}'
                                )
                            )
                        )
                    )
                )
            );
    }

    function getTokenIdSvg(
        uint256 tokenId, 
        uint256[4] memory tokenDNA
    ) internal view returns (string memory svg) {   
        string memory noggles = Builder.buildNoggles(tokenId, params.getColors()[tokenDNA[1]], tokenDNA[2], params.getColors(), params.getGridPositions(), params.getNogglePaths(), staticBlockNum);
        return
            string(
                abi.encodePacked(
                    '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 320" width="2000" height="2000" shape-rendering="geometricPrecision"><path fill="#000" d="M0 0h320v320H0z"/>  <path stroke="#CACACA" stroke-opacity=".18" stroke-width=".411" d="M160 0v320M229.92 0v320M264.8 0v320M90.08 0v320M55.04 0v320M20 0v320M299.84 0v320M194.88 0v320M124.96 0v320M320 160H0m320-35.04H0m320 104.96H0m320 34.88H0M320 90.08H0m320-35.04H0m320-34.88H0m320 279.68H0m320-104.96H0"/>  <path fill="#',
                    // check color  
                    params.getColors()[tokenDNA[1]],
                    '" fill-rule="evenodd" d="',
                    params.getCheckPosition(),
                    '"/><path fill="#',
                    // circle color
                    params.getColors()[tokenDNA[0]],
                    '" d="M265.378 160c0-58.198-47.178-105.376-105.376-105.376-58.197 0-105.376 47.178-105.376 105.376 0 58.198 47.179 105.376 105.376 105.376 58.198 0 105.376-47.178 105.376-105.376Z"/>',
                    noggles,
                    '</svg>'
                )
            );
    }

    function getTokenIdMetadata(uint256[4] memory tokenDNA) public view returns (string memory metadata) {
        metadata = string(
            abi.encodePacked(
                '{"trait_type":"Circle color", "value":"#',
                params.getColors()[tokenDNA[0]],
                '"},',
                '{"trait_type":"Noggle color", "value":"#',
                params.getColors()[tokenDNA[1]],
                '"},',
                '{"trait_type":"Check color", "value":"#',
                params.getColors()[tokenDNA[3]],
                '"},',
                '{"trait_type":"Noggle style", "value":"',
                noggleTypes[tokenDNA[2]],
                '"}'
            )
        );

        return string(abi.encodePacked('[', metadata, ']'));
    }  

    function setParams(IParams _params) public onlyOwner {
        params = _params;
        emit ParamsUpdated(_params);
    }

    /*
    ⌐◨—————————————————————————————————————————————————————————————◨
                           utility functions
    ⌐◨—————————————————————————————————————————————————————————————◨ 
    */

    function chooseNoggleType(uint256 tokenId) internal view returns (uint256) {
        uint256 num = tokenId * staticBlockNum;       
        if (num % 9 < 3) {
            return 1;
        } if (num % 9 < 5 && num % 9 > 3) {
            return 2;
        } else {
            return 0;
        }
    }

    function setTokenContract(IERC721AUpgradeable _tokenContract) external onlyOwner {
        tokenContract = _tokenContract;
    }

    function setName(string memory _name) public {
        require(msg.sender == _owner, "Rejected: not owner");
        name = _name;
    }

    function setDescription(string memory _description) public {
        require(msg.sender == _owner, "Rejected: not owner");
        description = _description;
    }

    function setContractImage(string memory _contractImage) public {
        require(msg.sender == _owner, "Rejected: not owner");
        contractImage = _contractImage;
    }

    function setSellerFeeBasisPoints(string memory _sellerFeeBasisPoints) public {
        require(msg.sender == _owner, "Rejected: not owner");
        sellerFeeBasisPoints = _sellerFeeBasisPoints;
    }

    function setSellerFeeRecipient(address _sellerFeeRecipient) public {
        require(msg.sender == _owner, "Rejected: not owner");
        sellerFeeRecipient = _sellerFeeRecipient;
    }
}

// File: contracts/Params.sol



// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.15;

import "@openzeppelin/contracts/access/Ownable.sol";

contract Params is Ownable {
    /* 
    ⌐◨—————————————————————————————————————————————————————————————◨
                                set state
    ⌐◨—————————————————————————————————————————————————————————————◨ */

    string public checkPosition = "M266.931 50.132a2.424 2.424 0 0 0-2.072-1.163c-.877 0-1.646.465-2.071 1.163a2.433 2.433 0 0 0-2.29.642 2.428 2.428 0 0 0-.641 2.29 2.425 2.425 0 0 0-.001 4.144c-.098.404-.091.827.021 1.228a2.436 2.436 0 0 0 1.681 1.68c.401.114.824.121 1.229.022a2.422 2.422 0 0 0 2.999.98 2.43 2.43 0 0 0 1.145-.98 2.42 2.42 0 0 0 2.29-.641 2.428 2.428 0 0 0 .641-2.29 2.424 2.424 0 0 0 0-4.144 2.435 2.435 0 0 0-.021-1.228 2.435 2.435 0 0 0-1.681-1.681c-.4-.112-.823-.12-1.228-.022h-.001Zm-2.44 7.223 2.813-4.22c.349-.522-.463-1.064-.812-.542l-2.482 3.726-.846-.843c-.442-.445-1.132.244-.688.688l1.338 1.326a.483.483 0 0 0 .677-.136Z";
    string[] public colors = ['E84AA9','F2399D','DB2F96','E73E85','FF7F8E','FA5B67','E8424E','D5332F','C23532','F2281C','D41515','9D262F','DE3237','DA3321','EA3A2D','EB4429','EC7368','FF8079','FF9193','EA5B33','EB5A2A','ED7C30','EF9933','EF8C37','F18930','F09837','F9A45C','F2A43A','F2A840','F2A93C','FFB340','F2B341','FAD064','F7CA57','F6CB45','FFAB00','F4C44A','FCDE5B','F9DA4D','F9DA4A','FAE272','F9DB49','FAE663','FBEA5B','E2F24A','B5F13B','94E337','63C23C','86E48E','77E39F','83F1AE','5FCD8C','9DEFBF','2E9D9A','3EB8A1','5FC9BF','77D3DE','6AD1DE','5ABAD3','4291A8','45B2D3','81D1EC','33758D','A7DDF9','9AD9FB','2480BD','60B1F4','A4C8EE','4576D0','2E4985','3263D0','25438C','525EAA','3D43B3','322F92','4A2387','371471','3B088C','9741DA','6C31D7', '000000', '367A8F','4581EE','49788D','49A25E','7DE778','9CCF48','A0B3B7','AA2C5C','BB2891','C99C5F','D6D3CE','D97661','D97760','E04639','EB5D2D','ED6D8E','FCF153', 'F40A0A', 'FFFC34', '13EA42', '1400FF'];
    string[] public nogglePaths = ['d="M190.2 106v29.6H205h-44.4 14.8V106H86.6v29.6h14.8-59.2V180H57v-29.6h29.6v44.4h88.8v-44.4h14.8v44.4H279V106h-88.8Z"', 'd="M234.5 103.801c-20.5 0-37.8 13.3-44 31.6h-15.7c-6.2-18.4-23.5-31.6-44-31.6s-37.8 13.3-44 31.6H42.1v44.4h14.8v-29.6h27.6c0 25.6 20.8 46.4 46.4 46.4 25.6 0 46.4-20.8 46.4-46.4h10.9c0 25.6 20.8 46.4 46.4 46.4 25.6 0 46.4-20.8 46.4-46.4-.1-25.6-20.8-46.4-46.5-46.4Z"']; 
    string[] public gridPositions = ['x="86.56" y="105.76"','x="86.56" y="135.359"','x="86.56" y="164.959"','x="116" y="105.76"','x="116" y="135.359"','x="116" y="164.959"','x="145.44" y="105.76"','x="145.44" y="135.359"','x="145.44" y="164.959"','x="190.4" y="105.76"','x="190.4" y="135.359"','x="190.4" y="164.959"','x="219.84" y="105.76"','x="219.84" y="135.359"','x="219.84" y="164.959"','x="249.28" y="105.76"','x="249.28" y="135.359"','x="249.28" y="164.959"'];
    constructor() {}

    /*
    ⌐◨—————————————————————————————————————————————————————————————◨
                           utility functions
    ⌐◨—————————————————————————————————————————————————————————————◨ 
    */

    function setCheckPosition(string memory _checkPositition) external onlyOwner {
        checkPosition = _checkPositition;
    }

    function setCheckColors(string[] memory _colors) external onlyOwner {
        colors = _colors;
    }

    function setNogglePaths(string[] memory _nogglePaths) external onlyOwner {
        nogglePaths = _nogglePaths;
    }
    
    function setGridPositions(string[] memory _gridPositions) external onlyOwner {
        gridPositions = _gridPositions;
    }

    /*
    ⌐◨—————————————————————————————————————————————————————————————◨
                           getter functions
    ⌐◨—————————————————————————————————————————————————————————————◨ 
    */

    function getCheckPosition() external view returns (string memory) {
        return checkPosition;
    }

    function getColors() external view returns (string[] memory) {
        return colors;
    }

    function getNogglePaths() external view returns (string[] memory) {
        return nogglePaths;
    }

    function getGridPositions() external view returns (string[] memory) {
        return gridPositions;
    }    
}

// File: erc721a-upgradeable/contracts/IERC721AUpgradeable.sol



// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

/**
 * @dev Interface of ERC721A.
 */
interface IERC721AUpgradeable {
    /**
     * The caller must own the token or be an approved operator.
     */
    error ApprovalCallerNotOwnerNorApproved();

    /**
     * The token does not exist.
     */
    error ApprovalQueryForNonexistentToken();

    /**
     * Cannot query the balance for the zero address.
     */
    error BalanceQueryForZeroAddress();

    /**
     * Cannot mint to the zero address.
     */
    error MintToZeroAddress();

    /**
     * The quantity of tokens minted must be more than zero.
     */
    error MintZeroQuantity();

    /**
     * The token does not exist.
     */
    error OwnerQueryForNonexistentToken();

    /**
     * The caller must own the token or be an approved operator.
     */
    error TransferCallerNotOwnerNorApproved();

    /**
     * The token must be owned by `from`.
     */
    error TransferFromIncorrectOwner();

    /**
     * Cannot safely transfer to a contract that does not implement the
     * ERC721Receiver interface.
     */
    error TransferToNonERC721ReceiverImplementer();

    /**
     * Cannot transfer to the zero address.
     */
    error TransferToZeroAddress();

    /**
     * The token does not exist.
     */
    error URIQueryForNonexistentToken();

    /**
     * The `quantity` minted with ERC2309 exceeds the safety limit.
     */
    error MintERC2309QuantityExceedsLimit();

    /**
     * The `extraData` cannot be set on an unintialized ownership slot.
     */
    error OwnershipNotInitializedForExtraData();

    // =============================================================
    //                            STRUCTS
    // =============================================================

    struct TokenOwnership {
        // The address of the owner.
        address addr;
        // Stores the start time of ownership with minimal overhead for tokenomics.
        uint64 startTimestamp;
        // Whether the token has been burned.
        bool burned;
        // Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
        uint24 extraData;
    }

    // =============================================================
    //                         TOKEN COUNTERS
    // =============================================================

    /**
     * @dev Returns the total number of tokens in existence.
     * Burned tokens will reduce the count.
     * To get the total number of tokens minted, please see {_totalMinted}.
     */
    function totalSupply() external view returns (uint256);

    // =============================================================
    //                            IERC165
    // =============================================================

    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);

    // =============================================================
    //                            IERC721
    // =============================================================

    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables
     * (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in `owner`'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`,
     * checking first that contract recipients are aware of the ERC721 protocol
     * to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move
     * this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement
     * {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external payable;

    /**
     * @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom}
     * whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token
     * by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external payable;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the
     * zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external payable;

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom}
     * for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}.
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    // =============================================================
    //                        IERC721Metadata
    // =============================================================

    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the token collection symbol.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);

    // =============================================================
    //                           IERC2309
    // =============================================================

    /**
     * @dev Emitted when tokens in `fromTokenId` to `toTokenId`
     * (inclusive) is transferred from `from` to `to`, as defined in the
     * [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
     *
     * See {_mintERC2309} for more details.
     */
    event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}

// File: erc721a-upgradeable/contracts/interfaces/IERC721AUpgradeable.sol



// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs

pragma solidity ^0.8.4;

import '../IERC721AUpgradeable.sol';