contract ERC721A is Context, ERC165, IERC721, IERC721Metadata {
	using Address for address;
	using Strings for uint256;

	// Compiler will pack this into a single 256bit word.
	struct TokenOwnership {
		// The address of the owner.
		address addr;
		// Keeps track of the start time of ownership with minimal overhead for tokenomics.
		uint64 startTimestamp;
		// Whether the token has been burned.
		bool burned;
	}

	// Compiler will pack this into a single 256bit word.
	struct AddressData {
		// Realistically, 2**64-1 is more than enough.
		uint64 balance;
		// Keeps track of mint count with minimal overhead for tokenomics.
		uint64 numberMinted;
		// Keeps track of burn count with minimal overhead for tokenomics.
		uint64 numberBurned;
		// For miscellaneous variable(s) pertaining to the address
		// (e.g. number of whitelist mint slots used).
		// If there are multiple variables, please pack them into a uint64.
		uint64 aux;
	}

	// The tokenId of the next token to be minted.
	uint256 internal _currentIndex;

	// The number of tokens burned.
	uint256 internal _burnCounter;

	// Token name
	string private _name;

	// Token symbol
	string private _symbol;

	// Mapping from token ID to ownership details
	// An empty struct value does not necessarily mean the token is unowned. See ownershipOf implementation for details.
	mapping(uint256 => TokenOwnership) internal _ownerships;

	// Mapping owner address to address data
	mapping(address => AddressData) private _addressData;

	// Mapping from token ID to approved address
	mapping(uint256 => address) private _tokenApprovals;

	// Mapping from owner to operator approvals
	mapping(address => mapping(address => bool)) private _operatorApprovals;

	constructor(string memory name_, string memory symbol_) {
		_name = name_;
		_symbol = symbol_;
		_currentIndex = _startTokenId();
	}

	/**
	 * To change the starting tokenId, please override this function.
	 */
	function _startTokenId() internal view virtual returns (uint256) {
		return 0;
	}

	/**
	 * @dev See {IERC721Enumerable-totalSupply}.
	 * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
	 */
	function totalSupply() public view returns (uint256) {
		// Counter underflow is impossible as _burnCounter cannot be incremented
		// more than _currentIndex - _startTokenId() times
		unchecked {
			return _currentIndex - _burnCounter - _startTokenId();
		}
	}

	/**
	 * Returns the total amount of tokens minted in the contract.
	 */
	function _totalMinted() internal view returns (uint256) {
		// Counter underflow is impossible as _currentIndex does not decrement,
		// and it is initialized to _startTokenId()
		unchecked {
			return _currentIndex - _startTokenId();
		}
	}

	/**
	 * @dev See {IERC165-supportsInterface}.
	 */
	function supportsInterface(bytes4 interfaceId)
		public
		view
		virtual
		override(ERC165, IERC165)
		returns (bool)
	{
		return
			interfaceId == type(IERC721).interfaceId ||
			interfaceId == type(IERC721Metadata).interfaceId ||
			super.supportsInterface(interfaceId);
	}

	/**
	 * @dev See {IERC721-balanceOf}.
	 */
	function balanceOf(address owner) public view override returns (uint256) {
		if (owner == address(0)) revert BalanceQueryForZeroAddress();
		return uint256(_addressData[owner].balance);
	}

	/**
	 * Returns the number of tokens minted by `owner`.
	 */
	function _numberMinted(address owner) internal view returns (uint256) {
		if (owner == address(0)) revert MintedQueryForZeroAddress();
		return uint256(_addressData[owner].numberMinted);
	}

	/**
	 * Returns the number of tokens burned by or on behalf of `owner`.
	 */
	function _numberBurned(address owner) internal view returns (uint256) {
		if (owner == address(0)) revert BurnedQueryForZeroAddress();
		return uint256(_addressData[owner].numberBurned);
	}

	/**
	 * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
	 */
	function _getAux(address owner) internal view returns (uint64) {
		if (owner == address(0)) revert AuxQueryForZeroAddress();
		return _addressData[owner].aux;
	}

	/**
	 * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
	 * If there are multiple variables, please pack them into a uint64.
	 */
	function _setAux(address owner, uint64 aux) internal {
		if (owner == address(0)) revert AuxQueryForZeroAddress();
		_addressData[owner].aux = aux;
	}

	/**
	 * Gas spent here starts off proportional to the maximum mint batch size.
	 * It gradually moves to O(1) as tokens get transferred around in the collection over time.
	 */
	function ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
		uint256 curr = tokenId;

		unchecked {
			if (_startTokenId() <= curr && curr < _currentIndex) {
				TokenOwnership memory ownership = _ownerships[curr];
				if (!ownership.burned) {
					if (ownership.addr != address(0)) {
						return ownership;
					}
					// Invariant:
					// There will always be an ownership that has an address and is not burned
					// before an ownership that does not have an address and is not burned.
					// Hence, curr will not underflow.
					while (true) {
						curr--;
						ownership = _ownerships[curr];
						if (ownership.addr != address(0)) {
							return ownership;
						}
					}
				}
			}
		}
		revert OwnerQueryForNonexistentToken();
	}

	/**
	 * @dev See {IERC721-ownerOf}.
	 */
	function ownerOf(uint256 tokenId) public view override returns (address) {
		return ownershipOf(tokenId).addr;
	}

	/**
	 * @dev See {IERC721Metadata-name}.
	 */
	function name() public view virtual override returns (string memory) {
		return _name;
	}

	/**
	 * @dev See {IERC721Metadata-symbol}.
	 */
	function symbol() public view virtual override returns (string memory) {
		return _symbol;
	}

	/**
	 * @dev See {IERC721Metadata-tokenURI}.
	 */
	function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
		if (!_exists(tokenId)) revert URIQueryForNonexistentToken();

		string memory baseURI = _baseURI();
		return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
	}

	/**
	 * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
	 * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
	 * by default, can be overriden in child contracts.
	 */
	function _baseURI() internal view virtual returns (string memory) {
		return "";
	}

	/**
	 * @dev See {IERC721-approve}.
	 */
	function approve(address to, uint256 tokenId) public override {
		address owner = ERC721A.ownerOf(tokenId);
		if (to == owner) revert ApprovalToCurrentOwner();

		if (_msgSender() != owner && !isApprovedForAll(owner, _msgSender())) {
			revert ApprovalCallerNotOwnerNorApproved();
		}

		_approve(to, tokenId, owner);
	}

	/**
	 * @dev See {IERC721-getApproved}.
	 */
	function getApproved(uint256 tokenId) public view override returns (address) {
		if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();

		return _tokenApprovals[tokenId];
	}

	/**
	 * @dev See {IERC721-setApprovalForAll}.
	 */
	function setApprovalForAll(address operator, bool approved) public override {
		if (operator == _msgSender()) revert ApproveToCaller();

		_operatorApprovals[_msgSender()][operator] = approved;
		emit ApprovalForAll(_msgSender(), operator, approved);
	}

	/**
	 * @dev See {IERC721-isApprovedForAll}.
	 */
	function isApprovedForAll(address owner, address operator)
		public
		view
		virtual
		override
		returns (bool)
	{
		return _operatorApprovals[owner][operator];
	}

	/**
	 * @dev See {IERC721-transferFrom}.
	 */
	function transferFrom(
		address from,
		address to,
		uint256 tokenId
	) public virtual override {
		_transfer(from, to, tokenId);
	}

	/**
	 * @dev See {IERC721-safeTransferFrom}.
	 */
	function safeTransferFrom(
		address from,
		address to,
		uint256 tokenId
	) public virtual override {
		safeTransferFrom(from, to, tokenId, "");
	}

	/**
	 * @dev See {IERC721-safeTransferFrom}.
	 */
	function safeTransferFrom(
		address from,
		address to,
		uint256 tokenId,
		bytes memory _data
	) public virtual override {
		_transfer(from, to, tokenId);
		if (to.isContract() && !_checkContractOnERC721Received(from, to, tokenId, _data)) {
			revert TransferToNonERC721ReceiverImplementer();
		}
	}

	/**
	 * @dev Returns whether `tokenId` exists.
	 *
	 * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
	 *
	 * Tokens start existing when they are minted (`_mint`),
	 */
	function _exists(uint256 tokenId) internal view returns (bool) {
		return _startTokenId() <= tokenId && tokenId < _currentIndex && !_ownerships[tokenId].burned;
	}

	function _safeMint(address to, uint256 quantity) internal {
		_safeMint(to, quantity, "");
	}

	/**
	 * @dev Safely mints `quantity` tokens and transfers them to `to`.
	 *
	 * Requirements:
	 *
	 * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
	 * - `quantity` must be greater than 0.
	 *
	 * Emits a {Transfer} event.
	 */
	function _safeMint(
		address to,
		uint256 quantity,
		bytes memory _data
	) internal {
		_mint(to, quantity, _data, true);
	}

	/**
	 * @dev Mints `quantity` tokens and transfers them to `to`.
	 *
	 * Requirements:
	 *
	 * - `to` cannot be the zero address.
	 * - `quantity` must be greater than 0.
	 *
	 * Emits a {Transfer} event.
	 */
	function _mint(
		address to,
		uint256 quantity,
		bytes memory _data,
		bool safe
	) internal {
		uint256 startTokenId = _currentIndex;
		if (to == address(0)) revert MintToZeroAddress();
		if (quantity == 0) revert MintZeroQuantity();

		_beforeTokenTransfers(address(0), to, startTokenId, quantity);

		// Overflows are incredibly unrealistic.
		// balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
		// updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
		unchecked {
			_addressData[to].balance += uint64(quantity);
			_addressData[to].numberMinted += uint64(quantity);

			_ownerships[startTokenId].addr = to;
			_ownerships[startTokenId].startTimestamp = uint64(block.timestamp);

			uint256 updatedIndex = startTokenId;
			uint256 end = updatedIndex + quantity;

			if (safe && to.isContract()) {
				do {
					emit Transfer(address(0), to, updatedIndex);
					if (!_checkContractOnERC721Received(address(0), to, updatedIndex++, _data)) {
						revert TransferToNonERC721ReceiverImplementer();
					}
				} while (updatedIndex != end);
				// Reentrancy protection
				if (_currentIndex != startTokenId) revert();
			} else {
				do {
					emit Transfer(address(0), to, updatedIndex++);
				} while (updatedIndex != end);
			}
			_currentIndex = updatedIndex;
		}
		_afterTokenTransfers(address(0), to, startTokenId, quantity);
	}

	/**
	 * @dev Transfers `tokenId` from `from` to `to`.
	 *
	 * Requirements:
	 *
	 * - `to` cannot be the zero address.
	 * - `tokenId` token must be owned by `from`.
	 *
	 * Emits a {Transfer} event.
	 */
	function _transfer(
		address from,
		address to,
		uint256 tokenId
	) private {
		TokenOwnership memory prevOwnership = ownershipOf(tokenId);

		bool isApprovedOrOwner = (_msgSender() == prevOwnership.addr ||
			isApprovedForAll(prevOwnership.addr, _msgSender()) ||
			getApproved(tokenId) == _msgSender());

		if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
		if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();
		if (to == address(0)) revert TransferToZeroAddress();

		_beforeTokenTransfers(from, to, tokenId, 1);

		// Clear approvals from the previous owner
		_approve(address(0), tokenId, prevOwnership.addr);

		// Underflow of the sender's balance is impossible because we check for
		// ownership above and the recipient's balance can't realistically overflow.
		// Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
		unchecked {
			_addressData[from].balance -= 1;
			_addressData[to].balance += 1;

			_ownerships[tokenId].addr = to;
			_ownerships[tokenId].startTimestamp = uint64(block.timestamp);

			// If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
			// Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
			uint256 nextTokenId = tokenId + 1;
			if (_ownerships[nextTokenId].addr == address(0)) {
				// This will suffice for checking _exists(nextTokenId),
				// as a burned slot cannot contain the zero address.
				if (nextTokenId < _currentIndex) {
					_ownerships[nextTokenId].addr = prevOwnership.addr;
					_ownerships[nextTokenId].startTimestamp = prevOwnership.startTimestamp;
				}
			}
		}

		emit Transfer(from, to, tokenId);
		_afterTokenTransfers(from, to, tokenId, 1);
	}

	/**
	 * @dev Destroys `tokenId`.
	 * The approval is cleared when the token is burned.
	 *
	 * Requirements:
	 *
	 * - `tokenId` must exist.
	 *
	 * Emits a {Transfer} event.
	 */
	function _burn(uint256 tokenId) internal virtual {
		TokenOwnership memory prevOwnership = ownershipOf(tokenId);

		_beforeTokenTransfers(prevOwnership.addr, address(0), tokenId, 1);

		// Clear approvals from the previous owner
		_approve(address(0), tokenId, prevOwnership.addr);

		// Underflow of the sender's balance is impossible because we check for
		// ownership above and the recipient's balance can't realistically overflow.
		// Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
		unchecked {
			_addressData[prevOwnership.addr].balance -= 1;
			_addressData[prevOwnership.addr].numberBurned += 1;

			// Keep track of who burned the token, and the timestamp of burning.
			_ownerships[tokenId].addr = prevOwnership.addr;
			_ownerships[tokenId].startTimestamp = uint64(block.timestamp);
			_ownerships[tokenId].burned = true;

			// If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
			// Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
			uint256 nextTokenId = tokenId + 1;
			if (_ownerships[nextTokenId].addr == address(0)) {
				// This will suffice for checking _exists(nextTokenId),
				// as a burned slot cannot contain the zero address.
				if (nextTokenId < _currentIndex) {
					_ownerships[nextTokenId].addr = prevOwnership.addr;
					_ownerships[nextTokenId].startTimestamp = prevOwnership.startTimestamp;
				}
			}
		}

		emit Transfer(prevOwnership.addr, address(0), tokenId);
		_afterTokenTransfers(prevOwnership.addr, address(0), tokenId, 1);

		// Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.
		unchecked {
			_burnCounter++;
		}
	}

	/**
	 * @dev Approve `to` to operate on `tokenId`
	 *
	 * Emits a {Approval} event.
	 */
	function _approve(
		address to,
		uint256 tokenId,
		address owner
	) private {
		_tokenApprovals[tokenId] = to;
		emit Approval(owner, to, tokenId);
	}

	/**
	 * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract.
	 *
	 * @param from address representing the previous owner of the given token ID
	 * @param to target address that will receive the tokens
	 * @param tokenId uint256 ID of the token to be transferred
	 * @param _data bytes optional data to send along with the call
	 * @return bool whether the call correctly returned the expected magic value
	 */
	function _checkContractOnERC721Received(
		address from,
		address to,
		uint256 tokenId,
		bytes memory _data
	) private returns (bool) {
		try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (
			bytes4 retval
		) {
			return retval == IERC721Receiver(to).onERC721Received.selector;
		} catch (bytes memory reason) {
			if (reason.length == 0) {
				revert TransferToNonERC721ReceiverImplementer();
			} else {
				assembly {
					revert(add(32, reason), mload(reason))
				}
			}
		}
	}

	/**
	 * @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
	 * And also called before burning one token.
	 *
	 * startTokenId - the first token id to be transferred
	 * quantity - the amount to be transferred
	 *
	 * Calling conditions:
	 *
	 * - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
	 * transferred to `to`.
	 * - When `from` is zero, `tokenId` will be minted for `to`.
	 * - When `to` is zero, `tokenId` will be burned by `from`.
	 * - `from` and `to` are never both zero.
	 */
	function _beforeTokenTransfers(
		address from,
		address to,
		uint256 startTokenId,
		uint256 quantity
	) internal virtual {}

	/**
	 * @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
	 * minting.
	 * And also called after one token has been burned.
	 *
	 * startTokenId - the first token id to be transferred
	 * quantity - the amount to be transferred
	 *
	 * Calling conditions:
	 *
	 * - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
	 * transferred to `to`.
	 * - When `from` is zero, `tokenId` has been minted for `to`.
	 * - When `to` is zero, `tokenId` has been burned by `from`.
	 * - `from` and `to` are never both zero.
	 */
	function _afterTokenTransfers(
		address from,
		address to,
		uint256 startTokenId,
		uint256 quantity
	) internal virtual {}
}
/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension. Built to optimize for lower gas during batch mints.
 *
 * Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
 *
 * Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
 *
 * Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
 */
function name() public view virtual override returns (string memory) {
	return _name;
}

/**
 	 * @dev See {IERC721Metadata-name}.
 	 */