// Contract: AltNouns
// Target function: function publicMint(uint256 tokenId) public payable virtual
toString(pluckNum(tokenId, "Way", 1, 99)),' ','0.0',toString(pluckNum(tokenId, "Vee", 1, 99))));
            feDisplacementMap = '50';
        } else if (altType == 2) {
            // Uhhhhh
            feTurbulence = string(abi.encodePacked('0.0',toString(pluckNum(tokenId, "Uhhhhh", 15, 20))));
            feDisplacementMap = toString(pluckNum(tokenId, "Uhhhhh", 100, 300));
        } else if (altType == 3) {
            // Glitching
            return '<feDisplacementMap in="SourceGraphic" scale="0"><animate attributeName="scale" begin="0s" dur="10s" values="18.51;16.14;-7.81;4.40;-5.97;5.83;-4.63;8.88;-6.38;11.70;9.35;-14.77;-18.40;8.72;-12.61;14.69;6.56;11.47;-5.59;13.16;19.81;3.56;-1.47;3.95;-16.19;-8.11;-3.04;-10.54;-2.04;-14.23;2.00;-12.81;16.28;-0.54;7.18;-1.52;-10.86;-9.92;-7.48;-0.47;-15.37;17.88;19.83;-10.57;5.75;5.67;-1.84;10.87;1.80;8.13;15.44;-11.35;7.77;-15.39;-18.19;14.66;-7.26;-14.34;-14.41;-3.52;-13.27;-7.34;-19.83;17.63;-14.66;-13.68;-18.70;10.14;0.93;-17.23;15.29;2.20;-0.94;-16.04;-0.55;15.80;-7.77;-12.51;-1.13;-11.18;-15.50;5.72;-13.99;-7.17;-2.19;10.36;-11.09;5.68;-15.20;3.09;-6.79;13.58;-12.04;-10.05;17.14;1.32;-15.67;-14.96;-1.01;-3.94;-11.90;-13.77;-2.27;14.63;12.37;-19.31;-3.99;-13.19;14.06;-15.91;-5.03;-6.54;19.74;-5.67;15.57;-6.80;14.24;2.03;-19.11;-14.10;0.35;-19.23;-13.11;12.04;9.69;13.88;5.13;11.94" repeatCount="indefinite"/></feDisplacementMap>';
        } else if (altType == 4) {
            // Static
            return '<feTurbulence numOctaves="3" seed="2" baseFrequency="0.02 0.05" type="fractalNoise"><animate attributeName="baseFrequency" begin="0s" dur="60s" values="0.002 0.06;0.004 0.08;0.002 0.06" repeatCount="indefinite"/></feTurbulence><feDisplacementMap scale="20" in="SourceGraphic"></feDisplacementMap>';
        } else if (altType == 5) {
            // Immobilized
            feTurbulence = '0.5';
            feDisplacementMap = '0';
            feDisplacementMapAnim = '<animate attributeName="scale" begin="0s" dur="0.5s" values="36.72;58.84;36.90;14.99;13.26;47.30;58.24;21.58;46.51;40.17;35.83;36.08;42.74;32.16;46.57;33.67;17.31;52.09;30.80;40.37;43.99;36.21;16.18;20.04;15.72;50.92;41.35;26.12;31.38;30.41;59.51;10.51;45.48;19.59;58.88;33.92;26.88;13.50;31.85;43.88;33.05;22.82;56.26;27.90;51.95;26.47;27.13;32.41;18.12;52.98;50.04;17.62;27.43;52.81;21.61;15.11;25.89;27.39;39.35;51.29" repeatCount="indefinite"/>';
        } else if (altType == 6) {
            // Snapped
            feTurbulence = string(abi.encodePacked('0.',toString(pluckNum(tokenId, "Snapped", 100, 500))));
            feDisplacementMap = toString(pluckNum(tokenId, "Snapped", 100, 500));
        } else if (altType == 7) {
            // ?????
            feTurbulence = string(abi.encodePacked('0.0',toString(pluckNum(tokenId, "?????", 100, 500)),'" numOctaves="10'));
            feDisplacementMap = toString(pluckNum(tokenId, "?????", 200, 300));
        } else if (altType == 8) {
            // Anti-noun
            return '<feColorMatrix in="SourceGraphic" type="matrix" values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0"/>';
        } else if (altType == 9) {
            // Wall
            return '<feTile in="SourceGraphic" x="90" y="100" width="140" height="100" /><feTile/>';
        } else if (altType == 10) {
            // Warped
            return '<feTile in="SourceGraphic" x="0" y="140" width="320" height="20" /><feTile/>';
        } else if (altType == 11) {
            // Totem
            return '<feTile in="SourceGraphic" x="0" y="100" width="320" height="80" /><feTile/>';
        } else if (altType == 12) {
            // Outta Focus
            return '<feGaussianBlur in="SourceGraphic" stdDeviation="3"><animate attributeName="stdDeviation" begin="0s" dur="4s" values="0;4;3;5;3;2;5;7;8;10;15;0;0;0;0;0;0;0;0" repeatCount="indefinite"/></feGaussianBlur>';
        } else if (altType == 13) {
            // Spirit
            return '<feColorMatrix type="matrix" values=".33 .33 .33 0 0 .33 .33 .33 0 0 .33 .33 .33 0 0  0 0 0 0.2 0"></feColorMatrix>';
        } else if (altType == 14) {
            // Collapsing
            return '<feTurbulence baseFrequency="0.05" type="fractalNoise" numOctaves="9"></feTurbulence><feDisplacementMap in="SourceGraphic" scale="200"><animate attributeName="scale" begin="0s" dur="16s" values="40;550;1;40" fill="freeze" repeatCount="indefinite"/></feDisplacementMap><feMorphology operator="erode" radius="25"><animate attributeName="radius" begin="0s" dur="16s" values="1;25;1;1" fill="freeze" repeatCount="indefinite"/></feMorphology>';
        } else if (altType == 15) {
            // Exploding
            return string(abi.encodePacked('<feMorphology operator="dilate" radius="',toString(pluckNum(tokenId, "Explode", 5, 40)),'"></feMorphology>'));
        } else if (altType == 16) {
            // Nether-noun
            return '<feTile in="SourceGraphic" x="90" y="100" width="140" height="100" /><feTile x="0" y="0" width="320" height="320"/><feBlend in2="SourceGraphic" mode="color-burn"/><feTile x="0" y="0" width="320" height="320"/><feBlend in2="SourceGraphic" mode="color-burn"/><feTile x="0" y="0" width="320" height="320"/><feBlend in2="SourceGraphic" mode="color-burn"/>';
        } else if (altType == 17) {
            // Distorted
            return '<feTile id="eye" in="SourceGraphic" x="142.5" y="110" width="45" height="50" /><feTile/><feTile in2="strip" x="0" y="0" width="320" height="320"/><feBlend in2="SourceGraphic" mode="exclusion"/>';
        } else if (altType == 18) {
            // Fractionalize
            return '<feTurbulence baseFrequency="0.01 0.01" type="fractalNoise" numOctaves="5" seed="12453"><animate attributeName="seed" begin="0s" dur="16s" values="1;20;160" repeatCount="indefinite" /></feTurbulence><feDisplacementMap in="SourceGraphic" scale="300"><animate attributeName="scale" begin="0s" dur="16s" values="1;200;1000;1;1" repeatCount="indefinite" /></feDisplacementMap>';
        }

        string memory result = string(abi.encodePacked('<feTurbulence baseFrequency="',feTurbulence,'" type="fractalNoise">',feTurbulenceAnim,'</feTurbulence><feDisplacementMap in="SourceGraphic" scale="',feDisplacementMap,'">',feDisplacementMapAnim,'</feDisplacementMap>'));

        return result;
    }

    function altNoun(string memory noun, uint256 tokenId) private view returns (string memory) {

        // Decoding the noun tokenURI
        string memory decodedNoun = string(Base64.decode(Strings._substring(noun, int256(Strings.length(noun) - 29), 29)));

        // Finds the index of ';' in decoded tokenURI & adds 8 to it (for 'base64,') to set the starting index for the encoded SVG
        uint256 index = uint256(Strings.indexOf(decodedNoun, ';'));
        index = index+8;

        // Substring subtracts 2 from (length - index) to skip the last two characters, i.e. '"}'
        string memory nounSVG = Strings._substring(decodedNoun, int256(Strings.length(decodedNoun) - index - 2), int256(index));

        // Decoding the noun encoded SVG
        bytes memory SVG = Base64.decode(nounSVG);

        // Opening SVG tag
        string memory openingTag = '<svg width="320" height="320" viewBox="0 0 320 320" xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges">';

        // Grabbing the Noun inside the SVG tag
        string memory innerSVG = getInnerSVG(SVG);

        // Opening <g> tag for filters
        string memory openingGTag = '<g filter="url(#alteration)">';

        // Closing <g> tag and alteration filter
        string memory alterationAndClosingTag = string(abi.encodePacked('</g><defs><filter id="alteration" x="-50%" y="-50%" width="200%" height="200%">',getAlteration(tokenId),'</filter></defs></svg>'));

        // Concatenating everything to get the final Alt Noun SVG
        return string(abi.encodePacked(openingTag,openingGTag,innerSVG,alterationAndClosingTag));
    }

    function getInnerSVG(bytes memory svgBytes) internal pure returns (string memory) {
        bytes memory result = new bytes(svgBytes.length - 122);
        for(uint i = 0; i < result.length; i++) {
            result[i] = svgBytes[i+116];
        }
        return string(result);
    }

    function tokenURI(uint256 tokenId) override public view returns (string memory) {
        require(_exists(tokenId), "URI query for nonexistent token");
        string memory noun = INounsToken(nounsTokenContract).dataURI(tokenId);
        string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "Alt Noun ', toString(tokenId), '", "description": "Hmmm... Something is up with Alt Noun ', toString(tokenId), '"',getTraits(tokenId),'"image": "data:image/svg+xml;base64,', Base64.encode(bytes(altNoun(noun, tokenId))), '"}'))));
        json = string(abi.encodePacked('data:application/json;base64,', json));
        return json;
    }

    /*
     __    __   __   __   __   ______  __   __   __   ______       ______  __  __   __   __   ______   ______  __   ______   __   __   ______    
    /\ "-./  \ /\ \ /\ "-.\ \ /\__  _\/\ \ /\ "-.\ \ /\  ___\     /\  ___\/\ \/\ \ /\ "-.\ \ /\  ___\ /\__  _\/\ \ /\  __ \ /\ "-.\ \ /\  ___\   
    \ \ \-./\ \\ \ \\ \ \-.  \\/_/\ \/\ \ \\ \ \-.  \\ \ \__ \    \ \  __\\ \ \_\ \\ \ \-.  \\ \ \____\/_/\ \/\ \ \\ \ \/\ \\ \ \-.  \\ \___  \  
     \ \_\ \ \_\\ \_\\ \_\\"\_\  \ \_\ \ \_\\ \_\\"\_\\ \_____\    \ \_\   \ \_____\\ \_\\"\_\\ \_____\  \ \_\ \ \_\\ \_____\\ \_\\"\_\\/\_____\ 
      \/_/  \/_/ \/_/ \/_/ \/_/   \/_/  \/_/ \/_/ \/_/ \/_____/     \/_/    \/_____/ \/_/ \/_/ \/_____/   \/_/  \/_/ \/_____/ \/_/ \/_/ \/_____/ 
                                                                                                                                             
    */

    // Checks if a supplied tokenId is a valid noun
    function isNounValid(uint256 tokenId) private view returns (bool) {
        bool isValid = (tokenId + nounsTokenIndexOffset == IERC721Enumerable(nounsTokenContract).tokenByIndex(tokenId));
        return isValid;
    }

    // Updates nounsTokenIndexOffset. Default value is 0 (since indexOf(tokenId) check should match tokenId), but in case a Noun is burned, the offset may require a manual update to allow Alt Nouns to be minted again
    function setNounsTokenIndexOffset(uint256 newOffset) public onlyOwner {
        nounsTokenIndexOffset = newOffset;
    }

    // Mint function that saves the Alt Type (for future tokenURI reads), mints the supplied tokenId and increments counters
    function mint(address destination, uint256 tokenId) private {
        require(isNounValid(tokenId), "This noun does not exist (yet). So its Alt Noun cannot exist either.");
        
        _altForId[tokenId] = getAltType(tokenId) + 1;
        _safeMint(destination, tokenId);
        numTokensMinted += 1;
        _mintPerAddress[msg.sender] += 1;
    }