// File: contracts/providers/AlphabetProvider.sol



// SPDX-License-Identifier: MIT

/*
 * NounsAssetProvider is a wrapper around NounsDescriptor so that it offers
 * various characters as assets to compose (not individual parts).
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import { Ownable } from '@openzeppelin/contracts/access/Ownable.sol';
import "assetprovider.sol/IAssetProvider.sol";
import "randomizer.sol/Randomizer.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import '@openzeppelin/contracts/interfaces/IERC165.sol';
import "../interfaces/IColorSchemes.sol";
import "../interfaces/ILayoutGenerator.sol";
import "fully-on-chain.sol/SVG.sol";

/**
 * MultiplexProvider create a new asset provider from another asset provider,
 * which draws multiple assets with the same set of provider-specific properties.
 */
contract AlphabetProvider is IAssetProvider, IERC165, Ownable {
  using Strings for uint256;
  using Randomizer for Randomizer.Seed;
  using BytesArray for bytes[];
  using SVG for SVG.Element;
  using TX for string;

  ILayoutGenerator public generator;
  IColorSchemes public colorSchemes;
  IFontProvider public font;

  constructor(IFontProvider _font, ILayoutGenerator _generator, IColorSchemes _colorSchemes) {
    font = _font;
    generator = _generator;
    colorSchemes = _colorSchemes;
  }

  function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
    return
      interfaceId == type(IAssetProvider).interfaceId ||
      interfaceId == type(IERC165).interfaceId;
  }

  function getOwner() external override view returns (address) {
    return owner();
  }

  function getProviderInfo() external view override returns(ProviderInfo memory) {
    return ProviderInfo("alphabet", "Alphabet", this);
  }

  function totalSupply() external pure override returns(uint256) {
    return 0;
  }

  function processPayout(uint256 _assetId) external override payable {
    uint amount = msg.value / 2;
    address payable pNouns = payable(address(0x8AE80e0B44205904bE18869240c2eC62D2342785));
    pNouns.transfer(amount); // 50% to pNouns
    emit Payout("alphabet", _assetId, pNouns, amount);

    amount = msg.value - amount; // eliminating round error
    font.processPayout{value:amount / 5}(); // 10% distribution to the font provider

    amount = amount - amount / 5;
    address payable payableTo = payable(owner());
    payableTo.transfer(amount);
    emit Payout("alphabet", _assetId, payableTo, amount);
  }

  function generateTraits(uint256 _assetId) external view returns (string memory) {
    return colorSchemes.generateTraits(_assetId);
  }

  function generateSVGPart(uint256 _assetId) external view override returns(string memory svgPart, string memory tag) {
    Randomizer.Seed memory seed;
    string[] memory scheme;
    (seed, scheme) = colorSchemes.getColorScheme(_assetId);
    tag = string(abi.encodePacked("alphabet", _assetId.toString()));

    for (uint i=0; i<scheme.length; i++) {
      scheme[i] = string(abi.encodePacked('#', scheme[i]));      
    }

    ILayoutGenerator.Node[] memory nodes;
    (seed, nodes) = generator.generate(seed, 0 + 30 * 0x100 + 60 * 0x10000);

    SVG.Element[] memory parts = new SVG.Element[](nodes.length);
    bytes memory text = new bytes(1);
    bytes memory scrabble = bytes("AAAAAAAAABBCCDDDDEEEEEEEEEEEEFFGGGHHIIII"
                              "IIIIIJKLLLLMMNNNNNNOOOOOOPPQRRTTTTTTUUUUVVWWXYYZ");
    for (uint i = 0; i < nodes.length; i++) {
      ILayoutGenerator.Node memory node = nodes[i];
      uint index;
      (seed, index) = seed.random(scrabble.length);
      text[0] = scrabble[index];
      uint width = font.widthOf(string(text));
      width = ((1024 - width) / 2 * node.size) / 1024;
      parts[i] = SVG.group([
                    SVG.rect(int(node.x), int(node.y), node.size, node.size)
                      .fill(scheme[i % scheme.length]),
                    SVG.text(font, string(text))
                      .transform(TX.translate(int(node.x + width), int(node.y + node.size/10)).scale1000(node.size))]);
    }
    svgPart = string(SVG.group(parts).id(tag).fill("#222").svg());
  }
}
// File: contracts/interfaces/IColorSchemes.sol



// SPDX-License-Identifier: MIT

/*
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import "randomizer.sol/Randomizer.sol";

interface IColorSchemes {
  function getColorScheme(uint256 _assetId) external view returns(Randomizer.Seed memory seed, string[] memory scheme);
  function generateTraits(uint256 _assetId) external view returns (string memory);
}

// File: contracts/interfaces/ILayoutGenerator.sol



// SPDX-License-Identifier: MIT

/*
 * NounsAssetProvider is a wrapper around NounsDescriptor so that it offers
 * various characters as assets to compose (not individual parts).
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import "randomizer.sol/Randomizer.sol";

interface ILayoutGenerator {
  struct Node {
    uint x;
    uint y;
    uint size;
    string scale;
  }

  function generate(Randomizer.Seed memory _seed, uint _props)
              external view returns(Randomizer.Seed memory seed, Node[] memory nodes);
}
// File: randomizer.sol/Randomizer.sol



// SPDX-License-Identifier: MIT

/*
 * Pseudo Random genearation library.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

library Randomizer {
  struct Seed {
    uint256 seed;
    uint256 value;
  }

  /**
   * Returns a seudo random number between 0 and _limit-1.
   * It also returns an updated seed.
   */
  function random(Seed memory _seed, uint256 _limit) internal pure returns (Seed memory seed, uint256 value) {
    seed = _seed;
    if (seed.value < _limit * 256) {
      seed.seed = uint256(keccak256(abi.encodePacked(seed.seed)));
      seed.value = seed.seed;
    }
    value = seed.value % _limit;
    seed.value /= _limit;
  }

  /**
   * Returns a randomized value based on the original value and ration (in percentage).
   * It also returns an updated seed. 
   */
  function randomize(Seed memory _seed, uint256 _value, uint256 _ratio) internal pure returns (Seed memory seed, uint256 value) {
    uint256 limit = _value * _ratio / 100;
    uint256 delta;
    (seed, delta) = random(_seed, limit * 2);
    value = _value - limit + delta;
  }
}
// File: fully-on-chain.sol/SVG.sol



// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import "bytes-array.sol/BytesArray.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "./IFontProvider.sol";
import "./Path.sol";
import "./Transform.sol";

library SVG {
  using Strings for uint;
  using BytesArray for bytes[];

  struct Attribute {
    string key;
    string value;
  }

  struct Element {
    bytes head;
    bytes tail;
    Attribute[] attrs;    
  }

  function path(bytes memory _path) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<path d="', _path); 
    element.tail = bytes('"/>\n');
  }

  function char(IFontProvider _font, string memory _char) internal view returns(Element memory element) {
    element = SVG.path(Path.decode(_font.pathOf(_char)));
  }

  function textWidth(IFontProvider _font, string memory _str) internal view returns(uint x) {
    bytes memory data = bytes(_str);
    bytes memory ch = new bytes(1);
    for (uint i=0; i<data.length; i++) {
      ch[0] = data[i];
      x += _font.widthOf(string(ch));
    }
  }

  function text(IFontProvider _font, string[2] memory _strs, uint _width) internal view returns(Element memory element) {
    string[] memory strs = new string[](2);
    strs[0] = _strs[0];
    strs[1] = _strs[1];
    element = text(_font, strs, _width);
  }
  function text(IFontProvider _font, string[3] memory _strs, uint _width) internal view returns(Element memory element) {
    string[] memory strs = new string[](3);
    strs[0] = _strs[0];
    strs[1] = _strs[1];
    strs[2] = _strs[2];
    element = text(_font, strs, _width);
  }

  function text(IFontProvider _font, string[4] memory _strs, uint _width) internal view returns(Element memory element) {
    string[] memory strs = new string[](4);
    for (uint i=0; i<_strs.length; i++) {
      strs[i] = _strs[i];
    }
    element = text(_font, strs, _width);
  }
  function text(IFontProvider _font, string[5] memory _strs, uint _width) internal view returns(Element memory element) {
    string[] memory strs = new string[](5);
    for (uint i=0; i<_strs.length; i++) {
      strs[i] = _strs[i];
    }
    element = text(_font, strs, _width);
  }
  function text(IFontProvider _font, string[6] memory _strs, uint _width) internal view returns(Element memory element) {
    string[] memory strs = new string[](6);
    for (uint i=0; i<_strs.length; i++) {
      strs[i] = _strs[i];
    }
    element = text(_font, strs, _width);
  }
  function text(IFontProvider _font, string[7] memory _strs, uint _width) internal view returns(Element memory element) {
    string[] memory strs = new string[](7);
    for (uint i=0; i<_strs.length; i++) {
      strs[i] = _strs[i];
    }
    element = text(_font, strs, _width);
  }

  function text(IFontProvider _font, string[] memory _strs, uint _width) internal view returns(Element memory element) {
    uint height = _font.height();
    uint maxWidth = _width;
    Element[] memory elements = new Element[](_strs.length);
    for (uint i=0; i<_strs.length; i++) {
      uint width = textWidth(_font, _strs[i]);
      if (width > maxWidth) {
        maxWidth = width;
      }
      elements[i] = transform(text(_font, _strs[i]), TX.translate(0, int(height * i)));
    }
    // extra group is necessary to let it transform 
    element = group(svg(transform(group(elements), TX.scale1000(1000 * _width / maxWidth))));
  }

  function text(IFontProvider _font, string memory _str) internal view returns(Element memory element) {
    bytes memory data = bytes(_str);
    bytes memory ch = new bytes(1);
    Element[] memory elements = new Element[](data.length);
    uint x;
    for (uint i=0; i<data.length; i++) {
      ch[0] = data[i];
      elements[i] = SVG.path(Path.decode(_font.pathOf(string(ch))));
      if (x > 0) {
        elements[i] = transform(elements[i], string(abi.encodePacked('translate(', x.toString(), ' 0)')));
      }
      x += _font.widthOf(string(ch));
    }
    element = group(elements);
  }

  function circle(int _cx, int _cy, int _radius) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<circle cx="', uint(_cx).toString(),'" cy="', uint(_cy).toString(),'" r="', uint(_radius).toString());
    element.tail = '"/>\n';
  }

  function ellipse(int _cx, int _cy, int _rx, int _ry) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<ellipse cx="', uint(_cx).toString(),'" cy="', uint(_cy).toString(),
                      '" rx="', uint(_rx).toString(), '" ry="', uint(_ry).toString());
    element.tail = '"/>\n';
  }

  function rect(int _x, int _y, uint _width, uint _height) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<rect x="', uint(_x).toString(),'" y="', uint(_y).toString(),
                                '" width="', _width.toString(), '" height="', _height.toString());
    element.tail = '"/>\n';
  }

  function rect() internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<rect width="100%" height="100%');
    element.tail = '"/>\n';
  }

  function stop(uint ratio) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<stop offset="', ratio.toString(), '%');
    element.tail = '"/>\n';
  }

  function use(string memory _id) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<use href="#', _id);
    element.tail = '"/>\n';
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function packed(Element[4] memory _elements) internal pure returns(bytes memory output) {
    bytes[] memory svgs = new bytes[](4);
    svgs[0] = svg(_elements[0]);
    svgs[1] = svg(_elements[1]);
    svgs[2] = svg(_elements[2]);
    svgs[3] = svg(_elements[3]);
    output = svgs.packed();
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function packed(Element[3] memory _elements) internal pure returns(bytes memory output) {
    bytes[] memory svgs = new bytes[](3);
    svgs[0] = svg(_elements[0]);
    svgs[1] = svg(_elements[1]);
    svgs[2] = svg(_elements[2]);
    output = svgs.packed();
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function packed(Element[2] memory _elements) internal pure returns(bytes memory output) {
    bytes[] memory svgs = new bytes[](2);
    svgs[0] = svg(_elements[0]);
    svgs[1] = svg(_elements[1]);
    output = svgs.packed();
  }

  function packed(Element[] memory _elements) internal pure returns(bytes memory output) {
    bytes[] memory svgs = new bytes[](_elements.length);
    for (uint i=0; i<_elements.length; i++) {
      svgs[i] = svg(_elements[i]);
    }
    output = svgs.packed();
  }

  function pattern(string memory _id, string memory _viewbox, string memory _width, string memory _height, bytes memory _elements) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<pattern id="', _id, '" viewBox="', _viewbox, '" width="', _width, '" height="', _height); 
    element.tail = abi.encodePacked('">', _elements, '</pattern>\n');
  }

  function pattern(string memory _id, string memory _viewbox, string memory _width, string memory _height, Element memory _element) internal pure returns(Element memory element) {
    element = pattern(_id, _viewbox, _width, _height, svg(_element));    
  }

  function filter(string memory _id, bytes memory _elements) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<filter id="', _id); 
    element.tail = abi.encodePacked('">', _elements, '</filter>\n');
  }

  function filter(string memory _id, Element memory _element) internal pure returns(Element memory element) {
    element = filter(_id, svg(_element));
  }

  function feGaussianBlur(string memory _src, string memory _stdDeviation) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<feGaussianBlur in="', _src, '" stdDeviation="', _stdDeviation);
    element.tail = '" />';
  }
  /*
      '  <feOffset result="offOut" in="SourceAlpha" dx="24" dy="32" />\n'
      '  <feGaussianBlur result="blurOut" in="offOut" stdDeviation="16" />\n'
      '  <feBlend in="SourceGraphic" in2="blurOut" mode="normal" />\n'
  */
  function feOffset(string memory _src, string memory _dx, string memory _dy) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<feOffset in="', _src, '" dx="', _dx, '" dy="', _dy);
    element.tail = '" />';
  }

  function feBlend(string memory _src, string memory _src2, string memory _mode) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<feBlend in="', _src, '" in2="', _src2, '" mode="', _mode);
    element.tail = '" />';
  }

  function linearGradient(string memory _id, bytes memory _elements) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<linearGradient id="', _id); 
    element.tail = abi.encodePacked('">', _elements, '</linearGradient>\n');
  }

  function linearGradient(string memory _id, Element memory _element) internal pure returns(Element memory element) {
    element = linearGradient(_id, svg(_element));
  }

  function radialGradient(string memory _id, bytes memory _elements) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<radialGradient id="', _id); 
    element.tail = abi.encodePacked('">', _elements, '</radialGradient>\n');
  }

  function radialGradient(string memory _id, Element memory _element) internal pure returns(Element memory element) {
    element = radialGradient(_id, svg(_element));
  }

  function group(bytes memory _elements) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<g x_x="x'); // HACK: dummy header for trailing '"'
    element.tail = abi.encodePacked('">', _elements, '</g>\n');
  }

  function group(Element memory _element) internal pure returns(Element memory element) {
    element = group(svg(_element));
  }

  function group(Element[] memory _elements) internal pure returns(Element memory element) {
    element = group(packed(_elements));
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function group(Element[2] memory _elements) internal pure returns(Element memory element) {
    element = group(packed(_elements));
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function group(Element[3] memory _elements) internal pure returns(Element memory element) {
    element = group(packed(_elements));
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function group(Element[4] memory _elements) internal pure returns(Element memory element) {
    element = group(packed(_elements));
  }

  function item(bytes memory _body) internal pure returns(Element memory element) {
    element.tail = _body;
  }

  function list(Element[] memory _elements) internal pure returns(Element memory element) {
    element.tail = packed(_elements);
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function list(Element[2] memory _elements) internal pure returns(Element memory element) {
    element.tail = packed(_elements);
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function list(Element[3] memory _elements) internal pure returns(Element memory element) {
    element.tail = packed(_elements);
  }

  // HACK: Solidity does not support literal expression of dynamic array yet
  function list(Element[4] memory _elements) internal pure returns(Element memory element) {
    element.tail = packed(_elements);
  }

  function mask(bytes memory _elements) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<mask x_x="x'); // HACK: dummy header for trailing '"'
    element.tail = abi.encodePacked(
      '">' 
      '<rect x="0" y="0" width="100%" height="100%" fill="black"/>'
      '<g fill="white">',
      _elements,
      '</g>' 
      '</mask>\n');
  }

  function mask(Element memory _element) internal pure returns(Element memory element) {
    element = mask(svg(_element));
  }

  function stencil(bytes memory _elements) internal pure returns(Element memory element) {
    element.head = abi.encodePacked('<mask x_x="x'); // HACK: dummy header for trailing '"'
    element.tail = abi.encodePacked(
      '">' 
      '<rect x="0" y="0" width="100%" height="100%" fill="white"/>'
      '<g fill="black">',
      _elements,
      '</g>' 
      '</mask>\n');
  }

  function stencil(Element memory _element) internal pure returns(Element memory element) {
    element = stencil(svg(_element));
  }

  function _append(Element memory _element, Attribute memory _attr) internal pure returns(Element memory element) {
    element.head = _element.head;
    element.tail = _element.tail;
    element.attrs = new Attribute[](_element.attrs.length + 1);
    for (uint i=0; i<_element.attrs.length; i++) {
      element.attrs[i] = _element.attrs[i];
    }
    element.attrs[_element.attrs.length] = _attr;   
  }

  function _append2(Element memory _element, Attribute memory _attr, Attribute memory _attr2) internal pure returns(Element memory element) {
    element.head = _element.head;
    element.tail = _element.tail;
    element.attrs = new Attribute[](_element.attrs.length + 2);
    for (uint i=0; i<_element.attrs.length; i++) {
      element.attrs[i] = _element.attrs[i];
    }
    element.attrs[_element.attrs.length] = _attr;   
    element.attrs[_element.attrs.length+1] = _attr2;   
  }

  function id(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("id", _value));
  }

  function fill(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("fill", _value));
  }

  function opacity(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("opacity", _value));
  }

  function stopColor(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("stop-color", _value));
  }

  function x1(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("x1", _value));
  }

  function x2(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("x2", _value));
  }

  function y1(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("y1", _value));
  }

  function y2(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("y2", _value));
  }

  function cx(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("cy", _value));
  }

  function cy(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("cy", _value));
  }

  function r(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("r", _value));
  }

  function fx(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("fx", _value));
  }

  function fy(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("fy", _value));
  }

  function result(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("result", _value));
  }

  function fillRef(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("fill", string(abi.encodePacked('url(#', _value, ')'))));
  }

  function filter(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("filter", string(abi.encodePacked('url(#', _value, ')'))));
  }

  function style(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("style", _value));
  }

  function transform(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("transform", _value));
  }

  function mask(Element memory _element, string memory _value) internal pure returns(Element memory element) {
    element = _append(_element, Attribute("mask", string(abi.encodePacked('url(#', _value,')'))));
  }

  function stroke(Element memory _element, string memory _color, uint _width) internal pure returns(Element memory element) {
    element = _append2(_element, Attribute("stroke", _color), Attribute("stroke-width", _width.toString()));
  }

  function svg(Element memory _element) internal pure returns (bytes memory output) {
    if (_element.head.length > 0) {
      output = _element.head;
      for (uint i=0; i<_element.attrs.length; i++) {
        Attribute memory attr = _element.attrs[i];
        output = abi.encodePacked(output, '" ', attr.key ,'="', attr.value);      
      }
    } else {
      require(_element.attrs.length == 0, "Attributes on list");
    }
    output = abi.encodePacked(output, _element.tail);
  }

  function document(string memory _viewBox, bytes memory _defs, bytes memory _body) internal pure returns (string memory) {
    bytes memory output = abi.encodePacked(
      '<?xml version="1.0" encoding="UTF-8"?>'
      '<svg viewBox="', _viewBox, '"'
      ' xmlns="http://www.w3.org/2000/svg">\n'
    );
    if (_defs.length > 0) {
      output = abi.encodePacked(output, '<defs>\n', _defs, '</defs>\n');
    }
    output = abi.encodePacked(output, _body, '</svg>\n');
    return string(output);
  }
}

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



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

pragma solidity ^0.8.0;

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

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

// File: assetprovider.sol/IAssetProvider.sol



// SPDX-License-Identifier: MIT

/**
 * This is a part of an effort to create a decentralized autonomous marketplace for digital assets,
 * which allows artists and developers to sell their arts and generative arts.
 *
 * Please see "https://fullyonchain.xyz/" for details. 
 *
 * Created by Satoshi Nakajima (@snakajima)
 */
pragma solidity ^0.8.6;

/**
 * IAssetProvider is the interface each asset provider implements.
 * We assume there are three types of asset providers.
 * 1. Static asset provider, which has a collection of assets (either in the storage or the code) and returns them.
 * 2. Generative provider, which dynamically (but deterministically from the seed) generates assets.
 * 3. Data visualizer, which generates assets based on various data on the blockchain.
 *
 * Note: Asset providers MUST implements IERC165 (supportsInterface method) as well. 
 */
interface IAssetProvider {
  struct ProviderInfo {
    string key;  // short and unique identifier of this provider (e.g., "asset")
    string name; // human readable display name (e.g., "Asset Store")
    IAssetProvider provider;
  }
  function getProviderInfo() external view returns(ProviderInfo memory);

  /**
   * This function returns SVGPart and the tag. The SVGPart consists of one or more SVG elements.
   * The tag specifies the identifier of the SVG element to be displayed (using <use> tag).
   * The tag is the combination of the provider key and assetId (e.e., "asset123")
   */
  function generateSVGPart(uint256 _assetId) external view returns(string memory svgPart, string memory tag);

  /**
   * This is an optional function, which returns various traits of the image for ERC721 token.
   * Format: {"trait_type":"TRAIL_TYPE","value":"VALUE"},{...}
   */
  function generateTraits(uint256 _assetId) external view returns (string memory);
  
  /**
   * This function returns the number of assets available from this provider. 
   * If the total supply is 100, assetIds of available assets are 0,1,...99.
   * The generative providers may returns 0, which indicates the provider dynamically but
   * deterministically generates assets using the given assetId as the random seed.
   */
  function totalSupply() external view returns(uint256);

  /**
   * Returns the onwer. The registration update is possible only if both contracts have the same owner. 
   */
  function getOwner() external view returns (address);

  /**
   * This function processes the royalty payment from the decentralized autonomous marketplace. 
   */
  function processPayout(uint256 _assetId) external payable;

  event Payout(string providerKey, uint256 assetId, address payable to, uint256 amount);
}

interface IAssetProviderEx is IAssetProvider {
  function generateSVGDocument(uint256 _assetId) external view returns(string memory document);
}
// File: @openzeppelin/contracts/access/Ownable.sol



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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

// File: @openzeppelin/contracts/interfaces/IERC165.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC165.sol)

pragma solidity ^0.8.0;

import "../utils/introspection/IERC165.sol";

// File: bytes-array.sol/BytesArray.sol



// SPDX-License-Identifier: MIT

/*
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

library BytesArray {
  /**
   * Equivalent to abi.encodedPacked(parts[0], parts[1], ..., parts[N-1]), where
   * N is the length of bytes.
   *
   * The complexty of this algorithm is O(M), where M is the number of total bytes.
   * Calling abi.encodePacked() in a loop reallocates memory N times, therefore,
   * the complexity will become O(M * N). 
   */
  function packed(bytes[] memory parts) internal pure returns (bytes memory ret) {
    uint count = parts.length;
    assembly {
      ret := mload(0x40)
      let retMemory := add(ret, 0x20)
      let bufParts := add(parts, 0x20)
      for {let i := 0} lt(i, count) {i := add(i, 1)} {
        let src := mload(bufParts) // read the address
        let dest := retMemory
        let length := mload(src)
        // copy 0x20 bytes each (and let it overrun)
        for {let j := 0} lt(j, length) {j := add(j, 0x20)} {
          src := add(src, 0x20) // dual purpose
          mstore(dest, mload(src))
          dest := add(dest, 0x20)
        }
        retMemory := add(retMemory, length)
        bufParts := add(bufParts, 0x20)
      }
      mstore(ret, sub(sub(retMemory, ret), 0x20))
      mstore(0x40, retMemory)
    }
  }

}
// File: fully-on-chain.sol/IFontProvider.sol



// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

interface IFontProvider {
  function height() external view returns(uint);
  function baseline() external view returns(uint);
  function widthOf(string memory _char) external view returns(uint);
  function pathOf(string memory _char) external view returns(bytes memory);

  /**
   * This function processes the royalty payment from the decentralized autonomous marketplace. 
   */
  function processPayout() external payable;

  event Payout(string providerKey, address payable to, uint256 amount);
}
// File: fully-on-chain.sol/Path.sol



// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import "./Vector.sol";

library Path {
  function roundedCorner(Vector.Struct memory _vector) internal pure returns(uint) {
    return uint(_vector.x/0x8000) + (uint(_vector.y/0x8000) << 32) + (566 << 64);
  }

  function sharpCorner(Vector.Struct memory _vector) internal pure returns(uint) {
    return uint(_vector.x/0x8000) + (uint(_vector.y/0x8000) << 32) + (0x1 << 80);
  }

  function closedPath(uint[] memory points) internal pure returns(bytes memory newPath) {
    uint length = points.length;
    assembly{
      function toString(_wbuf, _value) -> wbuf {
        let len := 2
        let cmd := 0
        if gt(_value,9) {
          if gt(_value,99) {
            if gt(_value,999) {
              cmd := or(shl(8, cmd), add(48, div(_value, 1000))) 
              len := add(1, len)
              _value := mod(_value, 1000)
            }
            cmd := or(shl(8, cmd), add(48, div(_value, 100)))
            len := add(1, len)
            _value := mod(_value, 100)
          }
          cmd := or(shl(8, cmd), add(48, div(_value, 10)))
          len := add(1, len)
          _value := mod(_value, 10)
        }
        cmd := or(or(shl(16, cmd), shl(8, add(48, _value))), 32)

        mstore(_wbuf, shl(sub(256, mul(len, 8)), cmd))
        wbuf := add(_wbuf, len)
      }

      // dynamic allocation
      newPath := mload(0x40)
      let wbuf := add(newPath, 0x20)
      let rbuf := add(points, 0x20)

      let wordP := mload(add(rbuf, mul(sub(length,1), 0x20)))
      let word := mload(rbuf)
      for {let i := 0} lt(i, length) {i := add(i, 1)} {
        let x := and(word, 0xffffffff)
        let y := and(shr(32, word), 0xffffffff)
        let r := and(shr(64, word), 0xffff)
        let sx := div(add(x, and(wordP, 0xffffffff)),2)
        let sy := div(add(y, and(shr(32, wordP), 0xffffffff)),2)
        if eq(i, 0) {
          mstore(wbuf, shl(248, 0x4D)) // M
          wbuf := add(wbuf, 1)
          wbuf := toString(wbuf, sx)
          wbuf := toString(wbuf, sy)
        }
        
        let wordN := mload(add(rbuf, mul(mod(add(i,1), length), 0x20)))
        {
          let ex := div(add(x, and(wordN, 0xffffffff)),2)
          let ey := div(add(y, and(shr(32, wordN), 0xffffffff)),2)

          switch and(shr(80, word), 0x01) 
            case 0 {
              mstore(wbuf, shl(248, 0x43)) // C
              wbuf := add(wbuf, 1)
              x := mul(x, r)
              y := mul(y, r)
              r := sub(1024, r)
              wbuf := toString(wbuf, div(add(x, mul(sx, r)),1024))
              wbuf := toString(wbuf, div(add(y, mul(sy, r)),1024))
              wbuf := toString(wbuf, div(add(x, mul(ex, r)),1024))
              wbuf := toString(wbuf, div(add(y, mul(ey, r)),1024))
            }
            default {
              mstore(wbuf, shl(248, 0x4C)) // L
              wbuf := add(wbuf, 1)
              wbuf := toString(wbuf, x)
              wbuf := toString(wbuf, y)
            }
          wbuf := toString(wbuf, ex)
          wbuf := toString(wbuf, ey)
        }
        wordP := word
        word := wordN
      }

      mstore(newPath, sub(sub(wbuf, newPath), 0x20))
      mstore(0x40, wbuf)
    }
  }

  function decode(bytes memory body) internal pure returns (bytes memory) {
    bytes memory ret;
    assembly{
      let bodyMemory := add(body, 0x20)
      let length := div(mul(mload(body), 2), 3)
      ret := mload(0x40)
      let retMemory := add(ret, 0x20)
      let data
      for {let i := 0} lt(i, length) {i := add(i, 1)} {
        if eq(mod(i, 16), 0) {
          data := mload(bodyMemory) // reading 8 extra bytes
          bodyMemory := add(bodyMemory, 24)
        }
        let low
        let high
        switch mod(i, 2)
        case 0 {
          low := shr(248, data)
          high := and(shr(240, data), 0x0f)
        }
        default {
          low := and(shr(232, data), 0xff)
          high := and(shr(244, data), 0x0f)
          data := shl(24, data)
        }
        
        switch high
        case 0 {
          if or(and(gt(low, 64), lt(low, 91)), and(gt(low, 96), lt(low, 123))) {
            mstore(retMemory, shl(248, low))
            retMemory := add(retMemory, 1)
          }
        }
        default {
          let cmd := 0
          let lenCmd := 2 // last digit and space
          // SVG value: undo (value + 1024) + 0x100 
          let value := sub(add(shl(8, high), low), 0x0100)
          switch lt(value, 1024)
          case 0 {
            value := sub(value, 1024)
          }
          default {
            cmd := 45 // "-"
            lenCmd := 3
            value := sub(1024,value)
          }
          if gt(value,9) {
            if gt(value,99) {
              if gt(value,999) {
                cmd := or(shl(8, cmd), 49) // always "1"
                lenCmd := add(1, lenCmd)
                value := mod(value, 1000)
              }
              cmd := or(shl(8, cmd), add(48, div(value, 100)))
              lenCmd := add(1, lenCmd)
              value := mod(value, 100)
            }
            cmd := or(shl(8, cmd), add(48, div(value, 10)))
            lenCmd := add(1, lenCmd)
            value := mod(value, 10)
          }
          // last digit and space
          cmd := or(or(shl(16, cmd), shl(8, add(48, value))), 32)

          mstore(retMemory, shl(sub(256, mul(lenCmd, 8)), cmd))
          retMemory := add(retMemory, lenCmd)
        }
      }
      mstore(ret, sub(sub(retMemory, ret), 0x20))
      mstore(0x40, retMemory)
    }
    return ret;
  }
}


// File: fully-on-chain.sol/Transform.sol



// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import "@openzeppelin/contracts/utils/Strings.sol";
import "bytes-array.sol/BytesArray.sol";

library TX {
  using Strings for uint;
  using BytesArray for bytes[];

  function toString(int _value) internal pure returns(string memory) {
    if (_value > 0) {
      return uint(_value).toString();
    }
    return string(abi.encodePacked("-", uint(-_value).toString()));
  }

  function translate(int x, int y) internal pure returns(string memory) {
    return string(abi.encodePacked('translate(',toString(x),' ',toString(y),')'));
  }

  function rotate(string memory _base, string memory _value) internal pure returns(string memory) {
    return string(abi.encodePacked(_base, ' rotate(', _value ,')'));
  }

  function scale(string memory _base, string memory _scale) internal pure returns(string memory) {
    return string(abi.encodePacked(_base, ' scale(', _scale ,')'));
  }

  function scale1000(uint _value) internal pure returns(string memory) {
    return string(abi.encodePacked('scale(',fixed1000(_value),')'));
  }

  function scale1000(string memory _base, uint _value) internal pure returns(string memory) {
    return string(abi.encodePacked(_base, ' scale(',fixed1000(_value),')'));
  }

  function fixed1000(uint _value) internal pure returns(string memory) {
    bytes[] memory array = new bytes[](3);
    if (_value > 1000) {
      array[0] = bytes((_value / 1000).toString());
    } else {
      array[0] = "0";
    }
    if (_value < 10) {
      array[1] = ".00";
    } else if (_value < 100) {
      array[1] = ".0";
    } else {
      array[1] = ".";
    }
    array[2] = bytes(_value.toString());
    return string(array.packed());
  }
}
// File: fully-on-chain.sol/Vector.sol



// SPDX-License-Identifier: MIT

/*
 * This is a part of fully-on-chain.sol, a npm package that allows developers
 * to create fully on-chain generative art.
 *
 * Created by Satoshi Nakajima (@snakajima)
 */

pragma solidity ^0.8.6;

import "trigonometry.sol/Trigonometry.sol";

library Vector {
  using Trigonometry for uint;
  int constant PI = 0x2000;
  int constant PI2 = 0x4000;
  int constant ONE = 0x8000;

  struct Struct {
    int x; // fixed point * ONE
    int y; // fixed point * ONE
  }

  function vector(int _x, int _y) internal pure returns(Struct memory newVector) {
    newVector.x = _x * ONE;
    newVector.y = _y * ONE;
  }

  function vectorWithAngle(int _angle, int _radius) internal pure returns(Struct memory newVector) {
    uint angle = uint(_angle + (PI2 << 64));
    newVector.x = _radius * angle.cos();
    newVector.y = _radius * angle.sin();
  }

  function div(Struct memory _vector, int _value) internal pure returns(Struct memory newVector) {
    newVector.x = _vector.x / _value;
    newVector.y = _vector.y / _value;
  }

  function mul(Struct memory _vector, int _value) internal pure returns(Struct memory newVector) {
    newVector.x = _vector.x * _value;
    newVector.y = _vector.y * _value;
  }

  function add(Struct memory _vector, Struct memory _vector2) internal pure returns(Struct memory newVector) {
    newVector.x = _vector.x + _vector2.x;
    newVector.y = _vector.y + _vector2.y;
  }

  function rotate(Struct memory _vector, int _angle) internal pure returns(Struct memory newVector) {
    uint angle = uint(_angle + (PI2 << 64));
    int cos = angle.cos();
    int sin = angle.sin();
    newVector.x = (cos * _vector.x - sin * _vector.y) / ONE;
    newVector.y = (sin * _vector.x + cos * _vector.y) / ONE;
  }
}
// File: trigonometry.sol/Trigonometry.sol



/**
 * Basic trigonometry functions
 *
 * Solidity library offering the functionality of basic trigonometry functions
 * with both input and output being integer approximated.
 *
 * This code was originally written by Lefteris Karapetsas
 * https://github.com/Sikorkaio/sikorka/blob/master/contracts/trigonometry.sol
 *
 * I made several changes to make it easy for me to manage and use. 
 *
 * @author Lefteris Karapetsas 
 * @author Satoshi Nakajima (snakajima)
 * @license BSD3
 */

// SPDX-License-Identifier: BSD3
pragma solidity ^0.8.6;

library Trigonometry {

    // constant sine lookup table generated by gen_tables.py
    // We have no other choice but this since constant arrays don't yet exist
    uint8 constant entry_bytes = 2;
    bytes constant sin_table = "\x00\x00\x0c\x8c\x18\xf9\x25\x28\x30\xfb\x3c\x56\x47\x1c\x51\x33\x5a\x82\x62\xf1\x6a\x6d\x70\xe2\x76\x41\x7a\x7c\x7d\x89\x7f\x61\x7f\xff";

    function sin_table_lookup(uint index) pure internal returns (uint16) {
        bytes memory table = sin_table;
        uint offset = (index + 1) * entry_bytes;
        uint16 trigint_value;
        assembly {
            trigint_value := mload(add(table, offset))
        }

        return trigint_value;
    }

    /**
     * Return the sine of an integer approximated angle as a signed 16-bit
     * integer.
     *
     * @param _angle A 14-bit angle. This divides the circle into 16384 (0x4000)
     *               angle units, instead of the standard 360 degrees.
     * @return The sine result as a number in the range -32767 to 32767.
     */
    function sin(uint _angle) internal pure returns (int) {
        uint angle = _angle % 0x4000;
        if (angle < 0x2000) {
            return sinQuarter(angle < 0x1000 ? angle : 0x2000 - angle);
        }
        return -sinQuarter(angle < 0x3000 ? angle - 0x2000 : 0x4000 - angle);
    }

    function sinQuarter(uint _angle) internal pure returns (int) {
        if (_angle == 0x1000) {
            return 0x7fff;
        }
        uint index = _angle / 0x100; // high 4-bit
        uint interp = _angle & 0xFF; // low 8-bit
        uint x1 = sin_table_lookup(index);
        uint x2 = sin_table_lookup(index + 1);
        return int(x1 + ((x2 - x1) * interp) / 0x100);
    }

    /**
     * Return the cos of an integer approximated angle.
     * It functions just like the sin() method but uses the trigonometric
     * identity sin(x + pi/2) = cos(x) to quickly calculate the cos.
     */
    function cos(uint _angle) internal pure returns (int) {
        return sin(_angle + 0x1000);
    }
}
// File: @openzeppelin/contracts/utils/Context.sol



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

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: @openzeppelin/contracts/utils/introspection/IERC165.sol



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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}