ETH Price: $2,525.00 (-3.01%)

Contract

0x2d902BB467F6990373E5487972f9Ef89a12f9F87
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040185713182023-11-14 16:24:35321 days ago1699979075IN
 Contract Creation
0 ETH0.2636113250.05693301

Advanced mode:
Parent Transaction Hash Block From To
View All Internal Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x6e853B95...cD9B61747
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
AlienRunesGenerator

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 100 runs

Other Settings:
paris EvmVersion
File 1 of 16 : AlienRunesGenerator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import './IAlienRunesGenerator.sol';
import './IFilters.sol';
import './SVG.sol';
import './Catmull.sol';
import './Shapes.sol';
import './Traits.sol';
import './Random.sol';
import './Gradients.sol';



/*

                  █████                  █████████   ███████████  
                ███░░░███               ███░░░░░███ ░░███░░░░░███ 
               ███   ░░███ █████ █████ ░███    ░███  ░███    ░███ 
              ░███    ░███░░███ ░░███  ░███████████  ░██████████  
              ░███    ░███ ░░░█████░   ░███░░░░░███  ░███░░░░░███ 
              ░░███   ███   ███░░░███  ░███    ░███  ░███    ░███ 
               ░░░█████░   █████ █████ █████   █████ █████   █████
                 ░░░░░░   ░░░░░ ░░░░░ ░░░░░   ░░░░░ ░░░░░   ░░░░░ 



                                ░░                                              
                               ▒▓▓▒                                             
                              ░▓▓▓▒                                             
                              ▒▓▓▓▒                                             
                             ▒▓▓▓▓▒                   ░░░                       
                    ░░       ▓█▓▓▓▒     ░▒░        ░▒▓▓▓░                       
                   ░▒▓▒     ▒▓▓▓▓▓▒    ▒▒▓       ░▒▓▓▓▒░                        
                   ░▒▓▓░    ▓█▓▓▓▓▒   ░▒▒░    ░▒▓▓▓▓▓▒            ░░░░░         
                    ▒▓▓▒░   ▒▓█▓▓▓▒  ░░░░    ░▓▓▓▒▓▓░     ░░░▒▓▓▓▓▓▓▓▓▒         
                    ▒▓▓▓▒   ░▓█▓▓█▓ ▒▓░░░   ░▓▓▓▓▓▒    ░▒▓██▓▓▓▓▓▓▓▒▒░          
                    ░▓▓▒▓▒   ▒██▓█▓░▓▓▒▒   ░▓▓▓▓▓▒   ░▒▓█▓▓▓▒▒▓▓▒▒              
                    ░▓▓▓▓▓░  ▒▓█▓█▓▒▓▓▓▒  ░▓▓▓▓▓░  ░▒██▓▓▓▓▓▓▒░                 
              ░      ▒▓▓▓▓▓░ ░▓█▓█▓▒▓▓▓░  ▒▓▓▓▓░ ░▒▓█▓▓▓█▓▓▒░                   
              ▒▒▒     ▒▓▓▓▓▓░ ▒▓██▓▓▓▓▓░ ▒▓▓▓▓░░▒▓██▓█▓▓▒░                      
  ░▒▒░         ▒▒▒░    ░▓▓▓▓▓░░▓██▓▓▓▓▓░▒▓▓▓▒░▒▓███▓▓▓▒░▒▓▓▒░▒▒▒▒▒▓▒            
  ░▒▓▓▓▒░░      ░░░░░   ░▓▓▓▓▓░▒▓█▓▓▓▓▒░▓▓▓▒░▓██▓▓▓▓▒▓▓▓▓▒░░░░░░░               
   ░▒▓▓▓▓▓▒▒░    ░▒░░▒░   ▒▓▓▓▓░▓▓▒ ▒▒   ░   ▒▒▓▓▓▓▓▓▓▓▓▒░                      
     ░▓▓▓▓▒▓▓▒▒░  ▒▓▓▓▓▓▒  ▒▓▓▓▒              ░▓▓▓▓▓▒░                          
       ▒▓█▓▓▓▓▓▓▓▓▒░░▓▓▓▓▓▒░░▓▓▒                  ░▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▒░░          
         ▒▓██▓▓▓▓▓█▓▓▓▒▒▓▓▓▓▓░                   ▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒░░      
          ░▒▓▓█████▓▓██▓▓▓▓▓▓▓░                     ▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▓▓▓░    
               ░▒▓▓▓██████▓▓░                      ░▓▓▓▓▒░              ░░░     
      ░░             ░▒▓▓▓▓█▒                      ░▓▓█████▓▓▓▒░                
    ░▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░▒░                     ░▓▓▓▓▓▓▓██▓▓████▓▓▓▒           
      ░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒                   ░▓▓▓▓▓▒▒▓▓▓█▓▓▓▓▓██▓▒         
          ░░▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░                 ░▓▓░░▒▓▓▓▓▓░░▒▓▓▓▓▓▓▓▓█▓▒       
               ░░░░        ░▒▒▓▓▓░              ░▓▓▓▒  ▒▓▓▓▓▓▒  ░▒▓▓▓▒▓▓▓▓░     
                      ░▒▒▓▓▓▓▓▓▓▓▒▒       ░▒ ░▒▒░▒▓▓▓▒░  ░▒░░▒░    ░▒▒▓▓▓▓▓▒░   
                ░░░░░░▒▓▓▓▓▓▓▓▓▓██▓▒░▒▓▓░░▓▓▒▓█▓▓░▓▓▓▓▓░   ░░░░░      ░▒▒▓▓▓▒░  
            ▒▒▒▒▓▒▒░░▓▓▓▒░▒▓▓███▓▓▒▒▓▓▓▒ ▒▓▓▓▓██▓░░▓▓▓▓▓░    ░▒▒▒         ▒▒▓▒  
                       ░▒▓█▓▓██▓▒ ▒▓▓▓▓░░▓▓▓▓▓███▒ ░▓▓▓▓▓▒     ▒▒▒░             
                    ░▒▓█▓▓▓██▓▒ ░▒▓▓▓▓░ ░▓▓▓▒▓███▓░ ░▓▓▓▓▓▓      ▒░             
                 ░▒▒▓▓▓▓▓██▓▒   ▓▓▓▓▓▒  ░▓▓▓▒▓█▓█▓▒  ░▓▓▓▓▓░                    
              ░▒▒▓▓▒▓▓▓██▓▒   ░▓▓▓▓▓▒   ▒▒▓▓░▒█▓▓█▓░  ░▓▒▓▓▒                    
           ░▒▓▓▓▓▓▓▓▓██▓▒    ░▓▓▓▓▓▒   ░░░▓▓ ▒█▓▓█▓░   ▒▓▓▓▓                    
         ░▒▓▓▓▓▓▓▓▓▒▒░░    ░▒▓▒▓▓▓▒    ░░░░  ▒▓▓▓▓█▓   ░▒▓▓▓                    
         ░▒▒▒░░           ░▒▓▓▓▓▓░     ▒▒░   ░▓▓▓▓█▓░   ░▒▓▓░                   
                         ░▓▒▓▓▒░      ▒▒▒    ░▓▓▓▓█▓     ░▓▓░                   
                        ▒▓▓▒░        ░▒▒     ░▓▓▓▓▓░      ░░                    
                        ▒▒░                  ░▓▓▓▓▒                             
                                             ░▓▓▓▓░                             
                                             ░▓▓▓▒                              
                                             ░▓▓▒░                              
                                              ▒▒                                
    
*/

contract AlienRunesGenerator is IAlienRunesGenerator {
    IFilters filters;

    constructor(IFilters _filters) {
        filters = _filters;
    }

    function genRndAndTraits(uint256 _tokenId) private pure returns (RandomCtx memory rndCtx, Traits.TraitsCtx memory traits) {
        rndCtx = Random.initCtx(_tokenId);
        traits = Traits.generateTraitsCtx(rndCtx);
    }

    function renderAsSvgInternal(uint256 _tokenId) private view returns (string memory result, Traits.TraitsCtx memory traits) {
        RandomCtx memory rndCtx;

        (rndCtx, traits) = genRndAndTraits(_tokenId);

        result = renderAsSvgInternalByTraits(traits, rndCtx);
    }

    function renderAsSvgInternalByTraits(Traits.TraitsCtx memory traits, RandomCtx memory rndCtx) private view returns (string memory result) {
        string memory randomized;
        uint256 maxDistance;

        (randomized, maxDistance) = Shapes.generateShape(rndCtx, traits);

        string memory placed = "";
        string memory placedInDef = "";

        (placed, placedInDef) = Shapes.shapePlacer(
            "shape",
            "placedShape",
            traits.shapePlacement
        );

        placed = filters.applyAllFilters(traits.filter, placed);

        result = string.concat(
            Svg.svgMainElement(maxDistance, traits.shape.viewBoxBuffer),
            Svg.defs(
                "",
                string.concat(
                    Gradients.createGradientMain("g1", traits.gradient),
                    filters.generateAllFilterStrings(rndCtx, traits.filter),
                    Svg.g(Svg.prop("id", "shape"), randomized),
                    placedInDef
                )
            ),
            placed,
            "</svg>"
        );
    }

    function renderAsSvg(uint256 _tokenId) external view override returns (string memory) {
        string memory result;

        (result, ) = renderAsSvgInternal(_tokenId);

        return result;
    }

    function renderAsSvgAndAttributes(uint256 _tokenId) external view override returns (string memory result, string memory attributes) {
        Traits.TraitsCtx memory traits;

        (result, traits) = renderAsSvgInternal(_tokenId);
        attributes = Traits.getTraitsAsJsonString(traits);

        return (result, attributes);
    }
}

File 2 of 16 : IAlienRunesGenerator.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

interface IAlienRunesGenerator {
    function renderAsSvg(uint256 _tokenId) external view returns (string memory result);
    function renderAsSvgAndAttributes(uint256 _tokenId) external view returns (string memory svg, string memory attributes);
}

File 3 of 16 : IFilters.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import './FilterTraits.sol';
import './RandomCtx.sol';

interface IFilters {
    function generateAllFilterStrings(RandomCtx memory rndCtx, FilterTraits memory filter) external view returns (string memory);
    function applyAllFilters(FilterTraits memory filter, string memory svgElement) external view returns (string memory);
}

File 4 of 16 : SVG.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import "./Utils.sol";

// Core SVG utilitiy library which helps us construct
// onchain SVG's with a simple, web-like API.
library Svg {
    using Utils for uint256;

    /* MAIN ELEMENTS */
    function g(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('g', _props, _children);
    }

    function symbol(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('symbol', _props, _children);
    }

    function defs(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('defs', _props, _children);
    }

    function use(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('use', _props, _children);
    }

    function path(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('path', _props, _children);
    }

    function text(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('text', _props, _children);
    }

    function line(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('line', _props, _children);
    }

    function circle(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('circle', _props, _children);
    }

    function circle(string memory _props) internal pure returns (string memory) {
        return el('circle', _props);
    }

    function rect(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('rect', _props, _children);
    }

    function rect(string memory _props) internal pure returns (string memory) {
        return el('rect', _props);
    }

    function filter(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('filter', _props, _children);
    }

    function feColorMatrix(string memory _props) internal pure returns (string memory) {
        return el('feColorMatrix', _props);
    }

    function cdata(string memory _content) internal pure returns (string memory) {
        return string.concat('<![CDATA[', _content, ']]>');
    }

    /* GRADIENTS */
    function radialGradient(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('radialGradient', _props, _children);
    }

    function linearGradient(string memory _props, string memory _children) internal pure returns (string memory) {
        return el('linearGradient', _props, _children);
    }

    function gradientStop(string memory offset, string memory stopColor, string memory _props) internal pure returns (string memory) {
        return
            el(
                'stop',
                string.concat(
                    prop('stop-color', stopColor),
                    ' ',
                    prop('offset', string.concat(offset, '%')),
                    ' ',
                    _props
                )
            );
    }

    function animateTransform(string memory _props) internal pure returns (string memory) {
        return el('animateTransform', _props);
    }

    function image(string memory _href, string memory _props) internal pure returns (string memory) {
        return
            el(
                'image',
                string.concat(prop('href', _href), ' ', _props)
            );
    }

    /* COMMON */
    // A generic element, can be used to construct any SVG (or HTML) element
    function el(bytes32 _tag, string memory _props, string memory _children) internal pure returns (string memory) {
        string memory strTag = Utils.toString(_tag);
        
        return
            string.concat(
                '<',
                strTag,
                ' ',
                _props,
                '>',
                _children,
                '</',
                strTag,
                '>'
            );
    }


    // A generic element, can be used to construct any SVG (or HTML) element without children
    function el(bytes32 _tag, string memory _props) internal pure returns (string memory) {
        return el(_tag, _props, "");
    }

    // an SVG attribute
    function prop(bytes32 _key, string memory _val) internal pure returns (string memory) {
        return string.concat(Utils.toString(_key), '=', '"', _val, '" ');
    }

    function svgMainElement(uint256 maxDistance, uint16 buffer) internal pure returns (string memory) { unchecked {
        maxDistance = 2 * maxDistance * buffer / 100; // 15% buffer;
        uint256 xy = maxDistance / 2;
    
        string memory xyStr = xy.toString();
        string memory maxDistStr = maxDistance.toString();

        // xyStr = "512";
        // maxDistStr = "1024";

        return 
            string.concat(
                '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" '// width="512px" height="512px" '   // 
                'viewBox="-', xyStr, ' -', xyStr, ' ', maxDistStr, ' ', maxDistStr, '" ', 
                'style="background:#eee" xmlns:xlink="http://www.w3.org/1999/xlink">');
    }}
}

File 5 of 16 : Catmull.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import './Lib2D.sol';
import './Utils.sol';

library Catmull {
    using Utils for int256;

    // tenstion from 0 to 100 (0..1)
    function toBezierPath(Lib2D.Point[] memory points, bool isClosed, int256 tension) internal pure returns (string memory) { unchecked {
        //if (points.length <= 0) return "M0,0";
        string memory result = string.concat("M", points[0].x.toString(), " ", points[0].y.toString());

        uint256 size = points.length;

        if (!isClosed) {
            size -= 1;
        }

        for (uint256 i=0; i<size; i++) {
            Lib2D.Point memory p0;
            Lib2D.Point memory p1;
            Lib2D.Point memory p2;
            Lib2D.Point memory p3;

            if (isClosed) {
                p0 = points[(i + size - 1) % size];
                p1 = points[i];
                p2 = points[(i + 1) % size];
                p3 = points[(i + 2) % size];
            } else {
                p0 = i == 0 ? points[0] : points[i - 1];
                p1 = points[i];
                p2 = points[i + 1];
                p3 = i == size - 1 ? p2 : points[i + 2];
            }

            int256 x1 = p1.x + (p2.x - p0.x) * tension / 600;
            int256 y1 = p1.y + (p2.y - p0.y) * tension / 600;

            int256 x2 = p2.x - (p3.x - p1.x) * tension / 600;
            int256 y2 = p2.y - (p3.y - p1.y) * tension / 600;

            result = string.concat(result, "C ", x1.toString(), " ", y1.toString(), " ",
                                                x2.toString(), " ", y2.toString(), " ", 
                                                p2.x.toString(), " ", p2.y.toString(), " ");
        }

        if (isClosed) {
            result = string.concat(result, "Z");
        }
        
        return result;
    }}
}

File 6 of 16 : Shapes.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import './SVG.sol';
import './Lib2D.sol';
import './Catmull.sol';
import './Division.sol';
import './Filters.sol';
import './Filters-generated.sol';
import './Traits.sol';
import './Random.sol';
import './Utils.sol';


library Shapes {
    using Utils for uint256;

    function generatePoints(RandomCtx memory rndCtx, Traits.ShapeTraits memory shape) internal pure returns (Lib2D.Point[] memory) { unchecked {
        Lib2D.Point[] memory points = new Lib2D.Point[](shape.numPoints);

        int256 distance = Random.randRange(rndCtx, 100, 200);
        int256 distanceSquared = distance ** 2;

        if (shape.pointsMethod == Traits.ShapePointsMethod.RND_DISTANCE) {
            distance = Random.randRange(rndCtx, 120, 200);
            distanceSquared =  distance ** 2;
        }

        for (uint8 i = 0; i < shape.numPoints; i++) {
            if (i == 0) {
                // placement for the first point
                points[i] = Lib2D.Point(Random.randRange(rndCtx, -100, 300), Random.randRange(rndCtx, -100, 300));
            } else {
                int256 deltaY = Random.randRange(rndCtx, 0, distance);
                int256 deltaYSquared = deltaY ** 2;
                int256 deltaXSquared = distanceSquared - deltaYSquared;
                int256 deltaX = 0;
                

                if (deltaXSquared > 0) {
                    deltaX = int256(Utils.ceilSqrt(uint256(deltaXSquared)));
                    // if (deltaX < 40) deltaX = 40;
                } else {
                    // deltaX = 40;
                }
                
                deltaY = int256(Utils.ceilSqrt(uint256(deltaYSquared)));
                // if (deltaY < 40) deltaY = 40;

                int256 xSign = Random.randSign(rndCtx, 50);
                if (shape.noXsignChange) {
                    xSign = 1;
                }

                int256 ySign = Random.randSign(rndCtx, 50);
                if (shape.noYsignChange) {
                    ySign = 1;
                }

                points[i] = Lib2D.Point(points[i-1].x + xSign * deltaX, 
                                        points[i-1].y + ySign * deltaY);

                if (i >= 2) {
                    uint256 distToPrevPrev = Utils.ceilSqrt(
                        uint256((points[i].x - points[i-2].x) ** 2) + uint256((points[i].y - points[i-2].y) ** 2)
                    );

                    if (distToPrevPrev < 80) {
                        // change the sign of X
                        points[i] = Lib2D.Point(points[i-1].x + (-xSign) * deltaX, 
                                                points[i-1].y + (-ySign) * deltaY);

                        uint256 distToPrevPrev2 = Utils.ceilSqrt(
                            uint256((points[i].x - points[i-2].x) ** 2) + uint256((points[i].y - points[i-2].y) ** 2)
                        );

                        if (distToPrevPrev > distToPrevPrev2) {
                            // doesn't make sence, revert to old version
                            points[i] = Lib2D.Point(points[i-1].x + xSign * deltaX, 
                                                    points[i-1].y + ySign * deltaY);
                        }                                                
                    }
                }

                if (shape.pointsMethod == Traits.ShapePointsMethod.RND_DISTANCE) {
                    distance = Random.randRange(rndCtx, 80, 200);
                    distanceSquared = distance ** 2;
                } else if (shape.pointsMethod == Traits.ShapePointsMethod.FIXED_DISTANCE) {
                    // do nothing, keep the same distance
                } else if (shape.pointsMethod == Traits.ShapePointsMethod.GOLDEN_RATIO_DISTANCE) {
                    distance = distance * int256(Utils.GOLDEN_RATIO) / int256(Utils.MULTIPLIER);
                    distanceSquared = distance ** 2; 
                }
            }
        }

        return points;
    }}

    function findMaxDistance(Lib2D.Point[] memory points) internal pure returns (uint256) { unchecked {
        uint256 maxDistance = 0;

        for (uint8 i = 0; i < points.length; i++) {
            uint256 currentDistance = uint256(points[i].x ** 2 + points[i].y ** 2);
            if (currentDistance > maxDistance) {
                maxDistance = currentDistance;
            }
        }

        return maxDistance;
    }}

    function fixLineWidth(RandomCtx memory rndCtx, uint256 maxDistance, Traits.ShapeTraits memory shape) internal pure {
        if (maxDistance < 100) {
            shape.wLineWidth = "1";
            shape.nLineWidth = "1";

            if (Random.randBool(rndCtx, 20)) {
                shape.wLineWidth = "2";
                shape.nLineWidth = "2";   
            }    
        } else if (maxDistance < 300) {
            shape.wLineWidth = "3";
            shape.nLineWidth = "2";

            if (Random.randBool(rndCtx, 20)) {
                shape.wLineWidth = "5";
                shape.nLineWidth = "3";   
            }      
        } else if (maxDistance < 500) {
            shape.wLineWidth = "8";
            shape.nLineWidth = "4";

            if (Random.randBool(rndCtx, 20)) {
                shape.wLineWidth = "10";
                shape.nLineWidth = "5";   
            }         
        } else if (maxDistance < 1000) {
            shape.wLineWidth = "18";
            shape.nLineWidth = "3";   

            if (Random.randBool(rndCtx, 20)) {
                shape.wLineWidth = "25";
                shape.nLineWidth = "5";   
            }         
        } else {
            shape.wLineWidth = Division.divisionStr(2, int256(maxDistance), Random.randRange(rndCtx, 40, 65));
            shape.nLineWidth = Division.divisionStr(2, int256(maxDistance), Random.randRange(rndCtx, 200, 600));
        }
    }

    function generateStrokeAndOpacityString(RandomCtx memory rndCtx, Traits.TraitsCtx memory traits, uint256 hue, bool isWide) internal pure returns (string memory) {
        string memory stroke = "";

        if (traits.shape.coloring == Traits.ShapeColoring.GRADIENT) {
            stroke = 'stroke="url(#g1)" ';
        } else {
            stroke = Svg.prop('stroke', string.concat("hsl(", hue.toString(),",", traits.gradient.saturationAndLightness,")"));
        }
        
        if (isWide) {
            uint256 opacity = uint256(Random.randRange(rndCtx, 80, 90));
            return string.concat(" ", stroke, ' stroke-width="', traits.shape.wLineWidth,'" opacity="', string.concat(opacity.toString(), '%" '));
        } else {
            uint256 opacity = uint256(Random.randRange(rndCtx, 10, 13));
            return string.concat(" ", stroke, 'stroke-width="', traits.shape.nLineWidth, '" opacity="', string.concat(opacity.toString(), '%" ')); 
        }
    }

    function generateShape(RandomCtx memory rndCtx, Traits.TraitsCtx memory traits) internal pure returns (string memory, uint256) { unchecked {
        Lib2D.Point[] memory points = generatePoints(rndCtx, traits.shape);

        string memory randomized = "";

        uint256 hue = traits.gradient.startingHue;
        uint256 maxDistance = Utils.ceilSqrt(findMaxDistance(points));
        
        fixLineWidth(rndCtx, maxDistance, traits.shape);

        // randomized = appendPathElement(rndCtx, randomized, points, generateStrokeAndOpacityString(rndCtx, traits, hue, true, true), traits, 200, 200);
        
        for (uint8 k = 0; k < traits.shape.numLines1 + 1; k++) {
            string memory tmp = "";
            for (uint8 i = 0; i < traits.shape.numLines2; i++) {
                
                if (k == 0) {
                    if (i >= traits.shape.numWiderLines) break;
                }

                string memory strokeAndOpacity;

                if (hue > 360) {
                    hue %= 360;
                }

                // check for widerLines
                if (k == 0) { 
                    strokeAndOpacity = generateStrokeAndOpacityString(rndCtx, traits, hue, true);

                    if (traits.shape.coloring == Traits.ShapeColoring.LINE_HUE_INC) {
                        hue += uint256(Random.randRange(rndCtx, 30, 50));
                    } else {
                        hue = traits.gradient.startingHue + uint256(Random.randRange(rndCtx, 0, 50));
                    }
                } else {
                    strokeAndOpacity = generateStrokeAndOpacityString(rndCtx, traits, hue, false);
                  
                    if (traits.shape.coloring == Traits.ShapeColoring.LINE_HUE_INC) {
                        hue += uint256(Random.randRange(rndCtx, 0, 4));
                    } else {
                        hue = traits.gradient.startingHue + uint256(Random.randRange(rndCtx, 0, 30));
                    }
                }
                                
                tmp = appendPathElement(rndCtx, tmp, points, strokeAndOpacity, traits, k, i);
            }

            randomized = string.concat(randomized, tmp);
        }

        return (randomized, maxDistance);
    }}

    function appendPathElement(RandomCtx memory rndCtx, string memory toAppend, Lib2D.Point[] memory points, string memory strokeAndOpacity, Traits.TraitsCtx memory traits, uint8 k, uint8 i) internal pure returns (string memory) { unchecked {
        Lib2D.Point[] memory newPoints;

        // if (k == 200) {
        //     // special case to go with originally selected points, no randomization or shifing
        //     newPoints = points;
        // } else 
        
        if (traits.shape.randomLineDelta) {
            newPoints = Lib2D.randomizePoints(rndCtx, points, traits.shape.randomDeltaX, traits.shape.randomDeltaY, traits.shape.mergeMod);
        } else {
            int16 kint = 10 * (int16(uint16(k)) - 1);
            int16 iint = int16(uint16(i));

            if (traits.shape.nonRandomCenter) {
                if (k % 2 == 0) {
                    kint = kint / 2;
                } else {
                    kint = -(kint / 2);
                }
            }

            newPoints = Lib2D.shiftPoints(points, kint * traits.shape.nonRandomXMultiplier / 100 + iint, kint * traits.shape.nonRandomYMultiplier / 100 + iint, traits.shape.mergeMod);
        }

        string memory newCatmull = Catmull.toBezierPath(newPoints, traits.shape.closed, traits.shape.catmullTension);
        
        string memory pathProps = string.concat(
            'd="', newCatmull,
            '" fill="url(#g1)" fill-opacity="', string.concat(uint256(traits.shape.fillOpacity).toString(),'%'),
            '" stroke-linecap="round" ',
            traits.shape.strokeDashArray,
            ' ',
            strokeAndOpacity
        );

        return string.concat(toAppend, "<path ", pathProps, "/>");
    }}

    function shapePlacer(string memory shapeID, string memory newShapeID, Traits.ShapePlacementTraits memory shapePlacement) internal pure returns(string memory placed, string memory placedInDef) {
        placedInDef = mainShapePlacer("mainShapes", shapeID, shapePlacement);
        placed = spiralShapePlacer("mainShapes", newShapeID, shapePlacement);
    }

    function createSpiralElement(string memory shapeID, Traits.ShapePlacementTraits memory shapePlacement, uint16 elementNumber, uint256 currentAngle, uint256 currentScale)  internal pure returns (string memory) {
        string memory scaleFormatted = Division.divisionStr(2, int256(currentScale), int256(Utils.MULTIPLIER));
        string memory currentElement = "";

        int256 opacity = Utils.lerp(100, int256(uint256(shapePlacement.spiralEndOpacity)), 0, int256(uint256(shapePlacement.spiralCopies)), int256(uint256(elementNumber)));
        string memory opacityStr = Division.divisionStr(2, opacity, int256(Utils.MULTIPLIER * 100));

        string memory style = "";

        if (elementNumber % 2 == 0 && shapePlacement.replaceSpiralWithMirrorForEven) {
            currentElement = Svg.use(
                                string.concat(
                                    Svg.prop("href", string.concat("#", shapeID)),
                                    Svg.prop("xlink:href", string.concat("#", shapeID)),
                                    Svg.prop("transform", string.concat("rotate(", 
                                        Division.divisionStr(2, int256(currentAngle), int256(Utils.MULTIPLIER)), 
                                        ") scale (", scaleFormatted,",-",scaleFormatted, ")")),
                                    Svg.prop("opacity", opacityStr),
                                    style
                                ),
                                ""
                            );
        } else {
            currentElement = Svg.use(
                                string.concat(
                                    Svg.prop("href", string.concat("#", shapeID)),
                                    Svg.prop("xlink:href", string.concat("#", shapeID)),
                                    Svg.prop("transform", string.concat("rotate(", 
                                        Division.divisionStr(2, int256(currentAngle), int256(Utils.MULTIPLIER)), 
                                        ") scale (", scaleFormatted, ")")),
                                    Svg.prop("opacity", opacityStr),
                                    style
                                ),
                                ""
                            );
        }    

        return currentElement;
    }

    // return <g> with ID - newShapeId that must be placed in <defs>
    function spiralShapePlacer(string memory shapeID, string memory newShapeID, Traits.ShapePlacementTraits memory shapePlacement) internal pure returns (string memory) {
        string memory result = "";

        uint256 currentAngle = shapePlacement.startAngle * Utils.MULTIPLIER; // to fix chrome bug with 180 rotation and gradient
        uint256 currentScale = shapePlacement.spiralStartScale;
        uint256 angleStep = Utils.MULTIPLIER * shapePlacement.spiralRotateAngle;

        if (shapePlacement.spiralCopies == 0) {
            shapePlacement.spiralCopies = 1;
        }

        for (uint16 i=0; i<shapePlacement.spiralCopies; i++) {
            string memory currentElement = createSpiralElement(shapeID, shapePlacement, i, currentAngle, currentScale);            

            currentAngle += angleStep;
            currentScale = currentScale * Utils.MULTIPLIER / shapePlacement.spiralDivideFactor;

            if (shapePlacement.spiralOuterOnTop) {
                result = string.concat(currentElement, result);
            } else {
                result = string.concat(result, currentElement);
            }
        }

        result = Svg.g(
            Svg.prop("id", newShapeID),
            result
        );

        return result;
    }


    function mainShapePlacer(string memory placeID, string memory shapeReferenceID, Traits.ShapePlacementTraits memory shapePlacement) internal pure returns (string memory) {
        string memory symetry;

        uint256 currentAngle = shapePlacement.startAngle * Utils.MULTIPLIER; // to fix chrome bug with 180 rotation and gradient
        uint256 angleStep = 360 * Utils.MULTIPLIER / shapePlacement.symetryPoints;

        string memory mirrorScale = Division.divisionStr(2, int256(uint256(shapePlacement.mirrorScalePercentage)), 100);

        string memory xyscale = string.concat(
            shapePlacement.mirrorX ? string.concat("-", mirrorScale) : mirrorScale,
            ",",
            shapePlacement.mirrorY ? string.concat("-", mirrorScale) : mirrorScale
        );

        for (uint16 i=0; i<shapePlacement.symetryPoints; i++) {
            string memory angleFormatted = Division.divisionStr(2, int256(currentAngle), int256(Utils.MULTIPLIER));

            if (i % 2 == 0 && shapePlacement.repalceMainWithMirrorForEven) {
                symetry = string.concat(symetry,
                    Svg.use(
                        string.concat(
                            Svg.prop("href", string.concat("#", shapeReferenceID)),
                            Svg.prop("xlink:href", string.concat("#", shapeReferenceID)),
                            Svg.prop("transform", string.concat("rotate(", angleFormatted,  ") scale(", mirrorScale,",-",mirrorScale,")"))
                        ),
                        ""
                    )
                );
            } else {
                string memory scaleStr = Division.divisionStr(2, Utils.lerp(100, shapePlacement.symetryEndScale, 0, int16(shapePlacement.symetryPoints), int16(i)), int256(Utils.MULTIPLIER * 100));

                xyscale = string.concat(
                    shapePlacement.mirrorX ? string.concat("-", scaleStr) : scaleStr,
                    ",",
                    shapePlacement.mirrorY ? string.concat("-", scaleStr) : scaleStr
                );

                symetry = string.concat(symetry,
                    Svg.use(
                        string.concat(
                            Svg.prop("href", string.concat("#", shapeReferenceID)),
                            Svg.prop("xlink:href", string.concat("#", shapeReferenceID)),
                            Svg.prop("transform", string.concat("rotate(", angleFormatted, ") scale(",scaleStr,")"))
                        ),
                        ""
                    )
                );
            }

            if (shapePlacement.mirrorX || shapePlacement.mirrorY) {
                symetry = string.concat(symetry,
                    Svg.use(
                        string.concat(
                            Svg.prop("href", string.concat("#", shapeReferenceID)),
                            Svg.prop("xlink:href", string.concat("#", shapeReferenceID)),
                            Svg.prop("transform", string.concat("rotate(", angleFormatted, ") scale(", xyscale ,")"))
                        ),
                        ""
                    )
                );
            }

            currentAngle += angleStep * shapePlacement.angleMultiplier / shapePlacement.angleDivider;
        }

        symetry = Svg.g(
            Svg.prop("id", placeID),
            symetry
        );

        return symetry;
    }
}

File 7 of 16 : Traits.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import './Random.sol';
import './Utils.sol';
import './FilterTraits.sol';


library Traits {
    using Utils for uint256;
    using Utils for int256; 

    struct ShapePlacementTraits {
        uint16 symetryPoints;                // how many symetry points, around the center
        int16 symetryEndScale;               
        bool mirrorX;                        // do we mirror arround X axis
        bool mirrorY;                        // do we mirror arround Y axis 
        uint16 spiralCopies;                 // how many spiral copies
        uint16 spiralRotateAngle;            // at what agle to rotate the spiral
        uint256 spiralStartScale;            // starting scale for spiral, usually 1 (but sometimes can be different);   Multiplied by 100000 == MULTIPLIER
        uint256 spiralDivideFactor;          // how to divide the spiralScale, multiplied by 100000 == MULTIPLIER
        uint16 startAngle;                   // start angle for both spiral and symetry
        bool spiralOuterOnTop;               // True - place outer elements on top - i.e. place them last
        uint16 spiralEndOpacity;             // opacity of the spiral at the end
        bool repalceMainWithMirrorForEven;   // mirror even shapes
        uint8 mirrorScalePercentage;         // mirror scale percentage, from 0 to 100
        bool replaceSpiralWithMirrorForEven; // mirror even layers of the spiral 
        uint8 angleMultiplier;                
        uint8 angleDivider;                  
        bool non360rotation;                 // result is irregular runes, that does not fully cover the circle
    }

    enum ShapePointsMethod { 
        RND_DISTANCE,
        FIXED_DISTANCE,
        GOLDEN_RATIO_DISTANCE
    }

    enum ShapeColoring {
        GRADIENT,
        LINE_HUE_RND,
        LINE_HUE_INC
    }

    struct ShapeTraits {
        uint8 numPoints;                 // number of points of lines (for others )
        ShapePointsMethod pointsMethod;  // how to generate points
        bool noXsignChange;
        bool noYsignChange;
        uint8 numLines1;
        uint8 numLines2;
        uint8 numWiderLines;
        string wLineWidth;
        string nLineWidth;
        string strokeDashArray;
        ShapeColoring coloring;
        uint8 fillOpacity;              // percentage - from 0 to 100
        bool randomLineDelta;
        bool randomDeltaDiff;
        int8 randomDeltaX;
        int8 randomDeltaY;
        int16 nonRandomXMultiplier;     // 1 .. (numLines1 + 1) * 10, it is multiplied with 10 for better accurace
        int16 nonRandomYMultiplier;     // 1 .. (numLines1 + 1) * 10, it is multiplied with 10 for better accurace
        bool nonRandomCenter;
        uint8 mergeMod;
        bool closed;
        int16 catmullTension;
        uint16 viewBoxBuffer;
    }

    enum GradientOpacityPattern {
        ALL_SAME,
        INCREASING,
        DECREASING,
        ZIG_ZAG
    }

    enum GradientPalettePattern {
        TWO_COLORS,
        TWO_COLORS_ZZ,
        TWO_COLORS_X2,
        LINEAR,
        GOLDEN_ANGLE,
        RAINBOW
    }

    struct GradientTraits {
        int256 angle;
        uint8 repeatCount;
        bool linear;
        bool reflect;
        bool discrete;
        uint8 discreteOffset;
        GradientOpacityPattern opacityPattern;
        uint8 beginOpacity;
        uint8 endOpacity;
        uint8 transparentFromBeginOffset;
        uint8 transparentToEndOffset;
        uint16 startingHue;
        uint8 paletteColors;
        uint16 paletteStep;
        GradientPalettePattern palettePattern;
        string saturationAndLightness;
        string saturationAndLightness2;
        string saturationAndLightness3;
    }

    int8 internal constant NUM_FILTERS = 22;

    

    struct TraitsCtx {
        ShapePlacementTraits shapePlacement; 
        ShapeTraits shape;
        GradientTraits gradient;
        FilterTraits filter;
    }

    function generateTraitsCtx(RandomCtx memory rndCtx) internal pure returns (TraitsCtx memory) {
        TraitsCtx memory result;

        result.shapePlacement = decideOnShapePlacementTraits(rndCtx);
        result.shape = decideOnShapeTraits(rndCtx);
        result.gradient = decideOnGradientTraitis(rndCtx);
        result.filter = decideOnFilterTraits(rndCtx);

        fineTuneTraits(result);

        return result;
    }

    // shapePlacement
    function decideOnShapePlacementTraits(RandomCtx memory rndCtx) internal pure returns (ShapePlacementTraits memory) { unchecked {
        ShapePlacementTraits memory shapePlacementTraits = ShapePlacementTraits({
            symetryPoints: uint8(int8(Random.randRange(rndCtx, 2, 12))),
            symetryEndScale: 100 - int8(Random.randWithProbabilities(rndCtx, hex"200101010206060606")) * 10,
            mirrorX: false,
            mirrorY: false,
            spiralCopies: 0, 
            spiralRotateAngle: 33,
            spiralStartScale: Utils.MULTIPLIER * 1,
            spiralDivideFactor: Utils.MULTIPLIER * 50 / uint256(Random.randRange(rndCtx, 20, 50)),
            startAngle: 1,
            spiralOuterOnTop: true,
            spiralEndOpacity: 0,
            repalceMainWithMirrorForEven: false,
            mirrorScalePercentage: (5 + Random.randWithProbabilities(rndCtx, hex"010305070909")) * 10,
            replaceSpiralWithMirrorForEven: Random.randBool(rndCtx, 30),
            angleMultiplier: 1,
            angleDivider: 1,
            non360rotation: false
        });

        // 2% chance for non 360 rotation
        if (Random.randBool(rndCtx, 2)) {
            if (Random.randBool(rndCtx, 70)) {
                shapePlacementTraits.angleMultiplier = uint8(int8(Random.randRange(rndCtx, 50, 90)));
            } else {
                shapePlacementTraits.angleMultiplier = uint8(int8(Random.randRange(rndCtx, 20, 50)));
            }
            shapePlacementTraits.angleDivider = 100;//uint8(int8(Random.randRange(rndCtx, 1, 10)));
            shapePlacementTraits.non360rotation = true;
        }

        if (shapePlacementTraits.symetryPoints <= 3) {
            shapePlacementTraits.spiralCopies = uint8(int8(Random.randRange(rndCtx, 2, 10)));
            shapePlacementTraits.spiralRotateAngle = uint16(int16(Random.randRange(rndCtx, 15, 30)));
            shapePlacementTraits.mirrorX = true;
        } else {
            shapePlacementTraits.spiralCopies = uint8(int8(Random.randRange(rndCtx, 0, 13 - int256(uint256(shapePlacementTraits.symetryPoints)))));
            shapePlacementTraits.mirrorX = Random.randBool(rndCtx, 50);
            shapePlacementTraits.spiralRotateAngle = uint16(int16(Random.randRange(rndCtx, 0, 45)));
        }

        // // 5% chance to have spiralDivideFactor than cause increase in size
        if (shapePlacementTraits.spiralCopies > 1 && Random.randBool(rndCtx, 5)) {
            uint256 newSpiralFactor = Utils.MULTIPLIER * 50 / uint256(Random.randRange(rndCtx, 55, 63));
            newSpiralFactor += 1;

            //shapePlacementTraits.spiralDivideFactor = newSpiralFactor
        }

        //
        //
        // shapePlacementTraits.symetryPoints = 6;
        // shapePlacementTraits.mirrorX = true;
        // shapePlacementTraits.spiralCopies = 0;
        // shapePlacementTraits.spiralRotateAngle = 22;
        // shapePlacementTraits.spiralDivideFactor = Lib2D.MULTIPLIER * 50 / 40;


        if (!shapePlacementTraits.mirrorX && shapePlacementTraits.symetryPoints % 2 == 0) {
            shapePlacementTraits.repalceMainWithMirrorForEven = Random.randBool(rndCtx, 50);
        }

        if (shapePlacementTraits.spiralCopies >= 12) {
            shapePlacementTraits.spiralEndOpacity = uint16(int16(Random.randRange(rndCtx, 1, 20)));
        } else if (shapePlacementTraits.spiralCopies >= 7) {
            shapePlacementTraits.spiralEndOpacity = uint16(int16(Random.randRange(rndCtx, 20, 40)));
        } else if (shapePlacementTraits.spiralCopies >= 4) {
            shapePlacementTraits.spiralEndOpacity = uint16(int16(Random.randRange(rndCtx, 20, 60)));
        } else {
            shapePlacementTraits.spiralEndOpacity = uint16(int16(Random.randRange(rndCtx, 40, 100)));
        }

        // shapePlacementTraits.mirrorX = false;
        // shapePlacementTraits.symetryPoints = 6;
        // shapePlacementTraits.repalceMainWithMirrorForEven = false;
        // shapePlacementTraits.mirrorScalePercentage = 60;
        // shapePlacementTraits.spiralCopies = 2;
        // shapePlacementTraits.spiralEndOpacity = 30;

        return shapePlacementTraits;
    }}

    function decideOnShapeTraits(RandomCtx memory rndCtx) internal pure returns (ShapeTraits memory) { unchecked {
        ShapeTraits memory shapeTraits = ShapeTraits({
            numPoints: 3 + Random.randWithProbabilities(rndCtx, hex"050A0C19140F050503"),
            pointsMethod: ShapePointsMethod(Random.randWithProbabilities(rndCtx, hex"3C190F")),
            noXsignChange: Random.randBool(rndCtx, 10),
            noYsignChange: Random.randBool(rndCtx, 10),
            numLines1: 7,
            numLines2: 7,
            numWiderLines: uint8(int8(Random.randRange(rndCtx, 1, 5))),
            wLineWidth: "18",
            nLineWidth: "3",
            strokeDashArray: "",
            coloring: ShapeColoring(Random.randWithProbabilities(rndCtx, hex"500F05")),
            fillOpacity: uint8(int8(Random.randRange(rndCtx, 0, 4))),
            randomLineDelta: Random.randBool(rndCtx, 40),
            randomDeltaDiff: Random.randBool(rndCtx, 20),
            randomDeltaX: int8(Random.randRange(rndCtx, 3, 20)),
            randomDeltaY: int8(Random.randRange(rndCtx, 3, 20)),
            nonRandomXMultiplier: 7,
            nonRandomYMultiplier: 7,
            nonRandomCenter: true,
            mergeMod: Random.randWithProbabilities(rndCtx, hex"0501050302"),
            closed: Random.randBool(rndCtx, 50),
            catmullTension: int16(uint16(Random.randWithProbabilities(rndCtx, hex"1505141E1E14030201")) * 50),
            viewBoxBuffer: 140
        });

        if (shapeTraits.mergeMod == 0) {
            shapeTraits.mergeMod = 200; // which means - no merging
        }

        // 10% chance for dash array
        if (Random.randBool(rndCtx, 10)) {
            int256 dashType = Random.randRange(rndCtx, 0, 9);

            if (dashType == 0) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="0% 1%"';    
            } else if (dashType == 1) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="1% 2%"';    
            } else if (dashType == 2) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="5% 5%"';    
            } else if (dashType == 3) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="10% 5%"';    
            } else if (dashType == 4) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="4% 5%"';    
            } else if (dashType == 5) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="15% 5%"';    
            } else if (dashType == 6) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="20% 30%"';   
            } else if (dashType == 7) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="35% 8%"';    
            } else if (dashType == 8) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="2% 25% 8%"';    
            } else if (dashType == 9) {
                shapeTraits.strokeDashArray = 'stroke-dasharray="12% 6% 13%"';    
            }
        }

        // shapeTraits.randomLineDelta = false;

        if (shapeTraits.catmullTension == 0) {
            shapeTraits.catmullTension = 20;
        } else {
            shapeTraits.catmullTension = int16(Random.randSign(rndCtx, 10) * shapeTraits.catmullTension);
        }

        // correct viewBoxBuffer
        if (shapeTraits.catmullTension <= 100) {
            shapeTraits.viewBoxBuffer = 125;
        } else if (shapeTraits.catmullTension <= 200) {
            shapeTraits.viewBoxBuffer = 130;
        } 

        if (!shapeTraits.randomDeltaDiff) {
            shapeTraits.randomDeltaY = shapeTraits.randomDeltaX;
        }

        uint16 multiplier = (1 + Random.randWithProbabilities(rndCtx, hex"0505231E0A")) * 2;    

        shapeTraits.nonRandomCenter = Random.randBool(rndCtx, 15);
        shapeTraits.nonRandomXMultiplier = int16(Random.randRange(rndCtx, int256(uint256(shapeTraits.numLines1)), int256(uint256(shapeTraits.numLines1 * multiplier))));
        shapeTraits.nonRandomYMultiplier = int16(Random.randRange(rndCtx, int256(uint256(shapeTraits.numLines1)), int256(uint256(shapeTraits.numLines1 * multiplier))));

        if (!shapeTraits.nonRandomCenter) {
            shapeTraits.nonRandomXMultiplier = int16(Random.randSign(rndCtx, 50) * shapeTraits.nonRandomXMultiplier);
            shapeTraits.nonRandomYMultiplier = int16(Random.randSign(rndCtx, 50) * shapeTraits.nonRandomYMultiplier);
        }


        return shapeTraits;
    }}

    // gradients
    function decideOnGradientTraitis(RandomCtx memory rndCtx) internal pure returns (GradientTraits memory) { unchecked {
        GradientTraits memory gradientTraits = GradientTraits({
            angle: Random.randRange(rndCtx, 0, 359),
            repeatCount: 1 + Random.randWithProbabilities(rndCtx, hex"32140F0A05"),
            linear: Random.randBool(rndCtx, 85),
            reflect: Random.randBool(rndCtx, 80),
            discrete: Random.randBool(rndCtx, 20),
            discreteOffset: uint8(int8(Random.randRange(rndCtx, 1, 10))),
            opacityPattern: GradientOpacityPattern(Random.randWithProbabilities(rndCtx, hex"3214140A")),
            beginOpacity: uint8(int8(Random.randRange(rndCtx, 20, 50))),
            endOpacity: 100,
            transparentFromBeginOffset: 5 * Random.randWithProbabilities(rndCtx, hex"3C140A0A"),
            transparentToEndOffset: 100 - 5 * Random.randWithProbabilities(rndCtx, hex"3C140A0A"),
            startingHue: uint16(int16(Random.randRange(rndCtx, 1, 359))),
            paletteColors: 2 + Random.randWithProbabilities(rndCtx, hex"28140A0A0A0703"),
            paletteStep: uint16(int16(Random.randRange(rndCtx, 20, 100))),
            palettePattern: GradientPalettePattern(Random.randWithProbabilities(rndCtx, hex"14141E0A0A01")),
            saturationAndLightness: "100%, 50%",
            saturationAndLightness2: "100%, 60%",
            saturationAndLightness3: "80%, 50%"
        });

        if (!gradientTraits.linear) {
            gradientTraits.transparentFromBeginOffset = 0;
            gradientTraits.transparentToEndOffset = 100;
        }

        return gradientTraits;
    }}


    function decideOnFilterTraits(RandomCtx memory rndCtx) internal pure returns (FilterTraits memory) { unchecked {
        FilterTraits memory filterTraits = FilterTraits({
            mainFilterType: FilterType(Random.randRange(rndCtx, 0, NUM_FILTERS-1)),
            additonalLighting: true
        });
        
        return filterTraits;
    }}

    function fineTuneTraits(TraitsCtx memory traits) internal pure {
        // fine tune on saturation and lightness
        {
            FilterType filter = traits.filter.mainFilterType;

            if (filter == FilterType.NONE ||
                filter == FilterType.SEPIA || 
                filter == FilterType.WATERCOLOR_BRIGHT ||
                filter == FilterType.DISPLACEMENT ||
                filter == FilterType.FROZEN || 
                filter == FilterType.INNER_OUTLINE ||
                filter == FilterType.CHALK) {
                traits.gradient.saturationAndLightness  = "90%,40%";
                traits.gradient.saturationAndLightness2 = "90%,45%";
                traits.gradient.saturationAndLightness3 = "80%,50%";
            }

            // traits.shape.coloring != ShapeColoring.GRADIENT ||
            if ( 
                filter == FilterType.GRAYSCALE || 
                filter == FilterType.SMOKE || 
                filter == FilterType.SPLASH ||
                filter == FilterType.GLASS ||
                filter == FilterType.SPREAD ||
                filter == FilterType.SPREAD_BRIGHT) {
                traits.gradient.saturationAndLightness  = "80%,40%";
                traits.gradient.saturationAndLightness2 = "80%,45%";
                traits.gradient.saturationAndLightness3 = "70%,50%";    
            }

            if (filter == FilterType.HALF_TONE) {
                traits.gradient.saturationAndLightness  = "30%,30%";
                traits.gradient.saturationAndLightness2 = "30%,35%";
                traits.gradient.saturationAndLightness3 = "30%,30%"; 
            }

        }

        // fix other properties based on filter
        if (traits.filter.mainFilterType == FilterType.SPREAD_BRIGHT ||
            traits.filter.mainFilterType == FilterType.SPREAD ||
            traits.filter.mainFilterType == FilterType.HALF_TONE ||
            traits.filter.mainFilterType == FilterType.RECOLORING || 
            traits.filter.mainFilterType == FilterType.STONE ||
            traits.filter.mainFilterType == FilterType.WATERCOLOR_BRIGHT ||
            traits.filter.mainFilterType == FilterType.WATERCOLOR) {
            traits.shape.strokeDashArray = "";
        }

        if (traits.filter.mainFilterType == FilterType.STONE) {
            traits.shape.numWiderLines = 4;
        }

        if (traits.shape.numPoints + traits.shapePlacement.symetryPoints > 20 || traits.shape.numPoints >= 10) {
            traits.shape.numLines1 = 6;
            traits.shape.numLines2 = 6;
        } else if (traits.shape.numPoints + traits.shapePlacement.symetryPoints >= 18) {
            traits.shape.numLines1 = 7;
            traits.shape.numLines2 = 6;
        } else if (traits.shape.numWiderLines >= 5) {
            // traits.shape.numLines1 = 6;
            // traits.shape.numLines2 = 7;
        }

        if (traits.shapePlacement.spiralDivideFactor <  Utils.MULTIPLIER) {
            traits.shape.viewBoxBuffer = 100;
        }
    }

    function getTraitsAsJsonString(TraitsCtx memory traits) internal pure returns (string memory) {
        string memory result = string.concat(
            stringTrait("Shape Points", uint256(traits.shape.numPoints).toString()),
            stringTrait("Points Generation Method", toString(traits.shape.pointsMethod)),
            stringTrait("Shape Is Closed", toString(traits.shape.closed)),
            stringTrait("Shape Wide Lines", uint256(traits.shape.numWiderLines).toString()),
            stringTrait("Coloring", toString(traits.shape.coloring)),
            stringTrait("Dashed", toString(bytes(traits.shape.strokeDashArray).length > 0)),
            stringTrait("Catmull-Rom Tension", int256(traits.shape.catmullTension).toString()),
            stringTrait("Gradient Palette", toString(traits.gradient.palettePattern)),
            stringTrait("Symetry Points", uint256(traits.shapePlacement.symetryPoints).toString())
        );

        result = string.concat(result,
            stringTrait("Symetry End Scale", int256(traits.shapePlacement.symetryEndScale).toString()),
            stringTrait("Additional Mirror", toString(traits.shapePlacement.mirrorX || traits.shapePlacement.mirrorY)),
            stringTrait("Symetry Mirror Even", toString(traits.shapePlacement.repalceMainWithMirrorForEven)),
            stringTrait("Non 360 rotation", toString(traits.shapePlacement.non360rotation)),
            stringTrait("Rings", uint256(traits.shapePlacement.spiralCopies).toString()),
            // stringTrait("Rings Scale Out", toString(traits.shapePlacement.spiralDivideFactor < Utils.MULTIPLIER)),            
            stringTrait("Ring Mirror Even", toString(traits.shapePlacement.replaceSpiralWithMirrorForEven)),
            stringTraitLast("Filter", toString(traits.filter.mainFilterType))
        );
        
        return string.concat('"attributes":[', result, ']');
    }

    function stringTrait(string memory traitName, string memory traitValue) internal pure returns (string memory) {
        return string.concat(stringTraitLast(traitName, traitValue),',');
    }

    function stringTraitLast(string memory traitName, string memory traitValue) internal pure returns (string memory) {
        return string.concat('{"trait_type":"', traitName,'","value":"',traitValue, '"}');
    }

    function toString(bool b) internal pure returns (string memory) {
        return b ? "Yes" : "No";
    }

    function toString(ShapePointsMethod method) internal pure returns (string memory) {
        if (method == ShapePointsMethod.RND_DISTANCE) {
            return "Random Distance";
        } else if (method == ShapePointsMethod.FIXED_DISTANCE) {
            return "Fixed Distance";
        }

        // GOLDEN_RATIO_DISTANCE
        return "Golden Ratio Distance";
    }

    function toString(ShapeColoring coloring) internal pure returns (string memory) {
        if (coloring == ShapeColoring.GRADIENT) {
            return "Gradient";
        } else if (coloring == ShapeColoring.LINE_HUE_RND) {
            return "Line Hue Random";
        }

        // LINE_HUE_INC
        return "Line Hue Increasing";
    }

    function toString(GradientPalettePattern gpp) internal pure returns (string memory) {
        if (gpp == GradientPalettePattern.TWO_COLORS) {
            return "Two Colors";
        } else if (gpp == GradientPalettePattern.TWO_COLORS_ZZ) {
            return "Two Colors Zig-Zag";
        } else if (gpp == GradientPalettePattern.TWO_COLORS_X2) {
            return "Two Colors X2";
        } else if (gpp == GradientPalettePattern.LINEAR) {
            return "Linear Offset";
        } else if (gpp == GradientPalettePattern.GOLDEN_ANGLE) {
            return "Golden Angle Offset";
        } 
        
        // RAINBOW
        return "Rainbow";
    }

    function toString(FilterType filter) internal pure returns (string memory) {
        if (filter == FilterType.GRAYSCALE) {
            return "Grayscale";
        } else if (filter == FilterType.SEPIA) {
            return "Sepia";
        } else if (filter == FilterType.WATERCOLOR) {
            return "Watercolor";
        } else if (filter == FilterType.WATERCOLOR_BRIGHT) {
            return "Bright Watercolor";
        } else if (filter == FilterType.STONE) {
            return "Stone";
        } else if (filter == FilterType.SMOKE) {
            return "Smoke";
        } else if (filter == FilterType.DISPLACEMENT) {
            return "Displacement";
        } else if (filter == FilterType.FROZEN) {
            return "Frozen";
        } else if (filter == FilterType.SPLASH) {
            return "Splash";
        } else if (filter == FilterType.RECOLORING) {
            return "Recoloring";
        } else if (filter == FilterType.SPARKS) {
            return "Sparks";
        } else if (filter == FilterType.GLASS) {
            return "Glass";
        } else if (filter == FilterType.SPREAD) {
            return "Spread";
        } else if (filter == FilterType.SPREAD_BRIGHT) {
            return "Spread Bright";
        } else if (filter == FilterType.INNER_OUTLINE) {
            return "Inner Outline";
        } else if (filter == FilterType.COLOR_OUTLINE) {
            return "Color Outline";
        } else if (filter == FilterType.NOISE_SHADOW) {
            return "Noise Shadow";
        } else if (filter == FilterType.RECOLORING2) {
            return "Recoloring 2";
        } else if (filter == FilterType.CHALK) {
            return "Chalk";
        } else if (filter == FilterType.BLACK_OUTLINE) {
            return "Black Outline";
        } else if (filter == FilterType.HALF_TONE) {
            return "Halftone Noise";
        }
  
        return "none";
    }
}

File 8 of 16 : Random.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import "./Division.sol";
import "./RandomCtx.sol";

library Random {
    function initCtx(uint256 startingSeed) internal pure returns (RandomCtx memory) {
        // some 10 digit prime numbers
        // 1024243321, 1024383257, 1028910301, 1111111231, 1111211111
        // 1317313771, 1500000001, 2999999929, 3333323333, 4332221111 
        // 5111111191, 6668999101, 7000000001, 8018018081, 9199999999
        return RandomCtx(startingSeed, 5111111191 * startingSeed);      
    }

    function setSeed(RandomCtx memory ctx, uint256 startingSeed) internal pure {
        ctx.seed = startingSeed;
    } 

    // function setSeedFromConract(RandomCtx memory ctx, uint256 startingSeed) internal view {
    //     ctx.seed = uint256(keccak256(
    //         abi.encode(
    //             startingSeed,
    //             blockhash(block.number - 1),
    //             block.coinbase,
    //             block.prevrandao,
    //             block.timestamp
    //         )
    //     ));
    // }

    function randInt(RandomCtx memory ctx) internal pure returns (uint256) {
        ctx.counter++;

        ctx.seed = uint256(keccak256(
            abi.encode(
                ctx.seed, ctx.counter
            )
        ));
        
        return ctx.seed;
    }

    function randUInt32(RandomCtx memory ctx) internal pure returns (uint32) {
        return uint32(int32(randRange(ctx, 0, 2147483647))); // positive only to accomodate SEED in SVG
    }

    function randFloat(RandomCtx memory ctx, uint8 decimalPlaces, int256 from, int256 to, int256 denominator) internal pure returns (string memory result) {
        int256 rInt = randRange(ctx, from, to);
        result = Division.divisionStr(decimalPlaces, rInt, denominator);
    }

    function randWithProbabilities(RandomCtx memory ctx, bytes memory probabilities) internal pure returns (uint8) { unchecked {
        uint256 probSum = 0;

        for (uint8 i = 0; i < probabilities.length; i++) {
            probSum += uint256(uint8(probabilities[i]));
        }

        int256 rnd = Random.randRange(ctx, 1, int256(probSum));

        probSum = 0;
        for (uint8 i = 0; i < probabilities.length; i++) {
            probSum += uint256(uint8(probabilities[i]));

            if (int256(probSum) >= rnd) {
                return i;
            }
        }

        return 0;
    }}

    function randRange(RandomCtx memory ctx, int256 from, int256 to) internal pure returns (int256) { unchecked {
        if (from > to) {
            to = from;
        }
        uint256 rnd = randInt(ctx);

        return from + int256(rnd >> 1) % (to - from + 1);
    }}

    /**
     * 
     * @param ctx - context
     * @param minusProbability - 0 to 100 percents
     */
    function randSign(RandomCtx memory ctx, int256 minusProbability) internal pure returns (int256) {
        if (randRange(ctx, 1, 100) <= minusProbability) {
            return -1;
        }
        return 1;
    }


    /**
     * 
     * @param ctx - context
     * @param trueProbability - 0 to 100 percents
     */
    function randBool(RandomCtx memory ctx, int256 trueProbability) internal pure returns (bool) {
        return (randRange(ctx, 1, 100) <= trueProbability);
    }
}

File 9 of 16 : Gradients.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import './SVG.sol';
import './Division.sol';
import './Utils.sol';
import './Traits.sol';

library Gradients {
    using Utils for int256;
    using Utils for uint256;

    struct ColorStop {
        string color;
        string offset;
        string opacity;
    }

    function createGradient(string memory id, ColorStop[] memory stops, Traits.GradientTraits memory traits) internal pure returns (string memory) { unchecked {
         
        string memory stopStr = "";

        for (uint256 i=0; i < stops.length; i++) {
            stopStr = string.concat(stopStr, Svg.gradientStop(stops[i].offset, stops[i].color, Svg.prop("stop-opacity", string.concat(stops[i].opacity, "%"))));
        }

        if (traits.repeatCount == 0) traits.repeatCount = 1;

        string memory calculatedPercentage;

        (,,calculatedPercentage) = Division.division(2, 100, int8(traits.repeatCount));

        string memory sm = "reflect";

        if (!traits.reflect) {
            sm = "repeat";
        }

        if (traits.linear) {
            return Svg.linearGradient(
                string.concat(
                    Svg.prop("id", id),
                    Svg.prop("gradientTransform", string.concat("rotate(",traits.angle.toString(),", 0.5, 0.5)")),
                    '  x1="0%" y1="0%" x2="', calculatedPercentage,'%" y2="',calculatedPercentage,'%" spreadMethod="',sm,'"'
                ),
                stopStr
            );
        } else {            
            // no spread method, there is a bug in Safari that does not rended correctly gradients with spreadMethod "repeat" or "reflect"
            return Svg.radialGradient(
                string.concat(
                    Svg.prop("id", id),
                    Svg.prop("gradientTransform", string.concat("rotate(",traits.angle.toString(),", 0.5, 0.5)")),
                    ' fx="50%" fy="50%" cx="50%" cy="50%" r="',calculatedPercentage,'%" '
                ),
                stopStr
            );
        }
    }}

    function createGradientMain(string memory id, Traits.GradientTraits memory traits) internal pure returns (string memory) {
        if (traits.palettePattern == Traits.GradientPalettePattern.TWO_COLORS) {
            return createGradientTwoColors(id, hslColor(traits.startingHue, traits.saturationAndLightness2), hslColor(traits.startingHue + traits.paletteStep, traits.saturationAndLightness), traits);
        } if (traits.palettePattern == Traits.GradientPalettePattern.TWO_COLORS_X2) {
            return createGradientTwoColorsX2(id, hslColor(traits.startingHue, traits.saturationAndLightness), hslColor(traits.startingHue + traits.paletteStep, traits.saturationAndLightness), traits);
        }  else if (traits.palettePattern == Traits.GradientPalettePattern.TWO_COLORS_ZZ) {
            string[] memory p2 = new string[](3);
            p2[0] = hslColor(traits.startingHue, traits.saturationAndLightness);
            p2[1] = hslColor(traits.startingHue + traits.paletteStep, traits.saturationAndLightness);
            p2[2] = p2[0];
            return createGradientFromPalette(id, p2, traits);
        } else if (traits.palettePattern == Traits.GradientPalettePattern.RAINBOW) {
            return createFullRanbowGradient(id, traits);
        } else if (traits.palettePattern == Traits.GradientPalettePattern.LINEAR) {
            string[] memory p2 = generateLinearOffsetPalette(traits.startingHue, traits.paletteStep, traits.paletteColors, traits.saturationAndLightness);
            return createGradientFromPalette(id, p2, traits);
        } 

        // default - golden angle pattern
        string[] memory palette = generateGoldenAnglePalette(traits.startingHue, 5, traits.saturationAndLightness3);
        return createGradientFromPalette(id, palette, traits);
    }

    function createGradientTwoColors(string memory id, string memory color1, string memory color2, Traits.GradientTraits memory traits) internal pure returns (string memory) {
        ColorStop[] memory stops = new ColorStop[](2);
        stops[0] = ColorStop({color: color1, offset: "45", opacity: "100"});
        stops[1] = ColorStop({color: color2, offset: "55", opacity: "100"});
        return createGradient(id, stops, traits);
    }

    function createGradientTwoColorsX2(string memory id, string memory color1, string memory color2, Traits.GradientTraits memory traits) internal pure returns (string memory) {
        ColorStop[] memory stops = new ColorStop[](4);
        stops[0] = ColorStop({color: color1, offset: "0", opacity: "100"});
        stops[1] = ColorStop({color: color2, offset: "33", opacity: "40"});
        stops[2] = ColorStop({color: color1, offset: "66", opacity: "40"});
        stops[3] = ColorStop({color: color2, offset: "100", opacity: "100"});
        return createGradient(id, stops, traits);
    }

    function createFullRanbowGradient(string memory id, Traits.GradientTraits memory traits) internal pure returns (string memory) {
        string[] memory palette;
        
        if (traits.discrete) {
            palette = generateLinearOffsetPalette(traits.startingHue, 20, 18, traits.saturationAndLightness);
        } else {
            palette = generateLinearOffsetPalette(traits.startingHue, 10, 36, traits.saturationAndLightness);
        }
        return createGradientFromPalette(id, palette, traits);
    }

    function createGradientFromPalette(string memory id, string[] memory palette, Traits.GradientTraits memory traits) internal pure returns (string memory) { unchecked {
        uint8 arrayAddOn = 0;
        if (traits.transparentFromBeginOffset > 0) {
            arrayAddOn += 2;
        }
        if (traits.transparentToEndOffset > 0 && traits.transparentToEndOffset < 100) {
            arrayAddOn += 2;
        } else {
            traits.transparentToEndOffset = 100;
        }
        
        if (traits.discrete) {
            arrayAddOn += uint8(palette.length);
        }

        ColorStop[] memory stops = new ColorStop[](palette.length + arrayAddOn);
        uint8 arrayIndex = 0;

        if (traits.transparentFromBeginOffset > 0) {
            stops[arrayIndex++] = ColorStop({color: palette[0], offset: "0", opacity: "0"});
            stops[arrayIndex++] = ColorStop({color: palette[0], offset: uint256(traits.transparentFromBeginOffset).toString(), opacity: "0"});
        }

        uint8 deltaOffset = 100;

        deltaOffset -= traits.transparentFromBeginOffset;
        deltaOffset -= (100 - traits.transparentToEndOffset);

        uint256 deltaOffsetStep = uint256(deltaOffset) * Utils.MULTIPLIER / (palette.length - 1 + (traits.discrete ? 1 : 0));
        uint256 offset = uint256(traits.transparentFromBeginOffset) * Utils.MULTIPLIER ;

        uint256 discreteStep = traits.discreteOffset * Utils.MULTIPLIER;
        if (discreteStep > deltaOffsetStep) {
            discreteStep = deltaOffsetStep;
        }

        uint256 deltaOpacityStep = uint256(traits.endOpacity - traits.beginOpacity) * Utils.MULTIPLIER / (palette.length - 1);
        uint256 opacity = uint256(traits.beginOpacity) * Utils.MULTIPLIER ;

        for (uint8 i = 0; i < palette.length; i++) {
            uint256 currentOpacity;

            if (traits.opacityPattern == Traits.GradientOpacityPattern.ALL_SAME) {
                currentOpacity = traits.endOpacity * Utils.MULTIPLIER;
            } else if (traits.opacityPattern == Traits.GradientOpacityPattern.INCREASING) {
                currentOpacity = opacity;
            } else if (traits.opacityPattern == Traits.GradientOpacityPattern.DECREASING) {
                currentOpacity = traits.endOpacity * Utils.MULTIPLIER - (opacity - traits.beginOpacity * Utils.MULTIPLIER);
            } else if (traits.opacityPattern == Traits.GradientOpacityPattern.ZIG_ZAG) {
                if (i % 2 == 0) {
                    currentOpacity = traits.beginOpacity * Utils.MULTIPLIER;
                } else {
                    currentOpacity = traits.endOpacity * Utils.MULTIPLIER;
                }
            }

            stops[arrayIndex++] = ColorStop({color: palette[i], 
                offset: Division.divisionStr(2, int256(offset), int256(Utils.MULTIPLIER)), 
                opacity: Division.divisionStr(2, int256(currentOpacity), int256(Utils.MULTIPLIER))
            });

            if (traits.discrete) {
                stops[arrayIndex++] = ColorStop({color: palette[i], 
                    offset: Division.divisionStr(2, int256(offset + deltaOffsetStep - discreteStep), int256(Utils.MULTIPLIER)), 
                    opacity: Division.divisionStr(2, int256(currentOpacity), int256(Utils.MULTIPLIER))
                });     
            }

            offset += deltaOffsetStep;
            opacity += deltaOpacityStep;
        }

        if (traits.transparentToEndOffset > 0 && traits.transparentToEndOffset < 100) {
            stops[arrayIndex++] = ColorStop({color: palette[palette.length-1], offset: uint256(traits.transparentToEndOffset).toString(), opacity: "0"});
            stops[arrayIndex++] = ColorStop({color: palette[palette.length-1], offset: "100", opacity: "0"});
        }

        return createGradient(id, stops, traits);
    }}

    function generateLinearOffsetPalette(uint256 startingHue, uint256 offset, uint256 steps, string memory saturationAndLightness) internal pure returns (string[] memory) {
        string[] memory result = new string[](steps);

        for (uint8 i = 0; i < steps; i++) {
            result[i] = hslColor(startingHue, saturationAndLightness);
            startingHue = (startingHue + offset) % 360;
        }

        return result;
    }

    // multiply is multiplied by Lib2D, multiplier
    function generateMultiplyOffsetPalette(uint256 startingHue, uint256 multiply, uint256 steps, string memory saturationAndLightness) internal pure returns (string[] memory) {
        string[] memory result = new string[](steps);

        for (uint8 i = 0; i < steps; i++) {
            result[i] = hslColor(startingHue, saturationAndLightness);
            startingHue = (startingHue * multiply / Utils.MULTIPLIER) % 360;
        }

        return result;
    }

    function generateGoldenAnglePalette(uint256 startingHue, uint256 steps, string memory saturationAndLightness) internal pure returns (string[] memory) {
        return generateMultiplyOffsetPalette(startingHue, 137 * Utils.MULTIPLIER, steps, saturationAndLightness);
    }

    function hslColor(uint256 hue, string memory saturationAndLightness) internal pure returns (string memory) {
        hue = hue % 360;
        return string.concat("hsl(", hue.toString(), ",", saturationAndLightness, ")");
    }
}

File 10 of 16 : FilterTraits.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

enum FilterType {
    NONE,
    GRAYSCALE,
    SEPIA,
    WATERCOLOR,
    WATERCOLOR_BRIGHT,
    STONE,
    SMOKE,
    DISPLACEMENT,
    FROZEN,
    SPLASH,
    RECOLORING,
    SPARKS,
    GLASS, 
    SPREAD,
    SPREAD_BRIGHT,
    INNER_OUTLINE,
    COLOR_OUTLINE,
    NOISE_SHADOW,
    RECOLORING2,
    CHALK,
    BLACK_OUTLINE,
    HALF_TONE
}

struct FilterTraits {
    FilterType mainFilterType;
    bool additonalLighting;
    //bool darken;
}

File 11 of 16 : RandomCtx.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
    
struct RandomCtx {
    uint256 seed;
    uint256 counter;
}

File 12 of 16 : Utils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

library Utils {
    uint256 internal constant MULTIPLIER   = 100000;
    uint256 internal constant GOLDEN_RATIO = 161803;

    /**
      * Compute the largest integer smaller than or equal to the square root of `n`
    */
    function floorSqrt(uint256 n) internal pure returns (uint256) { unchecked {
        if (n > 0) {
            uint256 x = n / 2 + 1;
            uint256 y = (x + n / x) / 2;
            while (x > y) {
                x = y;
                y = (x + n / x) / 2;
            }
            return x;
        }
        return 0;
    }}

    /**
      * Compute the smallest integer larger than or equal to the square root of `n`
    */
    function ceilSqrt(uint256 n) internal pure returns (uint256) { unchecked {
        uint256 x = floorSqrt(n);
        return x ** 2 == n ? x : x + 1;
    }}

    function lerp(int256 targetFrom, int256 targetTo, int256 currentFrom, int256 currentTo, int current) internal pure returns (int256) { unchecked {
        int256 t = 0;
        int256 divisor = currentTo - currentFrom - 1;
        
        if (divisor > 0) {
            t = (current - currentFrom) * int256(MULTIPLIER) / (divisor);
        }

        return targetFrom * int256(MULTIPLIER) + t * (targetTo - targetFrom);
    }}

    function toByteArray(bytes32 _bytes32) internal pure returns (bytes memory result) {
        uint8 i = 0;
        while(i < 32 && _bytes32[i] != 0) {
            i++;
        }
        bytes memory bytesArray = new bytes(i);
        for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
            bytesArray[i] = _bytes32[i];
        }
        return bytesArray;
    }

    function toString(bytes32 _bytes32) internal pure returns (string memory result) {
        return string(toByteArray(_bytes32));
    }

    /*

        Gas Efficient uint/int to string functions
        Copied from: https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol

    */

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(uint256 value) internal pure returns (string memory str) {
        /// @solidity memory-safe-assembly
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits.
            str := add(mload(0x40), 0x80)
            // Update the free memory pointer to allocate.
            mstore(0x40, add(str, 0x20))
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            let w := not(0) // Tsk.
            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            for { let temp := value } 1 {} {
                str := add(str, w) // `sub(str, 1)`.
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    /// @dev Returns the base 10 decimal representation of `value`.
    function toString(int256 value) internal pure returns (string memory str) {
        if (value >= 0) {
            return toString(uint256(value));
        }
        unchecked {
            str = toString(uint256(-value));
        }
        /// @solidity memory-safe-assembly
        assembly {
            // We still have some spare memory space on the left,
            // as we have allocated 3 words (96 bytes) for up to 78 digits.
            let length := mload(str) // Load the string length.
            mstore(str, 0x2d) // Store the '-' character.
            str := sub(str, 1) // Move back the string pointer by a byte.
            mstore(str, add(length, 1)) // Update the string length.
        }
    }


    // /// @dev Returns a concatenated string of `a` and `b`.
    // /// Cheaper than `string.concat()` and does not de-align the free memory pointer.
    // function concat(string memory a, string memory b)
    //     internal
    //     pure
    //     returns (string memory result)
    // {
    //     /// @solidity memory-safe-assembly
    //     assembly {
    //         let w := not(0x1f)
    //         result := mload(0x40)
    //         let aLength := mload(a)
    //         // Copy `a` one word at a time, backwards.
    //         for { let o := and(add(aLength, 0x20), w) } 1 {} {
    //             mstore(add(result, o), mload(add(a, o)))
    //             o := add(o, w) // `sub(o, 0x20)`.
    //             if iszero(o) { break }
    //         }
    //         let bLength := mload(b)
    //         let output := add(result, aLength)
    //         // Copy `b` one word at a time, backwards.
    //         for { let o := and(add(bLength, 0x20), w) } 1 {} {
    //             mstore(add(output, o), mload(add(b, o)))
    //             o := add(o, w) // `sub(o, 0x20)`.
    //             if iszero(o) { break }
    //         }
    //         let totalLength := add(aLength, bLength)
    //         let last := add(add(result, 0x20), totalLength)
    //         // Zeroize the slot after the string.
    //         mstore(last, 0)
    //         // Stores the length.
    //         mstore(result, totalLength)
    //         // Allocate memory for the length and the bytes,
    //         // rounded up to a multiple of 32.
    //         mstore(0x40, and(add(last, 0x1f), w))
    //     }
    // }
}

File 13 of 16 : Lib2D.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import "./Random.sol";
import "./RandomCtx.sol";

library Lib2D {
    struct Point {
        int256 x;
        int256 y;
    }

    function randomizeSinglePoint(RandomCtx memory ctx, Point memory p, int16 deltaX, int16 deltaY) internal pure returns (Point memory) {
        int256 newX = p.x + int256(Random.randRange(ctx, -deltaX, deltaX));
        int256 newY = p.y + int256(Random.randRange(ctx, -deltaY, deltaY));

        return Point(newX, newY); 
    }

    function randomizePoints(RandomCtx memory ctx, Point[] memory points, int16 deltaX, int16 deltaY, uint8 mergeMod) internal pure returns (Point[] memory) {
        Point[] memory newPoints = new Point[](points.length);

        for (uint8 i = 0; i < points.length; i++) {
            
            if ((i + 1) % mergeMod == 0) {
                newPoints[i] = Point(points[i].x, points[i].y);
            } else {
                newPoints[i] = randomizeSinglePoint(ctx, points[i], deltaX, deltaY);
            }
        }

        return newPoints;
    }

    function copyPoints(Point[] memory points) internal pure returns (Point[] memory) {
        Point[] memory newPoints = new Point[](points.length);

        for (uint8 i = 0; i < points.length; i++) {
            newPoints[i] = Point(points[i].x, points[i].y);
        }

        return newPoints;
    }

    function shiftPoints(Point[] memory points, int16 deltaX, int16 deltaY, uint8 mergeMod) internal pure returns (Point[] memory) {
        Point[] memory newPoints = new Point[](points.length);

        for (uint8 i = 0; i < points.length; i++) {

            if ((i + 1) % mergeMod == 0) {
                newPoints[i] = Point(points[i].x, points[i].y);
            } else {
                newPoints[i] = Point(points[i].x + deltaX, points[i].y + deltaY);
            }
        }

        return newPoints;
    }

}

File 14 of 16 : Division.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import "./Utils.sol";

library Division {
    using Utils for int256;

    function division(uint8 decimalPlaces, int256 numerator, int256 denominator) pure internal returns(int256 quotient, int256 remainder, string memory result) { unchecked {
        int256 factor = int256(10**decimalPlaces);
        quotient  = numerator / denominator;
        bool rounding = 2 * ((numerator * factor) % denominator) >= denominator;
        remainder = (numerator * factor / denominator) % factor;
        if (rounding) {
            remainder += 1;
        }
        result = string(abi.encodePacked(quotient.toString(), '.', numToFixedLengthStr(decimalPlaces, remainder)));
    }}

    function divisionStr(uint8 decimalPlaces, int256 numerator, int256 denominator) pure internal returns(string memory) {
        string memory result;
        (,,result) = division(decimalPlaces, numerator, denominator);
        return result;
    }

    function numToFixedLengthStr(uint256 decimalPlaces, int256 num) pure internal returns(string memory result) { unchecked {
        bytes memory byteString;
        for (uint256 i = 0; i < decimalPlaces; i++) {
            int256 remainder = num % 10;
            byteString = abi.encodePacked(remainder.toString(), byteString);
            num = num/10;
        }
        result = string(byteString);
    }}
}

File 15 of 16 : Filters.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import './IFilters.sol';
import './Filters-generated.sol';
import './FilterTraits.sol';
import './Random.sol';
import './RandomCtx.sol';
import './Utils.sol';

contract Filters is IFilters {
    using Utils for uint256; 

    function wrapInGandApplyFilter(string memory svgElement, string memory filterId) internal pure returns (string memory) {
        return  string.concat('<g filter="',string.concat('url(#', filterId, ')'),'">', svgElement, '</g>');
    }

    // function wrapInGandApplyFilter(string memory svgElement, Traits.FilterType filter) internal pure returns (string memory) {
    //     return wrapInGandApplyFilter(svgElement, getFilterId(filter));
    // }

    // function getFilterId(Traits.FilterType filter) internal pure returns (string memory) {
    //     // TODO: replace with just returing enum ID to string
    //     if (filter == Traits.FilterType.NONE) return "none";
    //     if (filter == Traits.FilterType.GRAYSCALE) return "grayscale";
    //     if (filter == Traits.FilterType.SEPIA) return "sepia";
    //     return "";
    // }
    
    function randBlendMode(RandomCtx memory rndCtx, bool noDifference) internal pure returns (string memory) {
        int256 mode = int8(Random.randWithProbabilities(rndCtx, hex"0A01010101"));

        if (mode == 1) return "screen";
        if (mode == 2) return "multiply";
        if (mode == 3) return "color-burn";

        if (noDifference) return "normal";

        if (mode == 4) return "difference";

        return "normal";           
    }

    function randomHtmlColor(RandomCtx memory rndCtx) internal pure returns (string memory) {
        int256 color = int8(Random.randWithProbabilities(rndCtx, hex"0A01010101"));

        if (color == 1) return "#F1E5AC";
        if (color == 2) return "#FAEDEB";
        if (color == 3) return "#FFE5B4";
        if (color == 4) return "#FCE8FF";

        return "white";                   
    }

    // following are to workaround a bug with viaIR, causing Random.sol to be dynamically linked, instead of inlining it
    function randUInt32(RandomCtx memory ctx) internal pure returns (uint32) {
        return Random.randUInt32(ctx);
    }

    function randFloat(RandomCtx memory ctx, uint8 decimalPlaces, int256 from, int256 to, int256 denominator) internal pure returns (string memory result) {
        return Random.randFloat(ctx, decimalPlaces, from, to, denominator);
    }

    function randRange(RandomCtx memory ctx, int256 from, int256 to) internal pure returns (int256) {
        return Random.randRange(ctx, from, to);
    }

    function randBool(RandomCtx memory ctx, int256 trueProbability) internal pure returns (bool) {
        return Random.randBool(ctx, trueProbability);
    }

    function generateFilterString(RandomCtx memory rndCtx, FilterType filter, string memory filterId) internal pure returns (string memory) {               
        if (filter == FilterType.NONE) return "";
        if (filter == FilterType.GRAYSCALE) return FiltersGenerated.createFilter_grayscale(filterId);
        if (filter == FilterType.SEPIA) return FiltersGenerated.createFilter_sepia(filterId);
        if (filter == FilterType.WATERCOLOR) {
            return FiltersGenerated.createFilter_watercolor(filterId,
                uint256(randUInt32(rndCtx)).toString(),                // seed 1
                uint256(randUInt32(rndCtx)).toString(),                // seed 2
                uint256(randRange(rndCtx, 1, 2)).toString(),           // glowRaius
                "out",
                randFloat(rndCtx, 2, 12, 20, 10),                      // colorK1
                randFloat(rndCtx, 2, 30, 70, 100),                     // colorK3
                randFloat(rndCtx, 2, 14, 20, 10)
            );
        }
        if (filter == FilterType.WATERCOLOR_BRIGHT) {
            if (randBool(rndCtx, 70)) {
                string memory operator = "atop";

                if (randBool(rndCtx, 50)) {
                    if (randBool(rndCtx, 50)) {
                        operator = "atop";
                    } else {
                        operator = "in";
                    }
                }

                return FiltersGenerated.createFilter_watercolor(filterId,
                    uint256(randUInt32(rndCtx)).toString(),              // seed 1
                    uint256(randUInt32(rndCtx)).toString(),              // seed 2
                    uint256(randRange(rndCtx, 1, 4)).toString(),         // glowRaius
                    operator,
                    randFloat(rndCtx, 2, 25, 35, 10),                    // colorK1
                    randFloat(rndCtx, 2, 250, 300, 100),                 // colorK3
                    "1.4"
                );
            } else {
                string memory operator = "in";

                if (randBool(rndCtx, 80)) {
                    operator = "atop";
                }

                return FiltersGenerated.createFilter_watercolor(filterId,
                    uint256(randUInt32(rndCtx)).toString(),              // seed 1
                    uint256(randUInt32(rndCtx)).toString(),              // seed 2
                    uint256(randRange(rndCtx, 1, 5)).toString(),         // glowRaius
                    operator,
                    randFloat(rndCtx, 2, 70, 120, 10),                   // colorK1
                    randFloat(rndCtx, 2, 300, 500, 100),                 // colorK3
                    randFloat(rndCtx, 2, 7, 18, 10)
                );

            }
        }

        if (filter == FilterType.STONE) {
            return FiltersGenerated.createFilter_stoneSurface(filterId,
                uint256(randUInt32(rndCtx)).toString(),              // seed 1
                randFloat(rndCtx, 5, 5, 15, 10000),
                uint256(randRange(rndCtx, 3, 4)).toString(),  
                uint256(randRange(rndCtx, 4, 10)).toString(),  
                randomHtmlColor(rndCtx),
                uint256(randRange(rndCtx, 4, 10)).toString()
            );
        }

        if (filter == FilterType.SMOKE) {
            return FiltersGenerated.createFilter_displacement2(filterId,
                uint256(randUInt32(rndCtx)).toString(),              // seed 1
                randFloat(rndCtx, 2, 2, 3, 100),                     // baseFreq
                uint256(randRange(rndCtx, 1, 4)).toString(),         // octaves
                uint256(randRange(rndCtx, 30, 50)).toString(),       // scale
                "2", 
                uint256(randRange(rndCtx, 5, 25)).toString(), 
                randBlendMode(rndCtx, false)
            );
        }

        if (filter == FilterType.DISPLACEMENT) {
            return FiltersGenerated.createFilter_displacement3(filterId,
                uint256(randUInt32(rndCtx)).toString(),              // seed 1
                randFloat(rndCtx, 2, 1, 5, 100),                     // baseFreq
                "5",                                                 //octaves
                uint256(randRange(rndCtx, 1, 10)).toString(),        // blur
                uint256(randRange(rndCtx, 40, 80)).toString()        // scale
            );
        }

        if (filter == FilterType.FROZEN) {
            return FiltersGenerated.createFilter_displacement2(filterId,
                uint256(randUInt32(rndCtx)).toString(),              // seed 1
                Random.randFloat(rndCtx, 2, 2, 3, 10),                     // baseFreq
                uint256(randRange(rndCtx, 1, 4)).toString(),         // octaves
                uint256(randRange(rndCtx, 20, 30)).toString(),       // scale
                "1", "1", 
                randBlendMode(rndCtx, false)
            );
        }

        if (filter == FilterType.SPLASH) {
            return FiltersGenerated.createFilter_displacement2(filterId,
                uint256(randUInt32(rndCtx)).toString(),              // seed 1
                "0.05",                                              // baseFreq
                uint256(randRange(rndCtx, 1, 4)).toString(),         // octaves
                uint256(randRange(rndCtx, 80, 100)).toString(),      // scale
                uint256(randRange(rndCtx, 1, 4)).toString(),  
                uint256(randRange(rndCtx, 3, 10)).toString(),  
                randBlendMode(rndCtx, false)
            );
        }

        if (filter == FilterType.RECOLORING) {
            string memory fx = randFloat(rndCtx, 4, 1, 10, 1000);
            string memory fy = fx;
            
            // if (Random.randBool(rndCtx, 20)) {
            //     fy = Random.randFloat(rndCtx, 4, 1, 10, 1000);
            // }

            return FiltersGenerated.createFilter_patternRecoloring(filterId,
                uint256(randUInt32(rndCtx)).toString(),              // seed 1
                fx,          
                fy
            );
        }

        if (filter == FilterType.SPARKS) {
            return FiltersGenerated.createFilter_sparks(filterId,
                uint256(randUInt32(rndCtx)).toString(),              // seed 1
                randFloat(rndCtx, 4, 1, 10, 1000),          
                uint256(randRange(rndCtx, 1, 10)).toString(),
                randFloat(rndCtx, 4, 2, 20, 10)   
            );
        }

        if (filter == FilterType.GLASS) {
            return FiltersGenerated.createFilter_glass(filterId);
        }

        if (filter == FilterType.SPREAD) {
            return FiltersGenerated.createFilter_spread(filterId,
                uint256(randRange(rndCtx, 1, 10)).toString(),
                randBlendMode(rndCtx, true)
            );
        }

        if (filter == FilterType.SPREAD_BRIGHT) {
            string memory offset = uint256(randRange(rndCtx, 1, 20)).toString();
            return FiltersGenerated.createFilter_spreadBright(filterId,
                uint256(randRange(rndCtx, 3, 10)).toString(),
                offset, offset
            );
        }

        if (filter == FilterType.INNER_OUTLINE) {
            return FiltersGenerated.createFilter_innerOutline1(filterId);
        }

        if (filter == FilterType.COLOR_OUTLINE) {
            return FiltersGenerated.createFilter_colorOutlineIn(filterId);
        }

        if (filter == FilterType.NOISE_SHADOW) {
            return FiltersGenerated.createFilter_noiseShadow(filterId,
                uint256(randRange(rndCtx, 5, 25)).toString(),
                uint256(randRange(rndCtx, 4, 11)).toString()
            );
        }

        if (filter == FilterType.RECOLORING2) {
            return FiltersGenerated.createFilter_recolor2(filterId,
                randFloat(rndCtx, 4, 1, 20, 1000),        
                uint256(randRange(rndCtx, 2, 6)).toString(),
                uint256(randUInt32(rndCtx)).toString()
            );
        }

        if (filter == FilterType.CHALK) {
            return FiltersGenerated.createFilter_chalk(filterId);
        }

        if (filter == FilterType.BLACK_OUTLINE) {
            return FiltersGenerated.createFilter_blackOutline(filterId,
                uint256(randRange(rndCtx, 1, 5)).toString(),
                uint256(randRange(rndCtx, 1, 3)).toString(),
                uint256(randRange(rndCtx, 8, 10)).toString()
            );
        }

        if (filter == FilterType.HALF_TONE) {
            return FiltersGenerated.createFilter_halftone(filterId);
        }

        return "";
    }

    function generateAllFilterStrings(RandomCtx memory rndCtx, FilterTraits memory filter) external pure override returns (string memory) {
        string memory mainFilter = generateFilterString(rndCtx, filter.mainFilterType, "mainF");

        string memory postFilter = filter.additonalLighting ? FiltersGenerated.createFilter_light("postF"): "";
        //string memory darkenFilter = filter.darken ? FiltersGenerated.createFilter_brightnessAndSaturation("darkenF", "1", "1"): "";

        return string.concat(mainFilter, " ", postFilter);//, " ", darkenFilter);
    }

    function applyAllFilters(FilterTraits memory filter, string memory svgElement) external pure override returns (string memory) {
        string memory result = svgElement;
        
        if (filter.mainFilterType != FilterType.NONE) {
            result = wrapInGandApplyFilter(result, "mainF");
        }

        if (filter.additonalLighting) {
            result = wrapInGandApplyFilter(result, "postF");
        }

        return result;
    }        
}

File 16 of 16 : Filters-generated.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import './SVG.sol';

/** AUTOGENERATED - DO NOT MODIFY THIS FILE DIRECTLY */

library FiltersGenerated {

    function createFilter_watercolor(string memory id, string memory seed1, string memory seed2, string memory glowRadius, string memory compositeOperator, string memory colorK1, string memory colorK3, string memory secondColorK3) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feTurbulence result="f0" type="fractalNoise" baseFrequency=".04" numOctaves="2" seed="', seed1, '"/>', 
            '<feTurbulence result="f15" type="fractalNoise" baseFrequency=".2" numOctaves="3" seed="', seed2, '"/>', 
            '<feComposite result="f17" in="SourceGraphic" in2="f0" operator="arithmetic" k1="0.5" k2="0.6" k4="-.07"/>'
            '<feMorphology id="water" result="f19" in="f17" operator="dilate" radius="1"/>'
            '<feDisplacementMap result="f19" in="f19" in2="f0" xChannelSelector="R" yChannelSelector="B" scale="2"/>'
            '<feDisplacementMap result="f19" in="f19" in2="f15" xChannelSelector="R" yChannelSelector="B" scale="4"/>'
            '<feDisplacementMap result="f16" in="f19" in2="f0" xChannelSelector="A" yChannelSelector="A" scale="6"/>'
            '<feGaussianBlur result="f16" in="f16" stdDeviation="1"/>'
            '<feComposite result="f19" in="f19" in2="f16" operator="arithmetic" k1="1.2" k2="-.25" k3="-.25" k4="0"/>'
            '<feDisplacementMap result="f18" in="f17" in2="f0" xChannelSelector="G" yChannelSelector="R" scale="4"/>'
            '<feDisplacementMap result="f18" in="f18" in2="f15" xChannelSelector="A" yChannelSelector="G" scale="2"/>'
            '<feDisplacementMap result="f20" in="f17" in2="f0" xChannelSelector="R" yChannelSelector="A" scale="16"/>'
            '<feMorphology result="f7" in="f20" operator="erode" radius="', glowRadius, '"/>', 
            '<feComposite result="f20" in="f20" in2="f7" operator="', compositeOperator);
        result = string.concat(result, '"/>', 
            '<feGaussianBlur result="f20" in="f20" stdDeviation="1.6"/>'
            '<feComposite id="color" result="f18" in="f18" in2="f20" operator="arithmetic" k1="', colorK1, '" k2="0" k3="', colorK3, '"/>', 
            '<feComposite in="f19" in2="f18" operator="arithmetic" k1="-0.8" k2="0.8" k3="', secondColorK3, '"/>', 
            '</filter>');
        return result;
    }

    function createFilter_displacement2(string memory id, string memory seed, string memory baseFreq, string memory octaves, string memory scale, string memory radius, string memory blur, string memory blendMode) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feTurbulence type="fractalNoise" seed="', seed, '" baseFrequency="', baseFreq, '" numOctaves="', octaves, '"/>', 
            '<feDisplacementMap in="SourceGraphic" xChannelSelector="G" scale="', scale, '" result="f8"/>', 
            '<feMorphology operator="dilate" radius="');
        result = string.concat(result, radius, '"/>', 
            '<feGaussianBlur stdDeviation="', blur, '"/>', 
            '<feBlend in2="f8" mode="', blendMode, '"/>', 
            '</filter>');
        return result;
    }

    function createFilter_displacement3(string memory id, string memory seed, string memory baseFreq, string memory octaves, string memory blur, string memory scale) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feTurbulence type="fractalNoise" seed="', seed, '" baseFrequency="', baseFreq, '" numOctaves="', octaves, '"/>', 
            '<feGaussianBlur result="f9" stdDeviation="', blur, '"/>', 
            '<feDisplacementMap in2="f9" in="SourceGraphic" xChannelSelector="R" yChannelSelector="B" scale="');
        result = string.concat(result, scale, '"/>', 
            '</filter>');
        return result;
    }

    function createFilter_patternRecoloring(string memory id, string memory seed, string memory freqX, string memory freqY) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feTurbulence seed="', seed, '" baseFrequency="', freqX, ' ', freqY, '"/>', 
            '<feColorMatrix values="0 0 0 2.5 0 0 2.5 0 0 0 0 0 1.5 0 0 0 0 0 0 2.5"/>'
            '<feComposite in2="SourceGraphic" operator="in"/>'
            '</filter>');
        return result;
    }

    function createFilter_stoneSurface(string memory id, string memory seed, string memory baseFreq, string memory diffuse, string memory surfaceScale, string memory color, string memory elevation) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feTurbulence type="fractalNoise" seed="', seed, '" baseFrequency="', baseFreq, '" numOctaves="9"/>', 
            '<feComponentTransfer>'
            '<feFuncA type="table" tableValues="0 .02 .1 .25 .2 .25 .3 .4 .5 .6 .7 .8"/>'
            '</feComponentTransfer>'
            '<feDiffuseLighting diffuseConstant="', diffuse, '" surfaceScale="', surfaceScale, '" lighting-color="', color);
        result = string.concat(result, '">', 
            '<feDistantLight elevation="', elevation, '"/>', 
            '</feDiffuseLighting>'
            '<feComposite in2="SourceGraphic" operator="in"/>'
            '</filter>');
        return result;
    }

    function createFilter_sparks(string memory id, string memory seed, string memory baseFreq, string memory octaves, string memory amplitude) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feTurbulence type="fractalNoise" seed="', seed, '" baseFrequency="', baseFreq, '" numOctaves="', octaves, '"/>', 
            '<feComponentTransfer>'
            '<feFuncA type="table" tableValues="0 1 0"/>'
            '</feComponentTransfer>'
            '<feColorMatrix values="0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 1"/>'
            '<feComponentTransfer>'
            '<feFuncR type="gamma" exponent="8"/>'
            '<feFuncG type="gamma" amplitude="', amplitude, '" exponent="32"/>', 
            '<feFuncB type="gamma" amplitude=".65" exponent="64"/>'
            '</feComponentTransfer>'
            '<feComposite in2="SourceGraphic" operator="in"/>'
            '<feBlend in2="SourceGraphic" mode="normal"/>'
            '</filter>');
        return result;
    }

    function createFilter_light(string memory id) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feGaussianBlur in="SourceAlpha" stdDeviation="3" result="f9"/>'
            '<feSpecularLighting in="f9" specularExponent="128" result="f11" lighting-color="white">'
            '<feDistantLight azimuth="225" elevation="70"/>'
            '</feSpecularLighting>'
            '<feComposite in="f11" in2="SourceAlpha" operator="in" result="f12"/>'
            '<feComposite in="SourceGraphic" in2="f12" operator="arithmetic" k2="1" k3="1"/>'
            '</filter>');
        return result;
    }

    function createFilter_sepia(string memory id) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" color-interpolation-filters="sRGB" x="-10%" y="-10%" height="120%" width="120%">', 
            '<feColorMatrix type="matrix" values="0.393 0.769 0.189 0 0 0.349 0.686 0.168 0 0 0.272 0.534 0.131 0 0 0 0 0 1 0"/>'
            '</filter>');
        return result;
    }

    function createFilter_grayscale(string memory id) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" color-interpolation-filters="sRGB" x="-10%" y="-10%" height="120%" width="120%">', 
            '<feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/>'
            '</filter>');
        return result;
    }

    function createFilter_glass(string memory id) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feBlend result="f24" mode="screen" in2="SourceGraphic"/>'
            '<feGaussianBlur stdDeviation="2" result="f9"/>'
            '<feComposite operator="xor" in="f9" in2="f24" result="f25"/>'
            '<feComposite result="f26" in="f25" operator="xor" in2="f25"/>'
            '<feGaussianBlur result="f9" stdDeviation="3" in="f26"/>'
            '<feSpecularLighting result="f11" specularExponent="55" specularConstant="1.5" surfaceScale="6" in="f9">'
            '<fePointLight z="20000" y="-8000" x="-5000"/>'
            '</feSpecularLighting>'
            '<feComposite in="f9" k3="1" k2="1.5" operator="arithmetic" in2="SourceGraphic" result="f25"/>'
            '<feComposite in="f11" operator="atop" in2="f25" result="f25"/>'
            '<feBlend mode="multiply" in2="f25"/>'
            '</filter>');
        return result;
    }

    function createFilter_spread(string memory id, string memory blur, string memory blendMode) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feGaussianBlur result="f27" stdDeviation="', blur, '" in="SourceGraphic"/>', 
            '<feComposite result="f28" operator="in" in2="f27" in="f27"/>'
            '<feComposite result="f29" operator="arithmetic" k2="1" in2="f28" in="f28"/>'
            '<feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 10 0" in="f29"/>'
            '<feGaussianBlur result="f30" stdDeviation="1"/>'
            '<feColorMatrix result="f31" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 5 -1" in="f30"/>'
            '<feBlend result="f32" mode="', blendMode, '" in2="f31" in="SourceGraphic"/>', 
            '<feComposite operator="in" in2="f31" in="f32"/>'
            '</filter>');
        return result;
    }

    function createFilter_spreadBright(string memory id, string memory blur, string memory offsetX, string memory offsetY) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feGaussianBlur result="f9" stdDeviation="', blur, '"/>', 
            '<feOffset result="f33" dx="', offsetX, '" dy="', offsetY, '"/>', 
            '<feComposite in2="f9" operator="in" in="f33" result="f26"/>'
            '<feBlend in2="f26" mode="difference" in="f26" result="f24"/>'
            '<feConvolveMatrix result="f25" in="f24" targetX="0" bias="0" divisor="2" kernelMatrix="-2 0 2 0 4 0 2 0 2" order="3 3"/>'
            '<feComposite in2="f25" operator="atop" in="f26"/>'
            '</filter>');
        return result;
    }

    function createFilter_innerOutline1(string memory id) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feGaussianBlur stdDeviation="2" in="SourceAlpha" result="f9"/>'
            '<feComposite in2="f9" operator="arithmetic" k1="-1" k2="3.2" k4="-2"/>'
            '<feColorMatrix values="0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 10 0" result="f26"/>'
            '<feComposite in2="f26" operator="out" in="SourceGraphic" result="f34"/>'
            '<feBlend in2="f34" mode="darken" in="f34"/>'
            '</filter>');
        return result;
    }

    function createFilter_colorOutlineIn(string memory id) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feGaussianBlur stdDeviation="2"/>'
            '<feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 10 -9" result="f26"/>'
            '<feComposite in2="f26" operator="in" in="SourceGraphic" result="f35"/>'
            '<feFlood in="f35"/>'
            '<feComposite in2="SourceGraphic" operator="in" result="f26"/>'
            '<feBlend in2="f26" mode="normal" in="f35"/>'
            '</filter>');
        return result;
    }

    function createFilter_recolor2(string memory id, string memory baseFreq, string memory octaves, string memory seed) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feGaussianBlur result="f9" stdDeviation="5"/>'
            '<feTurbulence baseFrequency="', baseFreq, '" numOctaves="', octaves, '" type="turbulence" result="f36" seed="', seed, '"/>', 
            '<feComposite in2="f9" operator="in" in="f9" result="f25"/>'
            '<feDisplacementMap in2="f25" scale="120" xChannelSelector="A" yChannelSelector="A" in="f36" result="f37"/>'
            '<feComposite in2="f37" operator="arithmetic" k1="2" k2=".25" k3="2.5" in="SourceGraphic" result="f25"/>'
            '<feComposite in2="SourceGraphic" operator="in" in="f25" result="f25"/>'
            '<feBlend in2="f25" mode="screen" result="f24"/>'
            '<feBlend in2="f24" mode="multiply"/>'
            '</filter>');
        return result;
    }

    function createFilter_chalk(string memory id) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feTurbulence result="f36" numOctaves="5" seed="0" type="fractalNoise" baseFrequency=".4"/>'
            '<feOffset dy="-5" dx="-5"/>'
            '<feDisplacementMap in2="f36" scale="30" xChannelSelector="R" yChannelSelector="G" in="SourceGraphic"/>'
            '</filter>');
        return result;
    }

    function createFilter_noiseShadow(string memory id, string memory stdDeviation, string memory slope) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-50%" y="-50%" width="200%" height="200%" color-interpolation-filters="sRGB">', 
            '<feOffset in="SourceAlpha" dx="0" dy="0" result="f33"/>'
            '<feGaussianBlur in="f33" stdDeviation="', stdDeviation, '" result="f9"/>', 
            '<feComponentTransfer in="f9" result="f38">'
            '<feFuncA type="table" tableValues=".65 .68 .75 .95 1 1 1"/>'
            '</feComponentTransfer>'
            '<feComposite operator="in" in="f9" in2="f38" result="f26"/>'
            '<feComponentTransfer in="f26" result="f38">'
            '<feFuncA type="linear" slope="', slope, '"/>', 
            '</feComponentTransfer>'
            '<feColorMatrix in="f38" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.48 0" result="f39"/>'
            '<feTurbulence result="f40" type="fractalNoise" numOctaves="6" baseFrequency="1.98" seed="777"/>'
            '<feColorMatrix in="f40" type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 7 -3" result="f41"/>'
            '<feComposite operator="arithmetic" in="f39" in2="f41" k1="0.51" k2="0.49" result="f42"/>'
            '<feMerge>'
            '<feMergeNode in="f42"/>'
            '<feMergeNode in="SourceGraphic"/>'
            '</feMerge>'
            '</filter>');
        return result;
    }

    function createFilter_blackOutline(string memory id, string memory stdDeviation, string memory k2, string memory k3) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feGaussianBlur result="f9" in="SourceAlpha" stdDeviation="', stdDeviation, '"/>', 
            '<feComposite in2="f9" operator="arithmetic" k2="', k2, '" k3="', k3, '" in="SourceGraphic" result="f26"/>', 
            '<feComposite in2="f26" operator="in" in="f26"/>'
            '</filter>');
        return result;
    }

    function createFilter_halftone(string memory id) internal pure returns (string memory) {
        string memory result = string.concat(
            '<filter id="', id, '" x="-10%" y="-10%" height="120%" width="120%" color-interpolation-filters="sRGB">', 
            '<feTurbulence type="fractalNoise" baseFrequency="0.7" numOctaves="8"/>'
            '<feColorMatrix type="saturate" values="0"/>'
            '<feComponentTransfer result="f43">'
            '<feFuncR type="discrete" tableValues="0 0 0 0 0 0 0 0 1 1"/>'
            '<feFuncG type="discrete" tableValues="0 0 0 0 0 0 0 0 1 1"/>'
            '<feFuncB type="discrete" tableValues="0 0 0 0 0 0 0 0 1 1"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f43">'
            '<feFuncA in="f43" type="discrete" tableValues="1 1 0 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer result="f44">'
            '<feFuncA type="table" tableValues="0 0 0 0 0 0 0 1"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f43">'
            '<feFuncA in="f43" type="discrete" tableValues="1 1 1 1 0 0 0 0 0 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer result="f45">'
            '<feFuncA type="table" tableValues="0 0 0 0 0 0 0 1"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f43">'
            '<feFuncA in="f43" type="discrete" tableValues="1 1 1 1 1 0 0 0 0 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer result="f46">'
            '<feFuncA type="table" tableValues="0 0 0 0 0 0 0 1"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f43">'
            '<feFuncA in="f43" type="discrete" tableValues="1 1 1 1 1 1 0 0 0 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer result="f47">'
            '<feFuncA type="table" tableValues="0 0 0 0 0 0 0 1"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f43">'
            '<feFuncA in="f43" type="discrete" tableValues="1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer result="f48">'
            '<feFuncA type="table" tableValues="0 0 0 0 0 0 0 1"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f43">'
            '<feFuncA in="f43" type="discrete" tableValues="1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer result="f49">'
            '<feFuncA type="table" tableValues="0 0 0 0 0 0 0 1"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f43">'
            '<feFuncA type="discrete" tableValues="0 0 1 1 1 1 1"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer result="f50">'
            '<feFuncA type="table" tableValues="0 0 0 0 0 0 1"/>'
            '</feComponentTransfer>'
            '<feColorMatrix in="SourceGraphic" type="luminanceToAlpha" result="neg-lum-map"/>'
            '<feComponentTransfer result="f51">'
            '<feFuncA type="table" tableValues="1 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f51" result="f52">'
            '<feFuncA type="discrete" tableValues="0 1 0 0 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f51" result="f53">'
            '<feFuncA type="discrete" tableValues="0 0 1 0 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f51" result="f54">'
            '<feFuncA type="discrete" tableValues="0 0 0 1 0 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f51" result="f55">'
            '<feFuncA type="discrete" tableValues="0 0 0 0 1 0 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f51" result="f56">'
            '<feFuncA type="discrete" tableValues="0 0 0 0 0 1 0 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f51" result="f57">'
            '<feFuncA type="discrete" tableValues="0 0 0 0 0 0 1 0"/>'
            '</feComponentTransfer>'
            '<feComponentTransfer in="f51" result="f58">'
            '<feFuncA type="discrete" tableValues="0 0 0 0 0 0 0 1"/>'
            '</feComponentTransfer>'
            '<feComposite operator="in" in="f52" in2="f44" result="f59"/>'
            '<feComposite operator="in" in="f53" in2="f45" result="f60"/>'
            '<feComposite operator="in" in="f54" in2="f46" result="f61"/>'
            '<feComposite operator="in" in="f55" in2="f47" result="f62"/>'
            '<feComposite operator="in" in="f56" in2="f48" result="f63"/>'
            '<feComposite operator="in" in="f57" in2="f49" result="f64"/>'
            '<feComposite operator="in" in="f58" in2="f50" result="f65"/>'
            '<feMerge>'
            '<feMergeNode in="f65"/>'
            '<feMergeNode in="f64"/>'
            '<feMergeNode in="f63"/>'
            '<feMergeNode in="f62"/>'
            '<feMergeNode in="f61"/>'
            '<feMergeNode in="f60"/>'
            '<feMergeNode in="f59"/>'
            '</feMerge>'
            '<feComposite operator="in" in2="SourceGraphic"/>'
            '</filter>');
        return result;
    }
}

Settings
{
  "remappings": [
    "@openzeppelin/=node_modules/@openzeppelin/",
    "erc721a/=node_modules/erc721a/",
    "forge-std/=lib/forge-std/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100,
    "details": {
      "peephole": true,
      "inliner": true,
      "deduplicate": true,
      "cse": true,
      "yul": true,
      "yulDetails": {
        "stackAllocation": true,
        "optimizerSteps": "[fv][edjr]T[secxL]d[fv][edjr]T[secxL]d"
      }
    }
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"contract IFilters","name":"_filters","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"renderAsSvg","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"}],"name":"renderAsSvgAndAttributes","outputs":[{"internalType":"string","name":"result","type":"string"},{"internalType":"string","name":"attributes","type":"string"}],"stateMutability":"view","type":"function"}]

Deployed Bytecode

0x60a0604052600436101561001257600080fd5b60003560e01c806322dac39314610032576384ab67b50361003757610106565b6100ca565b600080fd5b905035905b565b90602082820312610037576100579161003c565b90565b91909160005b8281106100705750506000910152565b8082015181850152602001610060565b8051808352909161009a908290602001809460200161005a565b601f01601f19160190565b90916100bc61005793604090818501918552610080565b918083039060200152610080565b34610037576100e26100dd366004610043565b6107ae565b906100f2604051928392836100a5565b0390f35b6100579160208083019252610080565b34610037576100f261012161011c366004610043565b61012d565b604051918291826100f6565b61013690610382565b5090565b634e487b7160e01b60009081526041600452602490fd5b90601f1990601f0116810190811067ffffffffffffffff82111761017457604052565b61013a565b906100416040519283610151565b610192610220610179565b9060008083528060208401528060408401528060608401528060808401528060a08401528060c08401528060e08401528061010084015280610120840152806101408401528061016084015280610180840152806101a0840152806101c0840152806101e0840152610200830152565b61020d6102e0610179565b906000808352806020840152806040840152606081818501528160808501528160a08501528160c08501528060e085015280610100850152610120840152806101408401528061016084015280610180840152806101a0840152806101c0840152806101e08401528061020084015280610220840152806102408401528061026084015280610280840152806102a08401526102c0830152565b6102b2610240610179565b90600080835280602084015280604084015260609080828501528060808501528060a08501528060c08501528060e08501528061010085015280610120850152806101408501528061016085015280610180850152806101a08501526101c0840152806101e084015280610200840152610220830152565b6103346040610179565b9060008083526020830152565b61034b6080610179565b90610354610187565b825260208201610362610202565b9052604082016103706102a7565b90526060820161037e61032a565b9052565b6103a06103a891610391610341565b5061039a61032a565b506103ab565b919082610606565b91565b6103c6906103b761032a565b506103c0610341565b50611a12565b9061005782613878565b67ffffffffffffffff811161017457601f1990601f011660200190565b906103ff6103fa836103d0565b610179565b918252565b61005760006103ed565b61041860056103ed565b64736861706560d81b602082015290565b610433600b6103ed565b6a706c61636564536861706560a81b602082015290565b9092919261045a6103fa826103d0565b93818552602085019082840111610037576100419261005a565b9080601f830112156100375781516100579260200161044a565b9060208282031261003757815167ffffffffffffffff8111610037576100579201610474565b634e487b7160e01b60009081526021600452602490fd5b601611156104d557565b6104b4565b90610041826104cb565b61037e906104da565b90610041916104fd8282516104e4565b6020908101511515910152565b916100579260609061051f81838101956104ed565b60400152610080565b6040513d6000823e3d90fd5b61053e60026103ed565b61673160f01b602082015290565b9161004191939261056b81608081019680518252602090810151910152565b604001906104ed565b61058581518093809360200161005a565b0190565b61059f9261059f61059f92610057979694610574565b90610574565b9392610041926105bc926040519660208801610589565b839003601f198101845283610151565b9161059f6105de949361059f93610574565b651e17b9bb339f60d11b815260060190565b6040519392610041926105bc92602087016105cc565b9061067c6000926106178184611d85565b610622959195610404565b5061062b610404565b50610647835161063961040e565b610641610429565b906130c9565b91909560018060a01b038454169060608601928351908660409a8b51809b8192631b5d88c560e01b835260049687840161050a565b0381875afa98891561076c578799610771575b50918693916106cd8b6106bd8b9c9d966106b76106e99d6102c09060200151015161ffff1690565b90611d30565b9b01516106c8610534565b6108af565b9551935163e9470c3360e01b815298899485938493840161054c565b03915afa801561076c576100579761073c958561072f9693610742575b50610729919050610724611a5960f21b61071e61040e565b90611c0a565b611a3a565b916105a5565b610737610404565b611a49565b906105f0565b61072992935090610764913d8091833e61075c8183610151565b81019061048e565b919038610706565b610528565b889a93916106e9999a50846106bd6106cd926106b761079c8d9a98963d808d833e61075c8183610151565b9e5050949650509b995050919361068f565b906107c4610057926107be610341565b50610382565b929092614f94565b600611156104d557565b90610041826107cc565b61005790516107d6565b634e487b7160e01b60009081526011600452602490fd5b61ffff918216908216019190821161081557565b6107ea565b67ffffffffffffffff81116101745760051b60200190565b906103ff6103fa8361081a565b60005b82811061084e57505050565b606081830152602001610842565b9061004161087261086c84610832565b9361081a565b601f19016020840161083f565b634e487b7160e01b60009081526032600452602490fd5b9081518110156108aa5760051b6020010190565b61087f565b906101c081016108bf81516107d6565b6000906108d46108ce836107d6565b916107d6565b14610ad4576108e382516107d6565b906002916108f36108ce846107d6565b0361095857505050610057916101608201610952610913825161ffff1690565b6101e085019061094961093861092f845161ffff8095166119f4565b955161ffff1690565b6101a088015161ffff165b90610801565b915191166119f4565b9161117b565b61096283516107d6565b926001936109726108ce866107d6565b03610a2257506100579491610a0a610a1c92610a03610991600361085c565b966109f2886101608b016109498c6109436109e56109b1855161ffff1690565b946109dc8b6101e08601986109cc8a5161ffff809b166119f4565b6109d68383610896565b52610896565b505161ffff1690565b916101a0015161ffff1690565b6109fc828a610896565b5287610896565b5085610896565b51610a158286610896565b5283610896565b50611304565b92505050610a3081516107d6565b610a3d6108ce60056107d6565b03610a4c57506100579161126e565b610a5690516107d6565b610a636108ce60036107d6565b14610a975761005791610a91610a7f61016084015161ffff1690565b60056102208501519161ffff1661191a565b90611304565b61005791610a91610aae61016084015161ffff1690565b6101a08401516101808501516101e08601519260ff9091169161ffff9081169116611899565b5050610057916101608201610b2d610aee825161ffff1690565b610b20610b11610b0861020088015161ffff8095166119f4565b945161ffff1690565b6101a087015161ffff16610943565b6101e086015191166119f4565b91610bd9565b606090610b3f82610179565b918083528060208401526040830152565b60005b828110610b5f57505050565b610b67610b33565b81830152602001610b53565b90610041610b8361086c84610832565b601f190160208401610b50565b610b9a60026103ed565b61343560f01b602082015290565b610bb260036103ed565b6203130360ec1b602082015290565b610bcb60026103ed565b61353560f01b602082015290565b610c806100579493610c73610bee6002610b73565b94610c656060610c05610c0082610179565b938452565b610c57610c52610c13610b90565b92610c2060209485880152565b610c4c8b610c2c610ba8565b97610c396040998a830152565b610c4560008093610896565b528c610896565b50610179565b958652565b610c5f610bc1565b90850152565b610c6d610ba8565b90830152565b600190610a158286610896565b50610e69565b610c8f91610574565b602560f81b81525b60010190565b906100416105bc6040519360208501610c86565b61059f906100579392610574565b91906105bc610041916040519460208601610cb1565b610cdf60076103ed565b661c99599b1958dd60ca1b602082015290565b610cfc60066103ed565b651c995c19585d60d21b602082015290565b660e4dee8c2e8ca560cb1b9052565b90610d3491610d2b81610d0e565b60070190610574565b6a2c20302e352c20302e352960a81b8152600b0190565b906100416105bc6040519360208501610d1d565b610dae939261059f610d749261059f94610574565b7f2066783d22353025222066793d22353025222063783d22353025222063793d228152671a98129110391e9160c11b602082015260280190565b6201291160ed1b815260030190565b6040519392610041926105bc9260208701610d5f565b92610ded610e4496959361059f610e2496610e1295610574565b7510103c189e91181291103c989e91181291103c191e9160511b815260160190610574565b661291103c991e9160c91b8152610d2b565b7012911039b83932b0b226b2ba3437b21e9160791b815260110190610574565b601160f91b8152610c97565b949390926105bc92610041946040519760208901610dd3565b9092610e73610404565b916000925b8551841015610ee857610ede90610ed86020610e94878a610896565b510151610ea1878a610896565b5151610ed2610ebd6040610eb58b8e610896565b510151610c9d565b6b73746f702d6f70616369747960a01b611c0a565b91611ade565b90610cbf565b9260010192610e78565b925092909350610f2760208201610f00815160ff1690565b90600060ff80931615610fde575b600292610f1e6064935160ff1690565b16900b91611039565b915050610f32610cd5565b91610f406060820151151590565b15610fce575b604081015115610fa3579080610f9e9392610f98610f7e610f79610f726100579a611a5960f21b611c0a565b9351615d58565b610d4b565b706772616469656e745472616e73666f726d60781b611c0a565b90610e50565b611a77565b610fc99250610fc3610f7e610f79610f7261005798611a5960f21b611c0a565b90610dbd565b611a5b565b9150610fd8610cf2565b91610f46565b60018252610f0e565b634e487b7160e01b60009081526012600452602490fd5b8115611008570590565b610fe7565b8115611008570790565b601760f91b8152610c97565b61103461059f916100579493610574565b611017565b90929060ff8116600a0a9261104e8186610ffe565b906110788583970295611073611064848961100d565b9380600195861b121598610ffe565b61100d565b80956110ba575b50506110aa61109d8561109461005794615d58565b9460ff166110c8565b6040519360208501611023565b829003601f198101835282610151565b0193506110aa61109d61107f565b9160009060605b848310156111155761110a90600a906111046110aa6110f66110f1858a61100d565b615d58565b926040519360208501610cb1565b94610ffe565b9160010191926110cf565b9350915050565b61112660016103ed565b600360fc1b602082015290565b61113d60026103ed565b61333360f01b602082015290565b61115560026103ed565b61034360f41b602082015290565b61116d60026103ed565b611b1b60f11b602082015290565b610c8061005794936112616111906004610b73565b94610c65606091611259610c526111a685610179565b946111af848752565b610c4c8b6111bb61111c565b956111c8602097888b0152565b6111ed6111d3610ba8565b996111e060409b8c830152565b600090610a158286610896565b506112246111fa85610179565b6112028d8252565b61121361120d611133565b8a830152565b610c7361121e61114b565b8c830152565b506112316103ff85610179565b61124261123c611163565b88830152565b61124d61120d61114b565b610c4560028093610896565b610c5f610ba8565b600390610a158286610896565b90610057916112806080830151151590565b156112ad57610a9161129861016084015161ffff1690565b60146012906101e08601519261ffff16611899565b610a916112c061016084015161ffff1690565b600a6024906101e08601519261ffff16611899565b8115611008570490565b600411156104d557565b90610041826112df565b60ff91821691168115611008570690565b9290600060ff61131961012086015160ff1690565b16611857575b6113599060ff61014086015116801515908161184c575b501561183f5760020160ff165b6080850151611831575b60ff8351911601610b73565b600060805260ff61136f61012086015160ff1690565b16611786575b6113d060ff80606461138c61012089015160ff1690565b9003168161139f61014089015160ff1690565b60640316900316620186a084516113b96080890151151590565b1561177d5760ff60015b16016000190191026112d5565b91620186a060ff6113e661012088015160ff1690565b160291620186a060ff6113fd60a089015160ff1690565b160290848211611775575b61143b620186a060ff6114236101008b9a989a015160ff1690565b60e08b015160ff1690031602845160001901906112d5565b96620186a060ff61145060e084015160ff1690565b1602946000975b855160ff8a1610156116755760018a8960ff9389898e898e600061147e60c08401516112e9565b61149161148b60006112e9565b916112e9565b0361159d575050886114ac610100620186a093015160ff1690565b16025b6115208c6114bf8b851687610896565b516115006114d2620186a08a600261185f565b6114f96114e4620186a088600261185f565b916114f2610c526060610179565b6020850152565b6040830152565b6115198c608051818e6080510116608052168093610896565b528d610896565b5060808b015161153c575b505050500198019901169795611457565b6114f96114e4620186a061156a8161155d611593998f611572991690610896565b51978b8b0103600261185f565b93600261185f565b8660805181886080510116608052169061158c828c610896565b5289610896565b5089898e3861152b565b6115aa60c08401516112e9565b6115b661148b8c6112e9565b036115c3575090506114af565b6115d060c08401516112e9565b6115dd61148b60026112e9565b036116145750620186a08a61160960e083836115fe61010089015160ff1690565b160295015160ff1690565b1602900390036114af565b91611623915060c001516112e9565b61163061148b60036112e9565b036114af5750876116426002836112f3565b1661166157620186a08861165a60e08d015160ff1690565b16026114af565b620186a08861165a6101008d015160ff1690565b509894509550935050610057945060ff61014085015116801515908161176a575b506116a15750610e69565b61174a6116b382516000190183610896565b516117426116d160ff6116cb6101408a015160ff1690565b16615d0e565b611259610c5261173b6060976116ee6116e98a610179565b978852565b6116fa60209586890152565b61172e8b61170661111c565b9861171360409a8b830152565b610c4560ff6080518160016080510116608052168093610896565b5080516000190190610896565b5196610179565b610c6d61111c565b61175660805184610896565b5261176360805183610896565b5038610c80565b606491501038611696565b849150611408565b60ff60006113c3565b611791600083610896565b5161179f6103ff6060610179565b6117b16117aa61111c565b6020830152565b6117bc6114f961111c565b6117c7600083610896565b526117d3600082610896565b506117df600083610896565b516118046117f760ff6116cb61012089015160ff1690565b6117aa610c006060610179565b61180f6114f961111c565b600260805261181f600183610896565b5261182b600182610896565b50611375565b60ff9081845116011661134d565b6064610140860152611343565b905060641138611336565b50600261131f565b9061186a9291611039565b91505090565b60ff8091169081146108155760010190565b9190820180921161081557565b8115611008570690565b93929190916118a78261085c565b906000955b60ff8716848110156118fd57816118de6118f1926118cd866118f7966119f4565b6118d78289610896565b5286610896565b506118ec8761016892611882565b61188f565b96611870565b956118ac565b5091955050505050565b8181029291811591840414171561081557565b61005792919061192e620186a06089611907565b909190939261193c8261085c565b906000935b60ff8516848110156118fd5761197d888361197761198e9461196688611994986119f4565b611970828b610896565b5288610896565b50611907565b6118ec61016891620186a0906112d5565b94611870565b93611941565b600b60fa1b9052565b630d0e6d8560e31b81526119d3916119be9160040190610574565b916119c88361199a565b600180930190610574565b602960f81b81520190565b91906105bc6100419160405194602086016119a3565b90611a0d611a08610057936101689061188f565b615d0e565b6119de565b611a1a61032a565b50610057611a2d82640130a55e17611907565b6117aa610c006040610179565b6100579190606760f81b611b98565b6100579190636465667360e01b611b98565b61005791906d1c98591a585b11dc98591a595b9d60921b611b98565b61005791906d1b1a5b99585c91dc98591a595b9d60921b611b98565b600160fd1b9052565b611aae611ab891610057959493610574565b916119c883611a93565b611ac181611a93565b0190610574565b6040519392610041926105bc9260208701611a9c565b9161005792611b18611b09611b03611b1e956939ba37b816b1b7b637b960b11b611c0a565b92610c9d565b651bd9999cd95d60d21b611c0a565b90611ac8565b63073746f760e41b611bac565b92611b6491611b58611ab89694611b7696611b4981600f60fa1b9052565b60019889948580930190610574565b601f60f91b8152611ac1565b613c2f60f01b81525b60020190610574565b601f60f91b81520190565b9392610041926105bc926040519660208801611b2b565b91611ba561005793615c33565b9182611b81565b61005791611bb8610404565b91611b98565b611bcc90611be59392610574565b603d60f81b8152601160f91b6001918201908152611ac1565b61011160f51b81525b60020190565b91906105bc610041916040519460208601611bbe565b90611c1761005792615c33565b611bf4565b92611ab8611aae611ca1610057979461059f611cb597987f3c73766720786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323081527f30302f73766722207072657365727665417370656374526174696f3d22784d696020820152756e594d696e206d656574222076696577426f783d222d60501b604082015260560190565b61202d60f01b815295600280970190610574565b61011160f51b8152017f7374796c653d226261636b67726f756e643a236565652220786d6c6e733a786c81527f696e6b3d22687474703a2f2f7777772e77332e6f72672f313939392f786c696e60208201526235911f60e91b604082015260430190565b9392610041926105bc926040519660208801611c1c565b61005791611d489160011b60649161ffff16026112d5565b611d5f611d59611a086002846112d5565b91615d0e565b818192611d19565b600311156104d557565b9061004182611d67565b6100579051611d71565b9190611d9560208201518461207c565b91611d9e610404565b9261ffff611db66101606040860151015161ffff1690565b1694611dc9611dc483612492565b615b67565b90611dd9602086015183836125ca565b6000965b60ff6001611df3608060208a0151015160ff1690565b011660ff89161015611fa35793611e08610404565b906000955b602088015160a081015160ff9081169089161015611f91578a89899260ff831615611f47575b5091611ea29160019360ff95610168808211611f38575b508b81838c868b16611ed457918991611e6293612859565b91611e74610140602086015101611d7b565b611e87611e816002611d71565b91611d71565b03611eac57611e996032601e8e611faf565b01995b8b612a20565b9701169591611e0d565b506040830151610160015161ffff1661ffff611ecb603260008f611faf565b91160199611e9c565b91600091611ee193612859565b91611ef3610140602086015101611d7b565b611f00611e816002611d71565b03611f1957611f12600460008e611faf565b0199611e9c565b506040830151610160015161ffff1661ffff611ecb601e60008f611faf565b611f419161188f565b38611e4a565b60c0015160ff935083169150611f5a9050565b1660ff88161015611f6e57868a8938611e33565b9550979690611f7f91939293610cbf565b9560ff90600101169693919091611ddd565b509550979690611f7f91939293610cbf565b50509450509190509190565b916105859192818413611fd6575b611fc690612000565b6001908490821c9203019061100d565b839150611fbd565b60001981146108155760010190565b9081526040810192916100419160200152565b61203860208083019061201a6120168351611fde565b8352565b6120316110aa855193516040519384019485611fed565b5190208252565b5190565b60005b82811061204b57505050565b61205361032a565b8183015260200161203f565b9061004161206f61086c84610832565b601f19016020840161203c565b61209260ff61208c845160ff1690565b1661205f565b61209f60c8606484611faf565b6002810a936120b16020820151611d71565b6120be611e816000611d71565b14612459575b60005b60ff6120d4835160ff1690565b1660ff821610156124505760ff811661212c5760ff90606319612108611a2d61012c61210181858c611faf565b938a611faf565b61211483831687610896565b5261212182821686610896565b505b600101166120c7565b949091612149600261214083600089611faf565b9060ff16900a90565b808303600080821315612445575061216361216991615b67565b91615b67565b90612175603288612473565b604086015161243d575b61218a603289612473565b906121986060880151151590565b612434575b896121e360206121c660ff6121b8600019860182168e610896565b51519460001901168c610896565b510151868502016117aa6121da6040610179565b93878602018452565b6121f060ff8c168a610896565b526121fe60ff8b1689610896565b50600260ff8b1610156122b8575b5050505061221d6020840151611d71565b61222a611e816000611d71565b0361224b57505061223e60c8605085611faf565b9060ff6002830a95612123565b91909461225b6020830151611d71565b612268611e816001611d71565b03612276575b60ff90612123565b6122836020830151611d71565b612290611e816002611d71565b0361226e579450906122ab906202780b02620186a090610ffe565b6002810a9490919061226e565b61232b8861232560028d60206123176122f8846122d860ff861689610896565b51516122eb600119870160ff168a610896565b515190039060ff16900a90565b9560ff8361230882871684610896565b51015194600119011690610896565b51015190039060ff16900a90565b01615b67565b8a896050831061233e575b50505061220c565b61232560026123c09361235860ff60001983011685610896565b51518988600003020161238e602061237760ff60001986011688610896565b5101518c8b60000302016117aa610c006040610179565b61239b60ff831686610896565b526123a960ff821685610896565b5060206123176122f8846122d860ff861689610896565b106123ce575b808a89612336565b61240f926123e36000198c0160ff168a610896565b51519102019260206123fc6000198c0160ff168a610896565b5101519102016117aa610c006040610179565b61241c60ff881686610896565b5261242a60ff871685610896565b50388080806123c6565b6001915061219d565b50600161217f565b916121699150615b67565b50505091505090565b93505061246960c8607884611faf565b6002810a936120c4565b90612482606460018094611faf565b131561248b5790565b5060001990565b90600090815b835160ff8216908110156124f1576124d46124b38287610896565b515160029081900a926124c860209189610896565b5101519060ff16900a90565b018381116124e9575b5060010160ff16612498565b9250386124dd565b5050915090565b61250260026103ed565b61062760f31b602082015290565b61251a60016103ed565b603360f81b602082015290565b61253160026103ed565b61323560f01b602082015290565b61254960016103ed565b603560f81b602082015290565b61256060016103ed565b600760fb1b602082015290565b61257760016103ed565b600d60fa1b602082015290565b61258e60026103ed565b61031360f41b602082015290565b6125a660016103ed565b601960f91b602082015290565b6125bd60016103ed565b603160f81b602082015290565b906064811015612627575061260860e08301926125ec6125e86125b3565b8552565b61010001916126006125fc6125b3565b8452565b601490612747565b612610575050565b61261f6100419261037e61259c565b61037e61259c565b61012c811015612674575061265560e08301926126456125e8612510565b61010001916126006125fc61259c565b61265d575050565b61266c6100419261037e61253f565b61037e612510565b6101f48110156126c157506126a260e08301926126926125e8612556565b61010001916126006125fc61256d565b6126aa575050565b6126b96100419261037e612584565b61037e61253f565b6103e881101561270657506126ef60e08301926126df6125e86124f8565b61010001916126006125fc612510565b6126f7575050565b6126b96100419261037e612527565b9061273f916127396002926127286127216041602884611faf565b848661185f565b60e08701526102589060c890611faf565b9161185f565b906101000152565b61275690606490600190611faf565b131590565b61276560126103ed565b71039ba3937b5b29e913ab9361411b3989491160751b602082015290565b610dae91610574565b906100416105bc6040519360208501612783565b916127bf6127dd9261005795946127b681611a93565b60010190610574565b6d39ba3937b5b296bbb4b23a341e9160911b81525b600e0190610574565b6a111037b830b1b4ba3c9e9160a91b81525b600b0190610574565b6040519392610041926105bc92602087016127a0565b916128246127dd9261005795946127b681611a93565b6e1039ba3937b5b296bbb4b23a341e9160891b81525b600f0190610574565b6040519392610041926105bc926020870161280e565b929190612864610404565b50610140916128796020830193845101611d7b565b612886611e816000611d71565b036128f657505061289561275b565b915b156128cb576128c56128c06128b461005795605a90605090611faf565b925160e0015192615d0e565b61278c565b91612843565b6128f06128c06128e361005795600d90600a90611faf565b9251610100015192615d0e565b916127f8565b6129269161290661291792615d0e565b906101e090604001510151906119de565b657374726f6b6560d01b611c0a565b91612897565b600191820b910b8115611008570590565b6129ba926129666100579695936129929361295d8162321e9160e91b9052565b60030190610574565b7f222066696c6c3d2275726c2823673129222066696c6c2d6f7061636974793d22815260200190610574565b780111039ba3937b5b296b634b732b1b0b81e913937bab732111603d1b815260190190610574565b6127b681611a93565b9392610041926105bc92604051966020880161293d565b6129e8906129fd9392610574565b6501e3830ba34160d51b815260060190610574565b61179f60f11b8152611bee565b91906105bc6100419160405194602086016129da565b612ae2949261005797612ab793976000956101809060200196612a47885192830151151590565b15612ae85750505050612a94916101c091612a6786519384015160000b90565b90612a86612a7a6101e086015160000b90565b94610260015160ff1690565b93600092830b920b92612eac565b8251610280810151612aae901515916102a0015160010b90565b60010b91612c7e565b90612ad5612ad060ff6116cb6101608551015160ff1690565b610c9d565b90516101200151916129c3565b90612a0a565b612b7a9550600a9260ff612b18818084169660019760001990890b01880b02870b9716860b946102400151151590565b612b7f575b505050612b60612b336102008951015160010b90565b8402830b82612b446064809361292c565b01840b94612b586102208b51015160010b90565b02840b61292c565b01900b90612b746102608751015160ff1690565b92612fed565b612a94565b612b906002969293949680946112f3565b16612ba957612b9f925061292c565b915b388080612b1d565b90612bb39161292c565b9003810b91612ba1565b611ab861005793926119c883604d60f81b9052565b91906105bc610041916040519460208601612bbd565b611ab8949795612c14612c329894612c07611ab894611ab89896610574565b61021960f51b8152611b6d565b612c1d81611a93565b611ab860019a8b809981978280960190610574565b61058581611a93565b96959261004195926105bc95926040519960208b01612be8565b612c5e91610574565b602d60f91b8152610c97565b906100416105bc6040519360208501612c55565b60009291612c96612c8f8584610896565b5151615d58565b92612cb9602094612cb386612cab8988610896565b510151615d58565b90612bd2565b948351928015612e8e575b81965b84881015612e7657612cd761032a565b50612ce061032a565b50612ce961032a565b50612cf261032a565b508115612e0f5760018689868a612d51612d4b8b612d41612d22612d1c83808a016000190161188f565b89610896565b5197612d3b83612d328a84610896565b519b8a0161188f565b90610896565b519560020161188f565b8c610896565b515b85518451865161025895612d6b928792900302610ffe565b01928a83880180518383878a019a888c5191015190030290612d8c91610ffe565b019883838951928751905190030290612da491610ffe565b9003948851940151905190030290612dbb91610ffe565b900391612dc790615d58565b94612dd190615d58565b90612ddb90615d58565b91612de590615d58565b9251612df090615d58565b9351612dfb90615d58565b94612e0596612c3b565b9660010196612cc7565b87612e6257612e1e8387610896565b515b612e2a8988610896565b5190612e3960018b0189610896565b5160001988018b03612e4d57868a82612d53565b868a612e5c8d6002018c610896565b51612d53565b612e70600019890187610896565b51612e20565b9650945050505050612e855790565b61005790612c6a565b926000190192612cc4565b60ff918216908216019190821161081557565b929094919394612ebc815161205f565b9160005b825160ff80831691821015612f4e5790612f2f9291612ee98b612ee4600186612e99565b6112f3565b16612f345780612efc612f299287610896565b51516118cd602080612f0e858b610896565b51015190612f24612f1f6040610179565b948552565b830152565b50611870565b612ec0565b806118cd858b612f47612f29958a610896565b518c612f8a565b5050505050925092505090565b60010b617fff1981146108155760000390565b9190918281019283128015916000139182169115161761081557565b6100579293612fbe612fdf92612f9e61032a565b50612fc48551612fbe612fb08a612f5b565b60019a8b0b908b0b85611faf565b90612f6e565b9660208096015193612fd581612f5b565b820b910b91611faf565b90612f24612f1f6040610179565b929192612ffa815161205f565b9060005b815160ff908183169081101561309e579061305c929160019161302589612ee48587612e99565b1661306157612f2991506130398186610896565b515161304b602080612f0e858a610896565b6130558288610896565b5285610896565b612ffe565b612f299161304b6130806130758489610896565b51518a840b90612f6e565b91612fdf6020918d83613093888d610896565b510151910b90612f6e565b505050509250505090565b6130b3600a6103ed565b696d61696e53686170657360b01b602082015290565b916130e0816103a893946130db6130a9565b61359c565b926130e96130a9565b613101565b61ffff8091169081146108155760010190565b92919261310c610404565b9161311d61010086015161ffff1690565b93620186a0916131328361ffff809816611907565b9360c0880151926131538861314c60a08c015161ffff1690565b1686611907565b9260808a01988061318e6131698c5161ffff1690565b82600091161561323d575b9150509891939590929496975b8361318e8c5161ffff1690565b16848b16101561321e579286949282856131b38f958c9b988f8f9e9c9a988899613335565b996131bd91611882565b9a6131c791611907565b8d60e001516131d5916112d5565b978d61012001516131e590151590565b600014916132059161318e9361321557906131ff91610cbf565b9b6130ee565b9a93959792505092949697613181565b6131ff91610cbf565b5050965050925095505061005793506107249150611a5960f21b611c0a565b60018d52613174565b61ffff91821691168115611008570690565b602360f81b815261005791906127b6565b906100416105bc6040519360208501613258565b6132a7929161328f91610d2b81610d0e565b6805240e6c6c2d8ca40560bb1b815260090190610574565b602960f81b8152610c97565b91906105bc61004191604051946020860161327d565b9261059f61005796959361059f61059f9661059f95610574565b949390926105bc926100419460405197602089016132c9565b9161328f613312926132a79594610d2b81610d0e565b612c2d60f01b8152611b6d565b6040519392610041926105bc92602087016132fc565b929390919360029161334c620186a080928561185f565b91613355610404565b506064946133a461ffff9661339d6133968961337761014087015161ffff1690565b168c60008c8061338c60808b015161ffff1690565b1692169286615bf2565b9186611907565b908761185f565b956133b7866133b1610404565b9a613246565b16159081613497575b501561345b576134366134248461005799979561341f8a98966134479661341961340661340061344d9f6133f390613269565b63343932b360e11b611c0a565b9c613269565b693c3634b7359d343932b360b11b611c0a565b9861185f565b61331f565b687472616e73666f726d60b81b611c0a565b92666f70616369747960c81b611c0a565b926132e3565b613455610404565b906134a6565b6134366134246100579896946134928997956134479561348c6134066134866133f361344d9f613269565b9b613269565b9761185f565b6132b3565b6101a0015115159050386133c0565b61005791906275736560e81b611b98565b602d60f81b815261005791906127b6565b906100416105bc60405193602085016134b7565b6134ea906100579392610574565b6127b68161199a565b91906105bc6100419160405194602086016134dc565b6132a7929161351b91610d2b81610d0e565b6705240e6c6c2d8ca560c31b815260080190610574565b91906105bc610041916040519460208601613509565b9161059f610057949361059f93610574565b6040519392610041926105bc9260208701613548565b9161351b613312926132a79594610d2b81610d0e565b6040519392610041926105bc9260208701613570565b9192906060906135c2620186a061ffff6135bc61010089015161ffff1690565b16611907565b926135eb6135d5620186a0610168611907565b61ffff6135e4895161ffff1690565b16906112d5565b9261360a606460ff6136026101808b015160ff1690565b16600261185f565b60408801511561386f57613638613620826134c8565b898401511561386857613632836134c8565b906134f3565b926000965b61ffff61364c8b5161ffff1690565b1661ffff8916101561384d5785613668620186a083600261185f565b948b8a61ffff613679600283613246565b16158061383f575b1561376e5750506136bf90610ed861344d61369e6133f386613269565b896136b96134248b806136b36134068c613269565b94613586565b9161355a565b945b60408c015115158015613761575b61371f575b50506137136137199161370d8c60ff6135e46137018d836136fa6101c087015160ff1690565b1690611907565b926101e0015160ff1690565b90611882565b976130ee565b9661363d565b6137199295610ed861344d84613757946136b96134248d61375161340661374b6133f36137139d613269565b96613269565b93613532565b94918791506136d4565b5060608c015115156136cf565b81985061344d6138016137c96137b36138289695610ed895600061ffff61379c610b08602086015160010b90565b169261ffff600194850b9316840b930b6064615bf2565b6137c16064620186a0611907565b90600261185f565b9a6137d76040820151151590565b15613835576137f36137e88d6134c8565b915b60600151151590565b1561382e576136328c6134c8565b9961380e6133f387613269565b906136b96134246138216134068a613269565b928d613532565b946136c1565b8b906134f3565b6137f38c916137ea565b506101608201511515613681565b50975050935050505061072461005792611a5960f21b611c0a565b82906134f3565b61363881613620565b613880610341565b506138b961388c610341565b9161389681613902565b83526138a181613f70565b60208401526138af816144cc565b60408401526146bf565b6060820152610057816147e8565b6138d160096103ed565b6810008080810303030360b91b602082015290565b6138f060066103ed565b6501030507090960d01b602082015290565b61390a610187565b50613918600c600283611faf565b90613a11600a60ff61393161392b6138c7565b85613c37565b1660000b0260000b606403613a0761395861394f6032601487611faf565b624c4b406112d5565b6139dc600a60ff61397061396a6138e6565b89613c37565b6005011602916139ab613984601e89612747565b956139a160ff613995610220610179565b9b168b9061ffff169052565b60000b60208a0152565b600060408901526000606089015260006080890152602160a08901526139d5620186a060c08a0152565b60e0880152565b600161010087015260016101208701526000610140870152600061016087015260ff16610180860152565b15156101a0840152565b60016101c083015260016101e08301526000610200830152613a34600282612747565b613bd1575b815161ffff1660038111613b915750613a6660ff613a5a600a600285611faf565b1661ffff166080840152565b613a81613a76601e600f84611faf565b61ffff1660a0840152565b600160408301525b600161ffff613a9d608085015161ffff1690565b161180613b80575b613b6a575b60408201511580613b49575b613b2c575b608082015161ffff16600c8110613ae95750613add6014600161005793611faf565b61ffff16610140830152565b60078110613b035750613add610057916014602891611faf565b600411613b1b57613add610057916014603c91611faf565b613add606461005792602890611faf565b613b44613b3a603283612747565b1515610160840152565b613abb565b5061ffff613b636002613b5e855161ffff1690565b613246565b1615613ab6565b613b7a61394f603f603784611faf565b50613aaa565b50613b8c600582612747565b613aa5565b60ff613a5a613ba592600d03600085611faf565b613bbc613bb3603283612747565b15156040840152565b613bcc613a76602d600084611faf565b613a89565b613bdc604682612747565b15613c1157613bfc613bf1605a603284611faf565b60ff166101c0840152565b60646101e08301526001610200830152613a39565b613c21613bf16032601484611faf565b613bfc565b9081518110156108aa570160200190565b600091825b815160ff851690811015613c7757613c57613c659184613c26565b516001600160f81b03191690565b60f81c019260ff906001011692613c3c565b50925090613c89600193848093611faf565b6000928384955b613c9e575b50505050905090565b815160ff871690811015613cdc57613c57613cb99184613c26565b60f81c019082821215613cd45794830160ff16949083613c90565b505050505090565b50613c95565b613cec60096103ed565b68050a0c19140f05050360b81b602082015290565b613d0b60036103ed565b623c190f60e81b602082015290565b613d2460036103ed565b62500f0560e81b602082015290565b613d3d60056103ed565b64028082818160d91b602082015290565b613d5860096103ed565b681505141e1e1403020160b81b602082015290565b9061037e90611d71565b613d81601d6103ed565b7f7374726f6b652d6461736861727261793d223132252036252031332522000000602082015290565b613db4601c6103ed565b7f7374726f6b652d6461736861727261793d223225203235252038252200000000602082015290565b613de760196103ed565b7839ba3937b5b296b230b9b430b93930bc9e91199a92901c129160391b602082015290565b613e16601a6103ed565b7f7374726f6b652d6461736861727261793d223230252033302522000000000000602082015290565b613e4960196103ed565b7839ba3937b5b296b230b9b430b93930bc9e91189a92901a929160391b602082015290565b613e7860186103ed565b7739ba3937b5b296b230b9b430b93930bc9e911a12901a929160411b602082015290565b613ea660196103ed565b7839ba3937b5b296b230b9b430b93930bc9e91189812901a929160391b602082015290565b613ed560186103ed565b7739ba3937b5b296b230b9b430b93930bc9e911a92901a929160411b602082015290565b613f0360186103ed565b7739ba3937b5b296b230b9b430b93930bc9e9118929019129160411b602082015290565b613f3160186103ed565b7739ba3937b5b296b230b9b430b93930bc9e9118129018929160411b602082015290565b613f5f60056103ed565b640282918f0560d91b602082015290565b613f78610202565b50613f81613ce2565b613f8b9082613c37565b60030190613f97613d01565b613fa19082613c37565b60ff16613fad90611d71565b613fb8600a83612747565b613fc3600a84612747565b613fd06005600186611faf565b613fd8613d1a565b613fe29086613c37565b60ff16613fee90611d71565b613ffb6004600088611faf565b614006602888612747565b614011601489612747565b9161401f601460038b611faf565b9361402d601460038c611faf565b95614036613d33565b614040908c613c37565b9761404c60328d612747565b998c614056613d4e565b61405f91613c37565b60ff1660320261ffff169b6102e061407690610179565b60ff909f168f528e6020019061408b91613d6d565b151560408e0152151560608d0152600760808d0152600760a08d015260ff1660c08c01526140b76124f8565b60e08c01526140c4612510565b6101008c01526140d2610404565b6101208c01526140e6906101408c01613d6d565b60ff166101608a0152151561018089015215156101a088015260000b6101c087015260000b6101e086015260076102008601526007610220860152600161024086015260ff16610260850152151561028084015260010b6102a0830152608c6102c083015261026082015160ff1660ff16156143c7575b614168600a82612747565b6142e2575b6102a082015160010b6142b85760146102a08301525b6102a082015160010b606481136142a45750607d6102c08301525b6101a082015115614282575b61422a61421f6141c16141bb613f55565b84613c37565b60010160011b60fe166141e26141d8600f86612747565b1515610240870152565b6142096141fe60ff60808801511661ffff848202169087611faf565b60010b610200870152565b61ffff60ff608087015116918202169084611faf565b60010b610220840152565b61024082015115614239575090565b61426b60328261426661424f8361005796612473565b61020087018051600190810b9290920290910b9052565b612473565b61022083018051600190810b9290920290910b9052565b61429f6142946101c084015160000b90565b60000b6101e0840152565b6141aa565b60c81261419e5760826102c083015261419e565b6142dd6142c6600a83612473565b6102a084018051600190810b9290920290910b9052565b614183565b60096142f081600084611faf565b8061430f57505061430a614302613f27565b610120840152565b61416d565b6001810361432457505061430a614302613ef9565b6002810361433957505061430a614302613ecb565b6003810361434e57505061430a614302613e9c565b6004810361436357505061430a614302613e6e565b6005810361437857505061430a614302613e3f565b6006810361438d57505061430a614302613e0c565b600781036143a257505061430a614302613ddd565b600881036143b757505061430a614302613daa565b0361430a5761430a614302613d77565b60c861026083015261415d565b6143de60056103ed565b6432140f0a0560d81b602082015290565b6143f960046103ed565b63190a0a0560e11b602082015290565b61441360046103ed565b631e0a050560e11b602082015290565b61442d60076103ed565b6628140a0a0a070360c81b602082015290565b61444a60066103ed565b6514141e0a0a0160d01b602082015290565b9061037e906112e9565b9061037e906107d6565b61447a60096103ed565b68313030252c2035302560b81b602082015290565b61449960096103ed565b68313030252c2036302560b81b602082015290565b6144b860086103ed565b673830252c2035302560c01b602082015290565b6144d46102a7565b506144e3610167600083611faf565b906144ec6143d4565b6144f69082613c37565b60010190614505605582612747565b614510605083612747565b9261451c601484612747565b9261452a600a600183611faf565b946145336143ef565b61453d9083613c37565b60ff16614549906112e9565b946145576032601485611faf565b61455f614409565b6145699085613c37565b60050296614575614409565b61457f9086613c37565b60050260ff1660640398614597610167600188611faf565b9360026145a2614423565b6145ac9089613c37565b01956145bb606460148a611faf565b976145c4614440565b6145cd91613c37565b60ff166145d9906107d6565b986145e5610240610179565b9d8e5260ff1660208e015298151560408d0190815298151560608d0152151560808c015260ff1660a08b015261461e9060c08b0161445c565b60ff1660e0890152606461010089015260ff90951661012088019081529460ff90961661014088019081529561ffff1661016088015260ff1661018087015261ffff166101a0860152614675906101c08601614466565b61467d614470565b6101e085015261468b61448f565b6102008501526146996144ae565b61022085015251156146aa57505090565b600090526064905290565b9061037e906104da565b6146d96146de916146ce61032a565b506000601591611faf565b6104da565b61005760016146f76146f06040610179565b93846146b5565b15156020830152565b61470a60076103ed565b663930252c34302560c81b602082015290565b61472760076103ed565b663930252c34352560c81b602082015290565b61474460076103ed565b663830252c35302560c81b602082015290565b61476160076103ed565b663830252c34302560c81b602082015290565b61477e60076103ed565b663830252c34352560c81b602082015290565b61479b60076103ed565b663730252c35302560c81b602082015290565b6147b860076103ed565b663330252c33302560c81b602082015290565b6147d560076103ed565b663330252c33352560c81b602082015290565b6148e1600060608301906148066148008351516104da565b916104da565b61480f826104da565b148015614c71575b8015614c57575b8015614c3d575b8015614c23575b8015614c09575b8015614bef575b614bb3575b61484960016104da565b614852826104da565b148015614b99575b8015614b7f575b8015614b65575b8015614b4b575b8015614b31575b614af5575b60159061488a614800836104da565b14614ab9575b61489b8251516104da565b6148a8614800600e6104da565b14908115614a98575b8115614a78575b508015614a59575b8015614a3a575b8015614a1b575b80156149fc575b6149e3575b51516104da565b6148ee61480060056104da565b146149d1575b602081019061490582515160ff1690565b81515161ffff169061491e61ffff9260ff809316610801565b82601491161180156149be575b1561497257505061495560066149488160808651019060ff169052565b60a08451019060ff169052565b5160e00151620186a0116149665750565b5160646102c090910152565b6149979061498285515160ff1690565b9061499085515161ffff1690565b9116610801565b60129116106149b957815160076080909101528151600660a090910152614955565b614955565b5083515160ff1681600a9116101561492b565b6020810151600460c0909101526148f4565b6149f761012060208501510161037e610404565b6148da565b50614a088151516104da565b614a1561480060036104da565b146148d5565b50614a278151516104da565b614a3461480060046104da565b146148ce565b50614a468151516104da565b614a5361480060056104da565b146148c7565b50614a658151516104da565b614a72614800600a6104da565b146148c0565b9050614a91614800614a8b8451516104da565b926104da565b14386148b8565b9050614aa58251516104da565b614ab2614800600d6104da565b14906148b1565b614af06101e0614ad2604087019182510161037e6147ae565b614ae361020082510161037e6147cb565b516102200161037e6147ae565b614890565b614b2c6101e0614b0e604087019182510161037e614757565b614b1f61020082510161037e614774565b516102200161037e614791565b61487b565b50614b3c600e6104da565b614b45826104da565b14614876565b50614b56600d6104da565b614b5f826104da565b1461486f565b50614b70600c6104da565b614b79826104da565b14614868565b50614b8a60096104da565b614b93826104da565b14614861565b50614ba460066104da565b614bad826104da565b1461485a565b614bea6101e0614bcc604087019182510161037e614700565b614bdd61020082510161037e61471d565b516102200161037e61473a565b61483f565b50614bfa60136104da565b614c03826104da565b1461483a565b50614c14600f6104da565b614c1d826104da565b14614833565b50614c2e60086104da565b614c37826104da565b1461482c565b50614c4860076104da565b614c51826104da565b14614825565b50614c6260046104da565b614c6b826104da565b1461481e565b50614c7c60026104da565b614c85826104da565b14614817565b614c95600c6103ed565b6b536861706520506f696e747360a01b602082015290565b614cb760186103ed565b77141bda5b9d1cc811d95b995c985d1a5bdb8813595d1a1bd960421b602082015290565b614ce5600f6103ed565b6e14da185c1948125cc810db1bdcd959608a1b602082015290565b614d0a60106103ed565b6f53686170652057696465204c696e657360801b602082015290565b614d3060086103ed565b67436f6c6f72696e6760c01b602082015290565b614d4e60066103ed565b6511185cda195960d21b602082015290565b614d6a60136103ed565b7221b0ba36bab63616a937b6902a32b739b4b7b760691b602082015290565b614d9360106103ed565b6f4772616469656e742050616c6574746560801b602082015290565b614db9600e6103ed565b6d53796d6574727920506f696e747360901b602082015290565b9461059f61059f989561059f61059f9661059f6100579e9d9b9761059f61059f9c61059f99610574565b98979491926105bc96936100419895926040519b60208d01614dd3565b614e2460116103ed565b7053796d6574727920456e64205363616c6560781b602082015290565b614e4b60116103ed565b7020b23234ba34b7b730b61026b4b93937b960791b602082015290565b614e7260136103ed565b7229bcb6b2ba393c9026b4b93937b91022bb32b760691b602082015290565b614e9b60106103ed565b6f2737b710199b18103937ba30ba34b7b760811b602082015290565b614ec160056103ed565b6452696e677360d81b602082015290565b614edc60106103ed565b6f2934b7339026b4b93937b91022bb32b760811b602082015290565b614f0260066103ed565b652334b63a32b960d11b602082015290565b61059f9461059f61059f989561059f61059f9661059f61059f966100579f9e9c98610574565b97969390926105bc959261004197946040519a60208c01614f14565b6d2261747472696275746573223a5b60901b8152614f7491906127d4565b605d60f81b8152610c97565b906100416105bc6040519360208501614f56565b60ff9060208082019183835151614fab9060ff1690565b16614fb590615d0e565b614fbd614c8b565b90614fc791615266565b9282815101614fd590611d7b565b614fde90615398565b614fe6614cad565b90614ff091615266565b936102808251016150019051151590565b61500a9061530f565b615012614cdb565b9061501c91615266565b9560c083510161502d905160ff1690565b1661503790615d0e565b61503f614d00565b9061504991615266565b9461014083510161505990611d7b565b61506290615447565b61506a614d26565b9061507491615266565b95610120845101515115156150889061530f565b615090614d44565b9061509a91615266565b93516102a00151600194906150b090860b615d58565b6150b8614d60565b906150c291615266565b906101c0986040998a890151016150d8906107e0565b6150e19061555e565b6150e9614d89565b906150f391615266565b9361ffff9b8c8a51516151079061ffff1690565b1661511190615d0e565b615119614daf565b9061512391615266565b9661512d98614dfd565b82519093015160010b615140910b615d58565b615148614e1a565b9061515291615266565b81519384015115159485156151776100579761522d9761518493615232575b5061530f565b61517f614e41565b615266565b6151a661519e61519961016087510151151590565b61530f565b61517f614e68565b906151e36151db6151ca6151c26151996102008a510151151590565b61517f614e91565b946116cb60808951015161ffff1690565b61517f614eb7565b9361522761521a61521561520a6152026151996101a08c510151151590565b61517f614ed2565b9860600151516104da565b6158b1565b615222614ef8565b6152d4565b96614f3a565b614f80565b606001511515905038615171565b61524991610574565b610c978161199a565b906100416105bc6040519360208501615240565b61005791615273916152d4565b615252565b6e3d913a3930b4ba2fba3cb832911d1160891b81526152b1929161529b9161283a565b6a1116113b30b63ab2911d1160a91b81526127ef565b61227d60f01b8152611bee565b91906105bc610041916040519460208601615278565b90610057916152be565b6152e860026103ed565b614e6f60f01b602082015290565b61530060036103ed565b6259657360e81b602082015290565b1561531c576100576152f6565b6100576152de565b61532e600e6103ed565b6d46697865642044697374616e636560901b602082015290565b615352600f6103ed565b6e52616e646f6d2044697374616e636560881b602082015290565b61537760156103ed565b74476f6c64656e20526174696f2044697374616e636560581b602082015290565b6153a26000611d71565b6153ab82611d71565b036153b95750610057615348565b6153c6611e816001611d71565b146153d35761005761536d565b610057615324565b6153e5600f6103ed565b6e4c696e65204875652052616e646f6d60881b602082015290565b61540a60086103ed565b6711dc98591a595b9d60c21b602082015290565b61542860136103ed565b724c696e652048756520496e6372656173696e6760681b602082015290565b6154516000611d71565b61545a82611d71565b036154685750610057615400565b615475611e816001611d71565b146154825761005761541e565b6100576153db565b61549460136103ed565b7211dbdb19195b88105b99db194813d9999cd95d606a1b602082015290565b6154bd600d6103ed565b6c131a5b99585c8813d9999cd95d609a1b602082015290565b6154e0600d6103ed565b6c2a3bb79021b7b637b939902c1960991b602082015290565b61550360126103ed565b7154776f20436f6c6f7273205a69672d5a616760701b602082015290565b61552b600a6103ed565b6954776f20436f6c6f727360b01b602082015290565b61554b60076103ed565b665261696e626f7760c81b602082015290565b61556860006107d6565b615571826107d6565b0361557f5750610057615521565b61558960016107d6565b615592826107d6565b036155a057506100576154f9565b6155aa60026107d6565b6155b3826107d6565b036155c157506100576154d6565b6155cb60036107d6565b6155d4826107d6565b036155e257506100576154b3565b6155ef6108ce60046107d6565b146155fc57610057615541565b61005761548a565b61560e600e6103ed565b6d48616c66746f6e65204e6f69736560901b602082015290565b615632600d6103ed565b6c426c61636b204f75746c696e6560981b602082015290565b61565560056103ed565b644368616c6b60d81b602082015290565b615670600c6103ed565b6b2932b1b7b637b934b733901960a11b602082015290565b615692600c6103ed565b6b4e6f69736520536861646f7760a01b602082015290565b6156b4600d6103ed565b6c436f6c6f72204f75746c696e6560981b602082015290565b6156d7600d6103ed565b6c496e6e6572204f75746c696e6560981b602082015290565b6156fa600d6103ed565b6c14dc1c99585908109c9a59da1d609a1b602082015290565b61571d60066103ed565b6514dc1c99585960d21b602082015290565b61573960056103ed565b64476c61737360d81b602082015290565b61575460066103ed565b65537061726b7360d01b602082015290565b615770600a6103ed565b695265636f6c6f72696e6760b01b602082015290565b61579060066103ed565b650a6e0d8c2e6d60d31b602082015290565b6157ac60066103ed565b65233937bd32b760d11b602082015290565b6157c8600c6103ed565b6b111a5cdc1b1858d95b595b9d60a21b602082015290565b6157ea60056103ed565b64536d6f6b6560d81b602082015290565b61580560056103ed565b6453746f6e6560d81b602082015290565b61582060116103ed565b70213934b3b43a102bb0ba32b931b7b637b960791b602082015290565b615847600a6103ed565b692bb0ba32b931b7b637b960b11b602082015290565b61586760056103ed565b64536570696160d81b602082015290565b61588260096103ed565b68477261797363616c6560b81b602082015290565b6158a160046103ed565b636e6f6e6560e01b602082015290565b6158bb60016104da565b6158c4826104da565b036158d25750610057615878565b6158dc60026104da565b6158e5826104da565b036158f3575061005761585d565b6158fd60036104da565b615906826104da565b03615914575061005761583d565b61591e60046104da565b615927826104da565b036159355750610057615816565b61593f60056104da565b615948826104da565b0361595657506100576157fb565b61596060066104da565b615969826104da565b0361597757506100576157e0565b61598160076104da565b61598a826104da565b0361599857506100576157be565b6159a260086104da565b6159ab826104da565b036159b957506100576157a2565b6159c360096104da565b6159cc826104da565b036159da5750610057615786565b6159e4600a6104da565b6159ed826104da565b036159fb5750610057615766565b615a05600b6104da565b615a0e826104da565b03615a1c575061005761574a565b615a26600c6104da565b615a2f826104da565b03615a3d575061005761572f565b615a47600d6104da565b615a50826104da565b03615a5e5750610057615713565b615a68600e6104da565b615a71826104da565b03615a7f57506100576156f0565b615a89600f6104da565b615a92826104da565b03615aa057506100576156cd565b615aaa60106104da565b615ab3826104da565b03615ac157506100576156aa565b615acb60116104da565b615ad4826104da565b03615ae25750610057615688565b615aec60126104da565b615af5826104da565b03615b035750610057615666565b615b0d60136104da565b615b16826104da565b03615b24575061005761564b565b615b2e60146104da565b615b37826104da565b03615b455750610057615628565b615b5261480060156104da565b14615b5f57610057615897565b610057615604565b615b7081615b7d565b906002820a03610c975790565b80615b885750600090565b6001600291615b9783826112d5565b9282615bb3828280880197615bac89886112d5565b01016112d5565b935b615bc1575b5050505090565b9091929384811115615bec5750615be38285615bdd81876112d5565b016112d5565b92919080615bb5565b93615bba565b9190936000938260001991030191848313615c18575b505050620186a081029203020190565b615c2a939450620186a0910302610ffe565b90388080615c08565b61005790615c64565b369037565b90610041615c57615c51846103ed565b936103d0565b601f190160208401615c3c565b60005b602060ff82169081109081615cf0575b5015615c8b57615c8690611870565b615c67565b90615c9960ff809316615c41565b9060005b83811690602080831080615cd4575b15613c95578210156108aa5781615cc984615ccf941a9186613c26565b53611870565b615c9d565b156108aa5783831a60f81b6001600160f81b0319161515615cac565b90156108aa5782901a60f81b6001600160f81b031916151538615c77565b90608091604080519384019360a00190526000835282906000198091945b0193600a90818106603001865304938415615d48578190615d2c565b93505082900391601f1901918252565b60009081811215615d8157615d6d9103615d0e565b90815191602d815260001901916001018252565b6100579150615d0e56fea26469706673582212207232ea5cbff0fb28a410e33c0283b69ef8d976038630027a322f42dce93a7f1a64736f6c63430008150033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.