// Contract: CryptoSVG
// Target function: function encode(bytes memory data) internal pure returns (string memory)
('14');

        parts[13] = generateFilterTexture('15', color, fType.surfaceScale);

        parts[14] = '</defs>';

        parts[15] = generateRadialGradient(
            'r1',
            RadialGradient(k150, k300, Strings.toString((80 * scale) / 100), false, kEmpty, kEmpty, false, true)
        );

        parts[16] = generatesStop(k0, k0, color);

        parts[17] = generatesStop(k0, '0.89', color);

        parts[18] = generatesStop('0.99', '0.9', color);

        parts[19] = generatesStop(k0, k1, color);

        parts[20] = '</radialGradient>';

        parts[21] = generateRadialGradient(
            'r2',
            RadialGradient('230', k300, Strings.toString((30 * scale) / 100), false, kEmpty, kEmpty, false, true)
        );

        parts[22] = generatesStop(k0, k0, color);

        parts[23] = generatesStop(k0, '0.89', color);

        parts[24] = generatesStop('0.99', '0.9', color);

        parts[25] = generatesStop(k0, k1, color);

        parts[26] = '</radialGradient>';

        parts[27] = generateRadialGradient('r3', RadialGradient('0.3', '0.3', '0.4', true, '0.5', '0.5', true, false));

        parts[28] = generatesStop(k1, k0, '#000000');

        parts[29] = generatesStop(k1, k1, color);

        parts[30] = '</radialGradient>';

        parts[31] = generateLinearGradient('l1', k0, k0, k1);

        parts[32] = generatesStop(k1, k0, color);

        parts[33] = generatesStop(k1, k1, '#000000');

        parts[34] = '</linearGradient>';

        parts[35] = '<rect x="0" y="0"  height="600" width="600" />';

        parts[36] = string(
            abi.encodePacked(
                '<g transform="translate(300, 300)" filter="',
                filterType == FilterType.unregular ? _url('11') : kEmpty,
                '">'
            )
        );

        parts[37] = filterType == FilterType.linearGradient
            ? generateCircle(Circle(k0, k0, 150, _url('l1'), _url(k1), k1))
            : filterType == FilterType.radialGradient
            ? generateCircle(Circle(k0, k0, 150, _url('r3'), _url(k1), k1))
            : filterType == FilterType.unregular || filterType == FilterType.pixel
            ? kEmpty
            : generateCircle(Circle(k0, k0, 150, color, _url(k1), k1));

        parts[38] = filterType == FilterType.linearGradient
            ? generateCircle(Circle(k0, k0, 150, _url('l1'), _url('2'), k1))
            : filterType == FilterType.radialGradient
            ? generateCircle(Circle(k0, k0, 150, _url('r3'), _url('2'), k1))
            : filterType == FilterType.unregular || filterType == FilterType.pixel
            ? kEmpty
            : generateCircle(Circle(k0, k0, 150, color, _url('2'), k1));

        parts[39] = filterType == FilterType.unregular
            ? generateCircle(Circle(k0, k0, 150, color, _url('11'), k1))
            : filterType == FilterType.blur
            ? generateCircle(Circle(k0, k0, 150, color, _url('3'), k1))
            : filterType == FilterType.turbulence
            ? generateCircle(Circle(k0, k0, 150, color, _url('10'), k1))
            : filterType == FilterType.pixel
            ? generateCircle(Circle(k0, k0, 150, color, _url('14'), k1))
            : filterType == FilterType.texture
            ? generateCircle(Circle(k0, k0, 150, color, _url('15'), k1))
            : filterType == FilterType.linearGradient
            ? generateCircle(Circle(k0, k0, 150, _url('l1'), kEmpty, k1))
            : filterType == FilterType.radialGradient
            ? generateCircle(Circle(k0, k0, 150, _url('r3'), kEmpty, k1))
            : generateCircle(Circle(k0, k0, 150, color, _url('9'), k1));

        parts[40] = '</g>';

        parts[41] = animateMoon(location, filterType);

        parts[42] = string(
            abi.encodePacked(
                '<g filter="',
                filterType == FilterType.unregular ? _url('11') : filterType == FilterType.pixel ? _url('14') : kEmpty,
                '">'
            )
        );

        parts[43] = animateCircle(location, Circle(k150, k300, 100, _url('r1'), '', '0.25'));

        parts[44] = animateCircle(location, Circle('225', k300, 50, _url('r2'), '', '0.25'));

        parts[45] = animateCircle(location, Circle('110', k300, 30, color, '', '0.15'));

        parts[46] = animateCircle(location, Circle('278', k300, 50, color, '', '0.1'));

        parts[47] = '</g>';

        parts[48] = string(abi.encodePacked('<g filter="', filterType == FilterType.pixel ? _url('14') : kEmpty, '">'));

        parts[49] = animateEllipse(location, Ellipase(k150, k300, 15, 35, color, _url('5'), '1'));

        parts[50] = animateEllipse(location, Ellipase(k150, k300, 25, 35, color, _url('6'), '1'));

        parts[51] = animateEllipse(location, Ellipase(k150, k300, 10, 30, color, _url('7'), '1'));

        parts[52] = '</g>';

        parts[53] = '</svg>';

        string memory output = kEmpty;
        uint256 step = 8;
        for (uint256 j; j < 6; j++) {
            output = string(
                abi.encodePacked(
                    output,
                    parts[j * step],
                    parts[j * step + 1],
                    parts[j * step + 2],
                    parts[j * step + 3],
                    parts[j * step + 4],
                    parts[j * step + 5],
                    parts[j * step + 6],
                    parts[j * step + 7]
                )
            );
        }
        output = string(abi.encodePacked(output, parts[48], parts[49], parts[50], parts[51], parts[52], parts[53]));
        return output;
    }

    function parseFilterType(FilterType ft) internal pure returns (string memory) {
        if (ft == FilterType.blur) {
            return 'blur';
        } else if (ft == FilterType.texture) {
            return 'texture';
        } else if (ft == FilterType.linearGradient) {
            return 'linear';
        } else if (ft == FilterType.radialGradient) {
            return 'radial';
        } else if (ft == FilterType.fractalNoise) {
            return 'fractal';
        } else if (ft == FilterType.turbulence) {
            return 'turbulence';
        } else if (ft == FilterType.pixel) {
            return 'pixel';
        } else if (ft == FilterType.unregular) {
            return 'unregular';
        }
        return '';
    }

    function randomWithTokenId(string memory prefix, uint256 tokenId) internal pure returns (uint256) {
        return CryptoSVGLibrary.random(string(abi.encodePacked(prefix, Strings.toString(tokenId))));
    }

    function getColor(uint256 tokenId) public pure returns (string memory) {
        uint256 rand = randomWithTokenId('color', tokenId);
        return string(abi.encodePacked('#', CryptoSVGLibrary.substring(Strings.toHexString(rand), 2, 8)));
    }

    function getType(uint256 tokenId) public pure returns (FType memory) {
        uint256 rand = randomWithTokenId('type', tokenId);
        uint256 rand1 = randomWithTokenId('baseFrequency', tokenId);
        uint256 rand2 = randomWithTokenId('numOctaves', tokenId);
        uint256 rand3 = randomWithTokenId('surfaceScale', tokenId);
        uint256 num = (rand % 100) + 1;
        uint256 num1 = (rand1 % 5) + 1;
        uint256 num2 = (rand2 % 5) + 1;
        uint256 num3 = (rand3 % 3) + 1;
        FilterType ft = FilterType.blur;
        if (num <= 25) {
            ft = FilterType.blur;
        } else if (num <= 45) {
            ft = FilterType.texture;
        } else if (num <= 60) {
            ft = FilterType.linearGradient;
        } else if (num <= 75) {
            ft = FilterType.radialGradient;
        } else if (num <= 85) {
            ft = FilterType.turbulence;
        } else if (num <= 95) {
            ft = FilterType.fractalNoise;
        } else if (num <= 98) {
            ft = FilterType.unregular;
        } else {
            ft = FilterType.pixel;
        }
        return
            FType(
                ft,
                string(abi.encodePacked('0.0', Strings.toString(num1))),
                Strings.toString(num2),
                Strings.toString(num3)
            );
    }

    function getStartingLocation(uint256 tokenId) public pure returns (uint256) {
        return randomWithTokenId('start', tokenId) % 901;
    }

    function getSpeed(uint256 tokenId) public pure returns (uint256, uint256) {
        uint256 rand = randomWithTokenId('speed', tokenId);
        uint256 speed = 1;
        uint256 num = (randomWithTokenId('level', tokenId) % 100) + 1;
        uint256 level = 6;
        if (num <= 25) {
            speed = (rand % (999 - 512)) + 513;
            level = 6;
        } else if (num <= 50) {
            speed = (rand % (512 - 256)) + 257;
            level = 5;
        } else if (num <= 75) {
            speed = (rand % (256 - 128)) + 129;
            level = 4;
        } else if (num <= 85) {
            speed = (rand % (128 - 64)) + 65;
            level = 3;
        } else if (num <= 95) {
            speed = (rand % (64 - 16)) + 17;
            level = 2;
        } else if (num <= 98) {
            speed = (rand % 16) + 1;
            level = 1;
        } else {
            speed = (rand % 9000) + 1000;
            level = 0;
        }
        return (speed, level);
    }

    function getPeriod(uint256 tokenId) public pure returns (uint256) {
        (uint256 speed, ) = getSpeed(tokenId);
        return _getPeriod(speed);
    }

    function _getPeriod(uint256 speed) internal pure returns (uint256) {
        return 900 * speed;
    }

    function packAttribute(
        string memory color,
        FType memory ft,
        uint256 level,
        uint256 period,
        uint256 startingLocation
    ) internal pure returns (string memory) {
        return
            string(
                abi.encodePacked(
                    '{"color": "',
                    string(
                        abi.encodePacked(
                            color,
                            '","type":"',
                            parseFilterType(ft.filterType),
                            '","speedLevel":"',
                            Strings.toString(level),
                            '","period":"',
                            string(abi.encodePacked(Strings.toString(period), ' block')),
                            '","startingLocation":"',
                            Strings.toString(startingLocation)
                        )
                    ),
                    '"}'
                )
            );
    }

    function _tokenURI(
        uint256 tokenId,
        FType memory ft,
        string memory color,
        uint256 location,
        uint256 startingLocation,
        uint256 speed,
        uint256 level
    ) internal pure returns (string memory) {
        int256 _location = moonLocation(location, startingLocation, speed);
        string memory json = Base64.encode(
            bytes(
                string(
                    abi.encodePacked(
                        '{"name": "SVG #',
                        Strings.toString(tokenId),
                        '", "description": "", "image": "data:image/svg+xml;base64,',
                        Base64.encode(bytes(generatePlant(color, _location, ft))),
                        '","attributes": ',
                        packAttribute(color, ft, level, _getPeriod(speed), startingLocation),
                        '}'
                    )
                )
            )
        );
        return string(abi.encodePacked('data:application/json;base64,', json));
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        (uint256 speed, uint256 level) = getSpeed(tokenId);
        return
            _tokenURI(
                tokenId,
                getType(tokenId),
                getColor(tokenId),
                block.number,
                getStartingLocation(tokenId),
                speed,
                level
            );
    }

    // Private and Internal functions

    /**
     * @dev Returns a random available token to be minted
     *
     * Code used as reference:
     * https://github.com/1001-digital/erc721-extensions/blob/main/contracts/RandomlyAssigned.sol
     */
    function _getTokenToBeMinted(uint16 _totalMintedTokens) private returns (uint16) {
        uint16 maxIndex = uint16(maxSupply - totalSupply());
        uint16 random = CryptoSVGLibrary.getRandomNumber(maxIndex, _totalMintedTokens);

        uint16 tokenId = tokenMatrix[random];
        if (tokenMatrix[random] == 0) {
            tokenId = random;
        }

        tokenMatrix[maxIndex - 1] == 0 ? tokenMatrix[random] = maxIndex - 1 : tokenMatrix[random] = tokenMatrix[
            maxIndex - 1
        ];

        return tokenId;
    }

    function mint(uint256 amount) public payable {
        require(active, 'No active');
        require(amount <= maxMintAmount, 'Exceed limit');
        require(msg.value >= price * amount, 'Insufficient ether provided');
        uint256 supply = totalSupply();
        require(supply + amount <= maxSupply, 'Exceed amount');

        uint16 tmpTotalMintedTokens = uint16(supply);
        for (uint256 i; i < amount; i++) {
            _safeMint(_msgSender(), _getTokenToBeMinted(tmpTotalMintedTokens));
            tmpTotalMintedTokens++;
        }
    }

    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, 'No balance');

        require(payable(msg.sender).send(balance));
    }

    function setActive(bool _active) public onlyOwner {
        active = _active;
    }

    constructor() ERC721('CryptoSVG', 'CRYPTOSVG') Ownable() {}
}

library CryptoSVGLibrary {
    function substring(
        string memory str,
        uint256 startIndex,
        uint256 endIndex
    ) internal pure returns (string memory) {
        bytes memory strBytes = bytes(str);
        bytes memory result = new bytes(endIndex - startIndex);
        for (uint256 i = startIndex; i < endIndex; i++) {
            result[i - startIndex] = strBytes[i];
        }
        return string(result);
    }

    function random(string memory input) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(input)));
    }

    /**
     * @dev Generates a pseudo-random number.
     */
    function getRandomNumber(uint16 _upper, uint16 _totalMintedTokens) internal view returns (uint16) {
        uint16 rand = uint16(
            random(
                string(
                    abi.encodePacked(
                        _totalMintedTokens,
                        blockhash(block.number - 1),
                        block.coinbase,
                        block.difficulty,
                        msg.sender
                    )
                )
            )
        );

        return rand % _upper;
    }
}

/// [MIT License]
/// @title Base64
/// @notice Provides a function for encoding some bytes in base64
/// @author Brecht Devos <brecht@loopring.org>
library Base64 {
    bytes internal constant TABLE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';