├── .env.example
├── .gitignore
├── tsconfig.json
├── scripts
├── planetsMetadata.ts
├── DrGreenMarketplace.ts
└── DrGreenNFT.ts
├── contracts
├── PlanetsMetadataInterface.sol
├── DrGreenNFTInterface.sol
├── DrGreenTimelock.sol
├── IERC7496.sol
├── DrGreenMarketplace.sol
├── DynamicTraits.sol
├── DrGreenNFT.sol
└── PlanetsMetadata.sol
├── hardhat.config.ts
├── package.json
├── LICENSE.md
├── README.md
└── test
├── DrGreenMarketplace.test.js
└── DrGreenNFT.test.js
/.env.example:
--------------------------------------------------------------------------------
1 | ALCHEMY_API_URL=""
2 | METAMASK_PRIVATE_KEY=""
3 | ETHERSCAN_API_KEY=""
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | node_modules
3 | typechain-types
4 | artifacts
5 | package-lock.json
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2020",
4 | "module": "commonjs",
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "strict": true,
8 | "skipLibCheck": true,
9 | "resolveJsonModule": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/scripts/planetsMetadata.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "hardhat";
2 |
3 | async function PlanetsMetadata() {
4 |
5 | const nftContract = await ethers.getContractFactory("PlanetsMetadata");
6 |
7 | const contract = await nftContract.deploy();
8 |
9 | console.log("Contract deployed at:", contract.target)
10 | }
11 |
12 | // We recommend this pattern to be able to use async/await everywhere
13 | // and properly handle errors.
14 | PlanetsMetadata().catch((error) => {
15 | console.error(error);
16 | process.exitCode = 1;
17 | });
18 |
--------------------------------------------------------------------------------
/scripts/DrGreenMarketplace.ts:
--------------------------------------------------------------------------------
1 | import { ethers, upgrades } from "hardhat";
2 |
3 | async function DrGreenMarketplace() {
4 | const DrGreenMarketplace = await ethers.getContractFactory(
5 | "DrGreenMarketplace"
6 | );
7 | const marketplace = await DrGreenMarketplace.deploy(
8 | "0x4758FFb3EF10bf657d4264AEd72db6Db564a9e11"
9 | );
10 | await marketplace.waitForDeployment();
11 | console.log("Marketplace deployed to:", await marketplace.getAddress());
12 | }
13 |
14 | DrGreenMarketplace().catch((error) => {
15 | console.error(error);
16 | process.exitCode = 1;
17 | });
18 |
--------------------------------------------------------------------------------
/contracts/PlanetsMetadataInterface.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.22;
3 |
4 | interface PlanetsMetadataInterface {
5 | function setRandomTokenMetadata(uint8 planetNo, uint256 tokenId)
6 | external
7 | returns (uint16);
8 |
9 | function setTokenMetadataId(uint256 tokenId, uint16 metadataId) external;
10 |
11 | function getMetadataIdByToken(uint256 tokenId)
12 | external
13 | view
14 | returns (uint256);
15 |
16 | function getAvailableNFTsbyPlanet(uint8 planetNo)
17 | external
18 | view
19 | returns (uint256);
20 | }
--------------------------------------------------------------------------------
/contracts/DrGreenNFTInterface.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity 0.8.22;
4 |
5 | interface DrGreenNFTInterface {
6 | function addClient(uint16 tokenId, uint256 clientsToAdd) external;
7 |
8 | function addTransaction(
9 | uint16 tokenId,
10 | uint256 txsToAdd,
11 | uint256 txsAmtToAdd,
12 | bool isRefunded
13 | ) external;
14 |
15 | function ownerOf(uint256 tokenId) external view returns (address);
16 |
17 | function royaltyInfo(
18 | uint256 tokenId,
19 | uint256 salePrice
20 | ) external view returns (address, uint256);
21 |
22 | function safeTransferFrom(address from, address to, uint256 tokenId) external;
23 | }
24 |
--------------------------------------------------------------------------------
/contracts/DrGreenTimelock.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | import "@openzeppelin/contracts/governance/TimelockController.sol";
5 |
6 | contract DrGreenTimelock is TimelockController {
7 | /**
8 | * @dev Constructor for the TimelockController
9 | * @param minDelay Minimum delay for executing proposals
10 | * @param proposers List of addresses with proposer role
11 | * @param executors List of addresses with executor role
12 | */
13 | constructor(
14 | uint256 minDelay,
15 | address[] memory proposers,
16 | address[] memory executors,
17 | address admin
18 | ) TimelockController(minDelay, proposers, executors, admin) {
19 | // Timelock initialized with given parameters
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/scripts/DrGreenNFT.ts:
--------------------------------------------------------------------------------
1 | import { ethers } from "hardhat";
2 |
3 | async function DrGreenNFT() {
4 | const nftContract = await ethers.getContractFactory("DrGreenNFT");
5 |
6 | const contract = await nftContract.deploy(
7 | "0xb3182Ae1069253DC69910F6475578878d3679DAc",
8 | "ipfs://QmNhYFSkkiWKAvL56iZkEfpLmvwubihNUroHqKSMsUQkWi/112_md/",
9 | "ipfs://Qma9B7dUc1FSnTjidRYNHx9R2KRGvXBWQsfmXnW8jpRNF8",
10 | "0x6397277046830b948B8029e2612d3CB23fB7c2b7",
11 | "0x32E71eD285659C4038c938c487C915fbc684ecf4"
12 | );
13 | console.log("Contract deployed at:", contract.target);
14 | }
15 |
16 | // We recommend this pattern to be able to use async/await everywhere
17 | // and properly handle errors.
18 | DrGreenNFT().catch((error) => {
19 | console.error(error);
20 | process.exitCode = 1;
21 | });
22 |
--------------------------------------------------------------------------------
/hardhat.config.ts:
--------------------------------------------------------------------------------
1 | import { HardhatUserConfig } from "hardhat/config";
2 | import "@nomiclabs/hardhat-ethers";
3 | import '@openzeppelin/hardhat-upgrades';
4 | import "@nomicfoundation/hardhat-toolbox";
5 | require("dotenv").config();
6 |
7 | const { ALCHEMY_API_URL, METAMASK_PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env;
8 |
9 | const config: HardhatUserConfig = {
10 | defaultNetwork: "sepolia",
11 | networks: {
12 | sepolia: {
13 | url: `${ALCHEMY_API_URL}`,
14 | accounts: [`${METAMASK_PRIVATE_KEY}`],
15 | },
16 | },
17 | solidity: {
18 | version: "0.8.22",
19 | settings: {
20 | optimizer: {
21 | enabled: true,
22 | runs: 200
23 | },
24 | },
25 | },
26 | etherscan: {
27 | apiKey: ETHERSCAN_API_KEY
28 | }
29 | };
30 |
31 | export default config;
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dr-green-nft",
3 | "devDependencies": {
4 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.6",
5 | "@nomicfoundation/hardhat-ethers": "^3.0.5",
6 | "@nomicfoundation/hardhat-network-helpers": "^1.0.10",
7 | "@nomicfoundation/hardhat-toolbox": "^4.0.0",
8 | "@nomicfoundation/hardhat-verify": "^2.0.4",
9 | "@openzeppelin/hardhat-upgrades": "^3.0.5",
10 | "@typechain/ethers-v6": "^0.5.1",
11 | "@typechain/hardhat": "^9.1.0",
12 | "@types/chai": "^4.3.12",
13 | "@types/mocha": "^10.0.6",
14 | "chai": "^4.4.1",
15 | "ethers": "^6.11.1",
16 | "hardhat": "^2.21.0",
17 | "hardhat-gas-reporter": "^1.0.10",
18 | "solidity-coverage": "^0.8.11",
19 | "ts-node": "^10.9.2",
20 | "typechain": "^8.3.2",
21 | "typescript": "^5.4.2"
22 | },
23 | "dependencies": {
24 | "@nomiclabs/hardhat-ethers": "^2.2.3",
25 | "@openzeppelin/contracts": "^5.0.2",
26 | "@openzeppelin/contracts-upgradeable": "^5.0.2",
27 | "dotenv": "^16.4.5"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 © Dr. Green NFT
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/contracts/IERC7496.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: CC0-1.0
2 | pragma solidity 0.8.22;
3 |
4 | interface IERC7496 {
5 | /* Events */
6 | event TraitUpdated(
7 | bytes32 indexed traitKey,
8 | uint256 tokenId,
9 | bytes32 traitValue
10 | );
11 | event TraitUpdatedRange(
12 | bytes32 indexed traitKey,
13 | uint256 fromTokenId,
14 | uint256 toTokenId
15 | );
16 | event TraitUpdatedRangeUniformValue(
17 | bytes32 indexed traitKey,
18 | uint256 fromTokenId,
19 | uint256 toTokenId,
20 | bytes32 traitValue
21 | );
22 | event TraitUpdatedList(bytes32 indexed traitKey, uint256[] tokenIds);
23 | event TraitUpdatedListUniformValue(
24 | bytes32 indexed traitKey,
25 | uint256[] tokenIds,
26 | bytes32 traitValue
27 | );
28 | event TraitMetadataURIUpdated();
29 |
30 | /* Getters */
31 | function getTraitValue(uint256 tokenId, bytes32 traitKey)
32 | external
33 | view
34 | returns (bytes32 traitValue);
35 |
36 | function getTraitValues(uint256 tokenId, bytes32[] calldata traitKeys)
37 | external
38 | view
39 | returns (bytes32[] memory traitValues);
40 |
41 | function getTraitMetadataURI() external view returns (string memory uri);
42 |
43 | /* Setters */
44 | // function setTrait(uint256 tokenId, bytes32 traitKey, bytes32 traitValue) external;
45 |
46 | /* Errors */
47 | error TraitValueUnchanged();
48 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dr. Green NFT
5 | Join the world’s first legal cannabis on-demand delivery business with Dr. Green!
6 |
7 | ## About
8 |
9 | Dr. Green simplifies the way cannabis is bought and sold worldwide. Using Ethereum blockchain and NFT technology as an authentication key ensures that every transaction is transparent and secure.
10 |
11 | With Dr. Green, anyone can join the cannabis industry safely. Our digital key, stored on the blockchain, includes our medical cannabis license and through smart contracts, holders of the digital key can drop ship cannabis legally wherever it's accepted - this includes recreational and medical cannabis.
12 |
13 | ## Dr Green Community
14 |
15 | [Website](https://drgreennft.com/)
16 |
17 | [Marketplace](https://marketplace.drgreennft.com/)
18 |
19 | [Telegram](https://t.me/DrGreenNFTentry)
20 |
21 | [Discord](https://discord.com/invite/drgreen)
22 |
23 | [Twitter](https://x.com/DrGreen_nft)
24 |
25 | [Instagram](https://www.instagram.com/drgreen/)
26 |
27 | [Whitepaper](https://drgreennft.com/assets/drgreen_whitepaper_2024.pdf)
28 |
29 | ## Overview
30 |
31 | This project is built using Hardhat, an Ethereum smart contract development environment. It includes a different smart contract required for NFT Mining and Trading, a deployment script, and a testing framework.
32 |
33 | ## Project Structure
34 |
35 | ```
36 | my-hardhat-project/
37 | ├── contracts/ # Solidity smart contracts
38 | │ └── MyContract.sol # Your custom contract
39 | ├── scripts/ # Deployment scripts
40 | │ └── deploy.js # Sample deployment script
41 | ├── test/ # Test scripts for smart contracts
42 | │ └── MyContract.js # Sample test for MyContract
43 | ├── artifacts/ # Compiled contract files (auto-generated)
44 | ├── cache/ # Cache files (auto-generated)
45 | ├── hardhat.config.js # Hardhat configuration file
46 | └── package.json # Project dependencies and scripts
47 | ```
48 |
49 | ## Prerequisites
50 |
51 | Ensure the following are installed:
52 |
53 | 1. Node.js (v16 or later)
54 | 2. npm (Node Package Manager)
55 |
56 | ## Setup Instructions
57 |
58 | 1. Clone the Repository
59 | ```
60 | git clone
61 | cd my-hardhat-project
62 | ```
63 |
64 | 2. Install Dependencies
65 | ```
66 | npm install
67 | ```
68 |
69 | 3. Compile the Contracts
70 | ```
71 | npx hardhat compile
72 | ```
73 |
74 | 4. Run Tests
75 | Run the test scripts in the test/ directory:
76 | ```
77 | npx hardhat test
78 | ```
79 |
80 | 5. Deploy the Contracts
81 | To deploy the contracts on a local network:
82 | ```
83 | npx hardhat run scripts/deploy.js
84 | ```
85 | To deploy on a specific network (e.g., Ethereum Testnets):
86 |
87 | Configure your hardhat.config.js with the desired network.
88 | Deploy using:
89 | ```
90 | npx hardhat run scripts/deploy.js --network
91 | ```
92 |
93 | 6. Run a Local Node
94 | To simulate a blockchain locally:
95 | ```
96 | npx hardhat node
97 | ```
98 |
99 | ## Configuration
100 | Customize your hardhat.config.js for additional settings:
101 |
102 | - Adding networks (e.g., Rinkeby, Goerli).
103 | - Adjusting Solidity versions.
104 | - Configuring plugins like Etherscan or Gas Reporter.
105 |
106 | ## Dependencies
107 | This project uses:
108 |
109 | - Hardhat: Ethereum development environment.
110 | - Ethers.js: Ethereum interaction library.
111 | - Mocha and Chai: Testing framework.
112 | Install additional plugins if required:
113 | ```
114 | npm install --save-dev @nomiclabs/hardhat-ethers ethers
115 | npm install --save-dev @nomiclabs/hardhat-waffle
116 | npm install --save-dev @nomiclabs/hardhat-etherscan
117 | ```
118 |
119 | ## License
120 | Copyright © Dr. Green NFT. All rights reserved.
121 |
122 | Licensed under the [MIT](LICENSE.md).
123 |
--------------------------------------------------------------------------------
/test/DrGreenMarketplace.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require("chai");
2 | const { ethers } = require("hardhat");
3 |
4 | describe("DrGreenMarketplace", function () {
5 | let DrGreenMarketplace, drGreenMarketplace, DrGreenNFT, drGreenNFT, owner, addr1, addr2;
6 |
7 | beforeEach(async function () {
8 | [owner, addr1, addr2] = await ethers.getSigners();
9 |
10 | // Deploy DrGreenNFT contract
11 | const DrGreenNFTFactory = await ethers.getContractFactory("DrGreenNFT");
12 | drGreenNFT = await DrGreenNFTFactory.deploy();
13 | await drGreenNFT.deployed();
14 |
15 | // Deploy DrGreenMarketplace contract
16 | const DrGreenMarketplaceFactory = await ethers.getContractFactory("DrGreenMarketplace");
17 | drGreenMarketplace = await DrGreenMarketplaceFactory.deploy(drGreenNFT.address);
18 | await drGreenMarketplace.deployed();
19 |
20 | // Mint an NFT to addr1
21 | await drGreenNFT.connect(addr1).mint(1);
22 | });
23 |
24 | it("Should allow buying an NFT with a valid signature", async function () {
25 | const tokenId = 1;
26 | const price = ethers.utils.parseEther("1");
27 | const salt = ethers.utils.randomBytes(32);
28 |
29 | // Generate signature
30 | const message = ethers.utils.solidityKeccak256(
31 | ["address", "uint256", "uint256"],
32 | [addr1.address, tokenId, price]
33 | );
34 | const signature = await addr1.signMessage(ethers.utils.arrayify(message));
35 |
36 | // Approve marketplace to transfer NFT
37 | await drGreenNFT.connect(addr1).setApprovalForAll(drGreenMarketplace.address, true);
38 |
39 | // Buy NFT
40 | await expect(drGreenMarketplace.connect(addr2).buyNFT(tokenId, signature, salt, { value: price }))
41 | .to.emit(drGreenMarketplace, "Bought")
42 | .withArgs(tokenId, drGreenNFT.address, price, addr1.address, addr2.address);
43 |
44 | // Check NFT ownership
45 | expect(await drGreenNFT.ownerOf(tokenId)).to.equal(addr2.address);
46 | });
47 |
48 | it("Should allow canceling a listing with a valid signature", async function () {
49 | const tokenId = 1;
50 | const price = ethers.utils.parseEther("1");
51 | const salt = ethers.utils.randomBytes(32);
52 |
53 | // Generate signature
54 | const message = ethers.utils.solidityKeccak256(
55 | ["address", "uint256", "uint256"],
56 | [addr1.address, tokenId, price]
57 | );
58 | const signature = await addr1.signMessage(ethers.utils.arrayify(message));
59 |
60 | // Cancel listing
61 | await expect(drGreenMarketplace.connect(addr1).cancelListing(tokenId, price, signature, salt))
62 | .to.emit(drGreenMarketplace, "ListingCancelled")
63 | .withArgs(addr1.address, tokenId, signature, await ethers.provider.getBlockNumber());
64 | });
65 |
66 | it("Should revert if signature is already used", async function () {
67 | const tokenId = 1;
68 | const price = ethers.utils.parseEther("1");
69 | const salt = ethers.utils.randomBytes(32);
70 |
71 | // Generate signature
72 | const message = ethers.utils.solidityKeccak256(
73 | ["address", "uint256", "uint256"],
74 | [addr1.address, tokenId, price]
75 | );
76 | const signature = await addr1.signMessage(ethers.utils.arrayify(message));
77 |
78 | // Approve marketplace to transfer NFT
79 | await drGreenNFT.connect(addr1).setApprovalForAll(drGreenMarketplace.address, true);
80 |
81 | // Buy NFT
82 | await drGreenMarketplace.connect(addr2).buyNFT(tokenId, signature, salt, { value: price });
83 |
84 | // Try to buy NFT again with the same signature
85 | await expect(drGreenMarketplace.connect(addr2).buyNFT(tokenId, signature, salt, { value: price }))
86 | .to.be.revertedWith("signature already used");
87 | });
88 |
89 | it("Should revert if signature is invalid", async function () {
90 | const tokenId = 1;
91 | const price = ethers.utils.parseEther("1");
92 | const salt = ethers.utils.randomBytes(32);
93 |
94 | // Generate signature with wrong owner
95 | const message = ethers.utils.solidityKeccak256(
96 | ["address", "uint256", "uint256"],
97 | [addr2.address, tokenId, price]
98 | );
99 | const signature = await addr2.signMessage(ethers.utils.arrayify(message));
100 |
101 | // Approve marketplace to transfer NFT
102 | await drGreenNFT.connect(addr1).setApprovalForAll(drGreenMarketplace.address, true);
103 |
104 | // Try to buy NFT with invalid signature
105 | await expect(drGreenMarketplace.connect(addr2).buyNFT(tokenId, signature, salt, { value: price }))
106 | .to.be.revertedWith("signature validation failed");
107 | });
108 | });
--------------------------------------------------------------------------------
/contracts/DrGreenMarketplace.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier:MIT
2 | pragma solidity 0.8.22;
3 |
4 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
5 | import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
6 | import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
7 | import "./DrGreenNFTInterface.sol";
8 |
9 | contract DrGreenMarketplace is ReentrancyGuard {
10 | DrGreenNFTInterface _NftInterface;
11 |
12 | event Bought(
13 | uint256 indexed tokenId,
14 | address indexed nft,
15 | uint256 price,
16 | address indexed seller,
17 | address buyer
18 | );
19 | event ListingCancelled(
20 | address indexed owner,
21 | uint256 indexed tokenId,
22 | bytes signature,
23 | uint256 timestamp
24 | );
25 |
26 | using ECDSA for bytes32;
27 |
28 | string public constant CONTRACT_NAME = "DrGreenMarketplace";
29 | string public constant VERSION = "1";
30 |
31 | mapping(bytes => bool) private _usedSignatures;
32 |
33 | constructor(address _NftContractAddress) {
34 | require(
35 | _NftContractAddress != address(0),
36 | "_NftContractAddress can not be empty"
37 | );
38 | _NftInterface = DrGreenNFTInterface(_NftContractAddress);
39 | }
40 |
41 | function buyNFT(
42 | uint256 tokenId,
43 | bytes memory sig,
44 | bytes32 salt
45 | ) external payable nonReentrant {
46 | require(sig.length > 0, "signature can not be empty.");
47 | require(salt.length > 0, "salt can not be empty.");
48 | require(!_usedSignatures[sig], "signature already used");
49 | address tokenOwner = _NftInterface.ownerOf(tokenId);
50 |
51 | verifySignature(sig, salt, tokenOwner, tokenId, msg.value);
52 |
53 | // Get royalty information and calculate seller amount
54 | (address receiver, uint256 royaltyAmount) = _NftInterface.royaltyInfo(
55 | tokenId,
56 | msg.value
57 | );
58 |
59 | uint256 sellerAmount = msg.value - royaltyAmount;
60 |
61 | // Perform payment with checks
62 | (bool sentToSeller, ) = tokenOwner.call{value: sellerAmount}("");
63 | require(sentToSeller, "Payment to seller failed");
64 |
65 | (bool sentToReceiver, ) = receiver.call{value: royaltyAmount}("");
66 | require(sentToReceiver, "Royalty payment failed");
67 |
68 | _usedSignatures[sig] = true;
69 |
70 | // Transfer NFT ownership to buyer
71 | _NftInterface.safeTransferFrom(tokenOwner, msg.sender, tokenId);
72 |
73 | emit Bought(
74 | tokenId,
75 | address(_NftInterface),
76 | msg.value,
77 | tokenOwner,
78 | msg.sender
79 | );
80 | }
81 |
82 | function cancelListing(
83 | uint256 tokenId,
84 | uint256 amount,
85 | bytes memory sig,
86 | bytes32 salt
87 | ) external {
88 | require(sig.length > 0, "signature can not be empty.");
89 | require(salt.length > 0, "salt can not be empty.");
90 | require(!_usedSignatures[sig], "signature already used");
91 | address tokenOwner = _NftInterface.ownerOf(tokenId);
92 | require(tokenOwner == msg.sender, "Owner only.");
93 | verifySignature(sig, salt, msg.sender, tokenId, amount);
94 | _usedSignatures[sig] = true;
95 | emit ListingCancelled(msg.sender, tokenId, sig, block.timestamp);
96 | }
97 |
98 | function verifySignature(
99 | bytes memory sig,
100 | bytes32 salt,
101 | address owner,
102 | uint256 tokenId,
103 | uint256 amount
104 | ) internal view returns (bool) {
105 | bytes32 digest = keccak256(
106 | abi.encodePacked(
107 | "\x19\x01",
108 | _getDomainSeparator(salt),
109 | keccak256(
110 | abi.encode(
111 | keccak256(
112 | "Sale(address owner,uint256 tokenId,uint256 amount)"
113 | ),
114 | owner,
115 | tokenId,
116 | amount
117 | )
118 | )
119 | )
120 | );
121 |
122 | // Validate signature directly
123 | require(digest.recover(sig) == owner, "signature validation failed");
124 | return true;
125 | }
126 |
127 | function _getDomainSeparator(bytes32 salt) internal view returns (bytes32) {
128 | return
129 | keccak256(
130 | abi.encode(
131 | keccak256(
132 | "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
133 | ),
134 | keccak256(bytes(CONTRACT_NAME)),
135 | keccak256(bytes(VERSION)),
136 | block.chainid,
137 | address(this),
138 | salt
139 | )
140 | );
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/contracts/DynamicTraits.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: CC0-1.0
2 | pragma solidity 0.8.22;
3 |
4 | import {IERC7496} from "./IERC7496.sol";
5 |
6 | library DynamicTraitsStorage {
7 | struct Layout {
8 | /// @dev A mapping of token ID to a mapping of trait key to trait value.
9 | mapping(uint256 => mapping(bytes32 => bytes32)) _traits;
10 | /// @dev An offchain string URI that points to a JSON file containing trait metadata.
11 | string _traitMetadataURI;
12 | }
13 |
14 | bytes32 internal constant STORAGE_SLOT =
15 | keccak256("contracts.storage.erc7496-dynamictraits");
16 |
17 | function layout() internal pure returns (Layout storage l) {
18 | bytes32 slot = STORAGE_SLOT;
19 | assembly {
20 | l.slot := slot
21 | }
22 | }
23 | }
24 |
25 | /**
26 | * @title DynamicTraits
27 | *
28 | * @dev Implementation of [ERC-7496](https://eips.ethereum.org/EIPS/eip-7496) Dynamic Traits.
29 | * Uses a storage layout pattern for upgradeable contracts.
30 | *
31 | * Requirements:
32 | * - Overwrite `setTrait` with access role restriction.
33 | * - Expose a function for `setTraitMetadataURI` with access role restriction if desired.
34 | */
35 | contract DynamicTraits is IERC7496 {
36 | using DynamicTraitsStorage for DynamicTraitsStorage.Layout;
37 |
38 | /**
39 | * @notice Get the value of a trait for a given token ID.
40 | * @param tokenId The token ID to get the trait value for
41 | * @param traitKey The trait key to get the value of
42 | */
43 | function getTraitValue(uint256 tokenId, bytes32 traitKey)
44 | public
45 | view
46 | virtual
47 | returns (bytes32 traitValue)
48 | {
49 | // Return the trait value.
50 | return DynamicTraitsStorage.layout()._traits[tokenId][traitKey];
51 | }
52 |
53 | /**
54 | * @notice Get the values of traits for a given token ID.
55 | * @param tokenId The token ID to get the trait values for
56 | * @param traitKeys The trait keys to get the values of
57 | */
58 | function getTraitValues(uint256 tokenId, bytes32[] calldata traitKeys)
59 | public
60 | view
61 | virtual
62 | returns (bytes32[] memory traitValues)
63 | {
64 | // Set the length of the traitValues return array.
65 | uint256 length = traitKeys.length;
66 | traitValues = new bytes32[](length);
67 |
68 | // Assign each trait value to the corresopnding key.
69 | for (uint256 i = 0; i < length; ) {
70 | bytes32 traitKey = traitKeys[i];
71 | traitValues[i] = getTraitValue(tokenId, traitKey);
72 | unchecked {
73 | ++i;
74 | }
75 | }
76 | }
77 |
78 | /**
79 | * @notice Get the URI for the trait metadata
80 | */
81 | function getTraitMetadataURI()
82 | external
83 | view
84 | virtual
85 | returns (string memory labelsURI)
86 | {
87 | // Return the trait metadata URI.
88 | return DynamicTraitsStorage.layout()._traitMetadataURI;
89 | }
90 |
91 | /**
92 | * @notice Set the value of a trait for a given token ID.
93 | * Reverts if the trait value is unchanged.
94 | * @dev IMPORTANT: Override this method with access role restriction.
95 | * @param tokenId The token ID to set the trait value for
96 | * @param traitKey The trait key to set the value of
97 | * @param newValue The new trait value to set
98 | */
99 | function setTrait(
100 | uint256 tokenId,
101 | bytes32 traitKey,
102 | bytes32 newValue
103 | ) internal {
104 | // Revert if the new value is the same as the existing value.
105 | bytes32 existingValue = DynamicTraitsStorage.layout()._traits[tokenId][
106 | traitKey
107 | ];
108 | if (existingValue == newValue) {
109 | revert TraitValueUnchanged();
110 | }
111 |
112 | // Set the new trait value.
113 | _setTrait(tokenId, traitKey, newValue);
114 |
115 | // Emit the event noting the update.
116 | emit TraitUpdated(traitKey, tokenId, newValue);
117 | }
118 |
119 | /**
120 | * @notice Set the trait value (without emitting an event).
121 | * @param tokenId The token ID to set the trait value for
122 | * @param traitKey The trait key to set the value of
123 | * @param newValue The new trait value to set
124 | */
125 | function _setTrait(
126 | uint256 tokenId,
127 | bytes32 traitKey,
128 | bytes32 newValue
129 | ) internal virtual {
130 | // Set the new trait value.
131 | DynamicTraitsStorage.layout()._traits[tokenId][traitKey] = newValue;
132 | }
133 |
134 | /**
135 | * @notice Set the URI for the trait metadata.
136 | * @param uri The new URI to set.
137 | */
138 | function _setTraitMetadataURI(string memory uri) internal virtual {
139 | // Set the new trait metadata URI.
140 | DynamicTraitsStorage.layout()._traitMetadataURI = uri;
141 |
142 | // Emit the event noting the update.
143 | emit TraitMetadataURIUpdated();
144 | }
145 |
146 | /**
147 | * @dev See {IERC165-supportsInterface}.
148 | */
149 | function supportsInterface(bytes4 interfaceId)
150 | public
151 | view
152 | virtual
153 | returns (bool)
154 | {
155 | return interfaceId == type(IERC7496).interfaceId;
156 | }
157 | }
--------------------------------------------------------------------------------
/test/DrGreenNFT.test.js:
--------------------------------------------------------------------------------
1 | const { expect } = require("chai");
2 | const { ethers } = require("hardhat");
3 |
4 | describe("DrGreenNFT", function () {
5 | let DrGreenNFT, drGreenNFT, PlanetsMetadata, planetsMetadata, owner, addr1, addr2;
6 |
7 | beforeEach(async function () {
8 | [owner, addr1, addr2] = await ethers.getSigners();
9 |
10 | // Deploy PlanetsMetadata contract
11 | const PlanetsMetadataFactory = await ethers.getContractFactory("PlanetsMetadata");
12 | planetsMetadata = await PlanetsMetadataFactory.deploy();
13 | await planetsMetadata.deployed();
14 |
15 | // Deploy DrGreenNFT contract
16 | const DrGreenNFTFactory = await ethers.getContractFactory("DrGreenNFT");
17 | drGreenNFT = await DrGreenNFTFactory.deploy(
18 | planetsMetadata.address,
19 | "baseTokenURI",
20 | "traitMetadataURI",
21 | owner.address,
22 | owner.address
23 | );
24 | await drGreenNFT.deployed();
25 | });
26 |
27 | it("Should allow minting standard NFTs during presale", async function () {
28 | const planetNo = 1;
29 | const mintLimit = 2;
30 | const sig = ethers.utils.randomBytes(65); // Mock signature
31 |
32 | // Set presale active
33 | await drGreenNFT.setPresaleActive(true);
34 |
35 | // Mint standard NFT
36 | await expect(drGreenNFT.connect(addr1).standardPreMint(planetNo, mintLimit, sig))
37 | .to.emit(drGreenNFT, "NFTMinted")
38 | .withArgs("StandardWhitelisted", addr1.address, 106, 1, 0, 0);
39 |
40 | // Check NFT ownership
41 | expect(await drGreenNFT.ownerOf(106)).to.equal(addr1.address);
42 | });
43 |
44 | it("Should allow minting standard NFTs during greenlist round", async function () {
45 | const planetNo = 1;
46 | const price = ethers.utils.parseEther("1");
47 | const merkleRoot = ethers.utils.randomBytes(32); // Mock merkle root
48 | const merkleProof = []; // Mock merkle proof
49 |
50 | // Create greenlist round
51 | await drGreenNFT.createRound(
52 | Math.floor(Date.now() / 1000),
53 | Math.floor(Date.now() / 1000) + 3600,
54 | price,
55 | [planetNo],
56 | 100,
57 | 1, // Greenlist round type
58 | merkleRoot
59 | );
60 |
61 | // Mint standard NFT
62 | await expect(drGreenNFT.connect(addr1).standardMint(planetNo, merkleProof, { value: price }))
63 | .to.emit(drGreenNFT, "NFTMinted")
64 | .withArgs("Standard", addr1.address, 107, 2, price, 1);
65 |
66 | // Check NFT ownership
67 | expect(await drGreenNFT.ownerOf(107)).to.equal(addr1.address);
68 | });
69 |
70 | it("Should allow minting gold NFTs by admin", async function () {
71 | const addresses = [addr1.address];
72 | const metadataIds = [1];
73 |
74 | // Mint gold NFT
75 | await expect(drGreenNFT.connect(owner).goldMint(addresses, metadataIds))
76 | .to.emit(drGreenNFT, "NFTMinted")
77 | .withArgs("Gold", addr1.address, 1, 1, 0, 0);
78 |
79 | // Check NFT ownership
80 | expect(await drGreenNFT.ownerOf(1)).to.equal(addr1.address);
81 | });
82 |
83 | it("Should allow minting platinum NFTs by admin", async function () {
84 | const addresses = [addr1.address];
85 | const metadataIds = [56];
86 |
87 | // Mint platinum NFT
88 | await expect(drGreenNFT.connect(owner).platinumMint(addresses, metadataIds))
89 | .to.emit(drGreenNFT, "NFTMinted")
90 | .withArgs("Platinum", addr1.address, 56, 56, 0, 0);
91 |
92 | // Check NFT ownership
93 | expect(await drGreenNFT.ownerOf(56)).to.equal(addr1.address);
94 | });
95 |
96 | it("Should allow setting royalty", async function () {
97 | const receiver = addr1.address;
98 | const feePercent = 900; // 9%
99 |
100 | // Set royalty
101 | await expect(drGreenNFT.connect(owner).setRoyalty(receiver, feePercent))
102 | .to.emit(drGreenNFT, "RoyaltyInfoUpdated")
103 | .withArgs(receiver, feePercent);
104 |
105 | // Check royalty info
106 | const royaltyInfo = await drGreenNFT.royaltyInfo(1, ethers.utils.parseEther("1"));
107 | expect(royaltyInfo[0]).to.equal(receiver);
108 | expect(royaltyInfo[1]).to.equal(ethers.utils.parseEther("0.09"));
109 | });
110 |
111 | it("Should allow setting token royalty", async function () {
112 | const tokenId = 1;
113 | const receiver = addr1.address;
114 | const feePercent = 900; // 9%
115 |
116 | // Set token royalty
117 | await expect(drGreenNFT.connect(owner).setTokenRoyalty(tokenId, receiver, feePercent))
118 | .to.emit(drGreenNFT, "TokenRotaltyUpdated")
119 | .withArgs(tokenId, receiver, feePercent);
120 |
121 | // Check token royalty info
122 | const royaltyInfo = await drGreenNFT.royaltyInfo(tokenId, ethers.utils.parseEther("1"));
123 | expect(royaltyInfo[0]).to.equal(receiver);
124 | expect(royaltyInfo[1]).to.equal(ethers.utils.parseEther("0.09"));
125 | });
126 |
127 | it("Should allow withdrawing funds", async function () {
128 | const price = ethers.utils.parseEther("1");
129 | const merkleRoot = ethers.utils.randomBytes(32); // Mock merkle root
130 | const merkleProof = []; // Mock merkle proof
131 |
132 | // Create greenlist round
133 | await drGreenNFT.createRound(
134 | Math.floor(Date.now() / 1000),
135 | Math.floor(Date.now() / 1000) + 3600,
136 | price,
137 | [1],
138 | 100,
139 | 1, // Greenlist round type
140 | merkleRoot
141 | );
142 |
143 | // Mint standard NFT
144 | await drGreenNFT.connect(addr1).standardMint(1, merkleProof, { value: price });
145 |
146 | // Withdraw funds
147 | await expect(drGreenNFT.connect(owner).withdrawFunds(owner.address))
148 | .to.emit(drGreenNFT, "FundsTransferred")
149 | .withArgs(owner.address, price);
150 |
151 | // Check contract balance
152 | expect(await ethers.provider.getBalance(drGreenNFT.address)).to.equal(0);
153 | });
154 |
155 | it("Should allow adding clients and transactions", async function () {
156 | const tokenId = 106;
157 | const clientsToAdd = 10;
158 | const txsToAdd = 5;
159 | const txsAmtToAdd = ethers.utils.parseEther("0.5");
160 |
161 | // Mint standard NFT
162 | const planetNo = 1;
163 | const mintLimit = 2;
164 | const sig = ethers.utils.randomBytes(65); // Mock signature
165 | await drGreenNFT.setPresaleActive(true);
166 | await drGreenNFT.connect(addr1).standardPreMint(planetNo, mintLimit, sig);
167 |
168 | // Add clients
169 | await drGreenNFT.connect(owner).addClient(tokenId, clientsToAdd);
170 |
171 | // Add transactions
172 | await drGreenNFT.connect(owner).addTransaction(tokenId, txsToAdd, txsAmtToAdd, false);
173 |
174 | // Check dynamic traits
175 | const clientCount = await drGreenNFT.getTraitValue(tokenId, "clientCount");
176 | const txCount = await drGreenNFT.getTraitValue(tokenId, "txCount");
177 | const txVolume = await drGreenNFT.getTraitValue(tokenId, "txVolume");
178 |
179 | expect(clientCount).to.equal(clientsToAdd.toString());
180 | expect(txCount).to.equal(txsToAdd.toString());
181 | expect(txVolume).to.equal(txsAmtToAdd.toString());
182 | });
183 |
184 | it("Should allow updating token URI", async function () {
185 | const tokenId = 106;
186 | const newMetadataUri = "newMetadataUri";
187 | const sig = ethers.utils.randomBytes(65); // Mock signature
188 |
189 | // Mint standard NFT
190 | const planetNo = 1;
191 | const mintLimit = 2;
192 | const mintSig = ethers.utils.randomBytes(65); // Mock signature
193 | await drGreenNFT.setPresaleActive(true);
194 | await drGreenNFT.connect(addr1).standardPreMint(planetNo, mintLimit, mintSig);
195 |
196 | // Update token URI
197 | await expect(drGreenNFT.connect(addr1).updateTokenURI(tokenId, newMetadataUri, sig))
198 | .to.emit(drGreenNFT, "UpdateTokenURI")
199 | .withArgs(tokenId, newMetadataUri, Math.floor(Date.now() / 1000));
200 |
201 | // Check token URI
202 | expect(await drGreenNFT.tokenURI(tokenId)).to.equal(newMetadataUri);
203 | });
204 | });
--------------------------------------------------------------------------------
/contracts/DrGreenNFT.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.22;
3 |
4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
5 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Pausable.sol";
6 | import "@openzeppelin/contracts/access/AccessControl.sol";
7 | import "@openzeppelin/contracts/utils/Strings.sol";
8 | import "@openzeppelin/contracts/token/common/ERC2981.sol";
9 | import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
10 | import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
11 | import "./PlanetsMetadataInterface.sol";
12 | import {DynamicTraits} from "./DynamicTraits.sol";
13 | import {IERC7496} from "./IERC7496.sol";
14 |
15 | contract DrGreenNFT is
16 | ERC721,
17 | ERC2981,
18 | AccessControl,
19 | ERC721Pausable,
20 | DynamicTraits
21 | {
22 | PlanetsMetadataInterface _planetsMetadata;
23 |
24 | event NFTMinted(
25 | string mintType,
26 | address indexed to,
27 | uint256 tokenId,
28 | uint16 metadataId,
29 | uint256 price,
30 | uint8 roundId
31 | );
32 | event NFTTransferred(
33 | address indexed from,
34 | address indexed to,
35 | uint256 tokenId
36 | );
37 | event FundsTransferred(address indexed to, uint256 amount);
38 | event UpdateTokenURI(uint16 indexed tokenId, string newUri, uint256 time);
39 | event RoundCreated(uint8 indexed roundId, Round round);
40 | event RoundUpdated(uint8 indexed roundId, Round round);
41 | event InventoryContractAddressUpdated(
42 | address indexed inventoryContractAddr
43 | );
44 | event RoyaltyInfoUpdated(address indexed receiver, uint96 feePercent);
45 | event TokenRotaltyUpdated(
46 | uint256 indexed tokenId,
47 | address indexed receiver,
48 | uint96 feePercent
49 | );
50 | event BaseTokenURIUpdated(string baseTokenUri);
51 |
52 | bytes32 public constant WHITELIST_SIGNER_ROLE =
53 | keccak256("WHITELIST_SIGNER_ROLE");
54 | bytes32 public constant NFT_UPDATE_SIGNER_ROLE =
55 | keccak256("NFT_UPDATE_SIGNER_ROLE");
56 |
57 | using Strings for uint256;
58 | using ECDSA for bytes32;
59 |
60 | struct NftMinted {
61 | uint8 goldMinted;
62 | uint8 platinumMinted;
63 | uint8 standardMinted;
64 | }
65 |
66 | enum RoundType {
67 | Presale,
68 | Greenlist,
69 | Public
70 | }
71 |
72 | struct Round {
73 | uint256 startTime;
74 | uint256 endTime;
75 | uint256 price;
76 | uint8[] planets;
77 | uint16 supply;
78 | RoundType roundType;
79 | bytes32 merkleRoot;
80 | bool isPaused;
81 | }
82 |
83 | uint8 private _goldCurrIndex = 1;
84 | uint8 private _platinumCurrIndex = 56;
85 | uint16 private _standardCurrIndex = 106;
86 | uint8 private _currentRoundId = 0;
87 | string public _baseTokenURI;
88 | bool public _isPresaleActive = false;
89 | address public _inventoryContractAddr;
90 |
91 | //mappings
92 | mapping(address => NftMinted) private _numberMinted;
93 | mapping(uint256 => string) private _tokenURIs;
94 | mapping(uint256 => Round) private _rounds;
95 | mapping(uint8 => bool) private _planetEnabled;
96 | mapping(bytes => bool) private _usedSignatures;
97 | mapping(uint16 => bool) private _usedMetadataIds;
98 |
99 | // constants
100 | uint8 private constant _platinumStartIndex = 56;
101 | uint16 private constant _standardStartIndex = 106;
102 | uint16 private constant MAX_SUPPLY = 5145;
103 | uint8 private constant GOLD_MAX_SUPPLY = 55;
104 | uint8 private constant PLATINUM_MAX_SUPPLY = 50;
105 | uint16 private constant NFTS_PER_PLANET = 252;
106 | bytes32 private constant CLIENT_COUNT_KEY =
107 | 0x636c69656e74436f756e74000000000000000000000000000000000000000000;
108 | bytes32 private constant TX_COUNT_KEY =
109 | 0x7478436f756e7400000000000000000000000000000000000000000000000000;
110 | bytes32 private constant TX_VOLUME_KEY =
111 | 0x7478566f6c756d65000000000000000000000000000000000000000000000000;
112 |
113 | constructor(
114 | address planetsMetadataAddr,
115 | string memory baseTokenURI,
116 | string memory traitMetadataURI,
117 | address ownerAddress,
118 | address royaltyReceiver
119 | ) ERC721("Dr Green Digital Key", "DRGDK") {
120 | require(
121 | planetsMetadataAddr != address(0),
122 | "metadata addr cant be empty"
123 | );
124 | require(bytes(baseTokenURI).length > 0, "base URI cant be empty");
125 | require(bytes(traitMetadataURI).length > 0, "trait URI cant be empty");
126 | require(ownerAddress != address(0), "ownerAddress cant be empty");
127 | require(royaltyReceiver != address(0), "royaltyReceiver cant be empty");
128 | _planetsMetadata = PlanetsMetadataInterface(planetsMetadataAddr);
129 | _baseTokenURI = baseTokenURI;
130 | _setTraitMetadataURI(traitMetadataURI);
131 | _setDefaultRoyalty(royaltyReceiver, 900);
132 | _grantRole(DEFAULT_ADMIN_ROLE, ownerAddress);
133 | _grantRole(WHITELIST_SIGNER_ROLE, ownerAddress);
134 | _grantRole(NFT_UPDATE_SIGNER_ROLE, ownerAddress);
135 | }
136 |
137 | // This is the modifier for the whitelisted user
138 | modifier isWhitelistedUser(
139 | string memory mintType,
140 | uint8 limit,
141 | bytes memory sig
142 | ) {
143 | require(_standardCurrIndex <= MAX_SUPPLY, "Max supply reached");
144 | require(
145 | _isWhitelisted(mintType, limit, sig),
146 | "Signature failed or user not whitelisted"
147 | );
148 | _;
149 | }
150 |
151 | modifier onlyOwner() {
152 | require(
153 | hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
154 | "Caller is not a owner"
155 | );
156 | _;
157 | }
158 |
159 | // set modifier to call the function by order management contract only
160 | modifier onlyInventoryContract() {
161 | require(
162 | msg.sender == _inventoryContractAddr,
163 | "only inventory contract can call this function."
164 | );
165 | _;
166 | }
167 |
168 | // function to create round by admin
169 | function createRound(
170 | uint256 startTime,
171 | uint256 endTime,
172 | uint256 price,
173 | uint8[] calldata planets,
174 | uint16 supply,
175 | RoundType roundType,
176 | bytes32 merkleRoot
177 | ) external onlyOwner {
178 | require(
179 | block.timestamp < startTime,
180 | "Start date must be later than the current time"
181 | );
182 | // if there is any round already created we need to make sure we can only create next round once current is finished
183 | if (_currentRoundId > 0) {
184 | Round storage round = _rounds[_currentRoundId];
185 | require(
186 | block.timestamp > round.endTime,
187 | "Current round is not yet ended."
188 | );
189 | }
190 | _currentRoundId++;
191 | _manageRound(
192 | _currentRoundId,
193 | startTime,
194 | endTime,
195 | price,
196 | planets,
197 | supply,
198 | roundType,
199 | merkleRoot,
200 | false
201 | );
202 | emit RoundCreated(_currentRoundId, _rounds[_currentRoundId]);
203 | }
204 |
205 | // Function to update round by admin
206 | function updateRound(
207 | uint8 roundId,
208 | uint256 startTime,
209 | uint256 endTime,
210 | uint256 price,
211 | uint8[] calldata planets,
212 | uint16 supply,
213 | RoundType roundType,
214 | bytes32 merkleRoot,
215 | bool isPaused
216 | ) external onlyOwner {
217 | require(roundId > 0 && roundId == _currentRoundId, "Invalid round ID");
218 | _manageRound(
219 | roundId,
220 | startTime,
221 | endTime,
222 | price,
223 | planets,
224 | supply,
225 | roundType,
226 | merkleRoot,
227 | isPaused
228 | );
229 | emit RoundUpdated(roundId, _rounds[roundId]);
230 | }
231 |
232 | // function to set inventory smart contract address by admin
233 | function setInventoryContractAddress(
234 | address inventoryContractAddress
235 | ) external onlyOwner {
236 | require(
237 | inventoryContractAddress != address(0),
238 | "cannot set zero address"
239 | );
240 | _inventoryContractAddr = inventoryContractAddress;
241 | emit InventoryContractAddressUpdated(inventoryContractAddress);
242 | }
243 |
244 | // This function is used to mint standard NFTs when whitelisted round is open and address is whitelisted
245 | /*
246 | * @dev
247 | * Requirements:
248 | * `planetNo` should be in uin8. Example: 2.
249 | * `mintLimit` should be in uin8. Example: 2.
250 | * `sig` should be in bytes. Example: 0x4418f..........d43e6b174571b.
251 | */
252 | function standardPreMint(
253 | uint8 planetNo,
254 | uint8 mintLimit,
255 | bytes memory sig
256 | ) external isWhitelistedUser("Standard", mintLimit, sig) {
257 | require(_isPresaleActive, "Presale is not active");
258 | require(_planetEnabled[planetNo], "Planet not enabled for this round");
259 | require(
260 | _numberMinted[msg.sender].standardMinted < mintLimit,
261 | "mint limit reached"
262 | );
263 | _numberMinted[msg.sender].standardMinted += 1;
264 | uint256 tokenId = _standardCurrIndex++;
265 | uint16 metadataId = _planetsMetadata.setRandomTokenMetadata(
266 | planetNo,
267 | tokenId
268 | );
269 | _safeMint(msg.sender, tokenId);
270 | emit NFTMinted(
271 | "StandardWhitelisted",
272 | msg.sender,
273 | tokenId,
274 | metadataId,
275 | 0,
276 | 0
277 | );
278 | }
279 |
280 | // @dev This function is used to mint standard NFTs
281 | /*
282 | * @dev
283 | * Requirements:
284 | * `roundId` should be in uin8.
285 | * `planetNo` should be in uin8.
286 | * `merkleProof` merkleProof.
287 | */
288 | function standardMint(
289 | uint8 planetNo,
290 | bytes32[] calldata merkleProof
291 | ) external payable {
292 | require(_standardCurrIndex <= MAX_SUPPLY, "Max supply reached");
293 | Round storage round = _rounds[_currentRoundId];
294 | require(
295 | round.roundType == RoundType.Greenlist ||
296 | round.roundType == RoundType.Public,
297 | "Only for greenlist and public mint"
298 | );
299 | require(round.startTime != 0, "No active round");
300 | require(
301 | block.timestamp >= round.startTime &&
302 | block.timestamp <= round.endTime &&
303 | !round.isPaused,
304 | "Minting is not active or paused"
305 | );
306 | require(_planetEnabled[planetNo], "Planet not enabled for this round");
307 | require(msg.value == round.price, "Incorrect payment amount");
308 | if (round.roundType == RoundType.Greenlist) {
309 | require(
310 | merkleProof.length > 0,
311 | "Merkle proof is required for Greenlist"
312 | );
313 | require(
314 | MerkleProof.verify(
315 | merkleProof,
316 | round.merkleRoot,
317 | keccak256(
318 | abi.encodePacked(
319 | msg.sender,
320 | address(this),
321 | block.chainid
322 | )
323 | )
324 | ),
325 | "You are not Greenlisted for this round"
326 | );
327 | }
328 | uint256 tokenId = _standardCurrIndex++;
329 | uint16 metadataId = _planetsMetadata.setRandomTokenMetadata(
330 | planetNo,
331 | tokenId
332 | );
333 | _safeMint(msg.sender, tokenId);
334 | emit NFTMinted(
335 | "Standard",
336 | msg.sender,
337 | tokenId,
338 | metadataId,
339 | msg.value,
340 | _currentRoundId
341 | );
342 | }
343 |
344 | /*
345 | * @dev
346 | * Requirements:
347 | * `addresses` Array of addresses to mint gold Nfts.
348 | * `metedataIds` Array of metedataIds which needs to be associated with NFT.
349 | */
350 | function goldMint(
351 | address[] calldata addresses,
352 | uint16[] calldata metedataIds
353 | ) external onlyOwner {
354 | uint8 totalGoldMinted = _goldCurrIndex - 1;
355 | require(totalGoldMinted < GOLD_MAX_SUPPLY, "all gold NFTs are minted.");
356 | require(
357 | addresses.length == metedataIds.length && addresses.length > 0,
358 | "Addresses and metedataIds must be the same length and non-empty"
359 | );
360 | uint256 totalToMint = addresses.length;
361 | require(
362 | totalGoldMinted + totalToMint <= GOLD_MAX_SUPPLY,
363 | "Total amount exceeds the gold NFT supply limit."
364 | );
365 | for (uint8 index = 0; index < totalToMint; index++) {
366 | require(
367 | addresses[index] != address(0),
368 | "address can not be empty."
369 | );
370 | uint16 metadataId = metedataIds[index];
371 | require(
372 | !_usedMetadataIds[metadataId],
373 | "MetadataId is already associated with NFT."
374 | );
375 | // Allowed metadataIds are between range 1 to 55
376 | require(
377 | metadataId > 0 && metadataId <= GOLD_MAX_SUPPLY,
378 | "metadataId must be between 1 to 55."
379 | );
380 | }
381 | for (uint8 i = 0; i < totalToMint; i++) {
382 | address addr = addresses[i];
383 | uint16 metadataId = metedataIds[i];
384 | _numberMinted[addr].goldMinted += 1;
385 | uint256 tokenId = _goldCurrIndex++;
386 | _planetsMetadata.setTokenMetadataId(tokenId, metadataId);
387 | _safeMint(addr, tokenId);
388 | _usedMetadataIds[metadataId] = true;
389 | emit NFTMinted("Gold", addr, tokenId, metadataId, 0, 0);
390 | }
391 | }
392 |
393 | // This function is used to mint platinum NFTs by admin
394 | /*
395 | * @dev
396 | * Requirements:
397 | * `addresses` Array of addresses to mint platinum Nfts.
398 | * `metedataIds` Array of metedataIds which needs to be associated with NFT.
399 | */
400 | function platinumMint(
401 | address[] calldata addresses,
402 | uint16[] calldata metedataIds
403 | ) external onlyOwner {
404 | uint8 totalPlatinumMinted = _platinumCurrIndex - _platinumStartIndex;
405 | require(
406 | totalPlatinumMinted < PLATINUM_MAX_SUPPLY,
407 | "all platinum NFTs are minted."
408 | );
409 | require(
410 | addresses.length == metedataIds.length && addresses.length > 0,
411 | "Addresses and metedataIds must be the same length and non-empty"
412 | );
413 | uint256 totalToMint = addresses.length;
414 | require(
415 | totalPlatinumMinted + totalToMint <= PLATINUM_MAX_SUPPLY,
416 | "Total amount exceeds the platinum NFT supply limit."
417 | );
418 | for (uint8 index = 0; index < totalToMint; index++) {
419 | require(
420 | addresses[index] != address(0),
421 | "address can not be empty."
422 | );
423 | uint16 metadataId = metedataIds[index];
424 | require(
425 | !_usedMetadataIds[metadataId],
426 | "MetadataId is already associated with NFT."
427 | );
428 | // Allowed metadataIds are between range 56 to 105
429 | require(
430 | metadataId >= _platinumStartIndex &&
431 | metadataId < _platinumStartIndex + PLATINUM_MAX_SUPPLY,
432 | "metadataId must be between 56 to 105."
433 | );
434 | }
435 | for (uint8 i = 0; i < totalToMint; i++) {
436 | address addr = addresses[i];
437 | uint16 metadataId = metedataIds[i];
438 | _numberMinted[addr].platinumMinted += 1;
439 | uint256 tokenId = _platinumCurrIndex++;
440 | _planetsMetadata.setTokenMetadataId(tokenId, metadataId);
441 | _safeMint(addr, tokenId);
442 | _usedMetadataIds[metadataId] = true;
443 | emit NFTMinted("Platinum", addr, tokenId, metadataId, 0, 0);
444 | }
445 | }
446 |
447 | // Function to set royalty which is paid to the given address
448 | /*
449 | * @dev
450 | * Requirements:
451 | * - `receiver` cannot be the zero address.
452 | * - the caller must have a balance of at least `amount`.
453 | * - `feePercent` should be given as multiplied by 100. Example: 9.5% should be as 950.
454 | */
455 | function setRoyalty(
456 | address receiver,
457 | uint96 feePercent
458 | ) external onlyOwner {
459 | if (receiver == address(0)) {
460 | receiver = msg.sender;
461 | }
462 | _setDefaultRoyalty(receiver, feePercent);
463 | emit RoyaltyInfoUpdated(receiver, feePercent);
464 | }
465 |
466 | // Function to set royalty for the specific tokenID
467 | /*
468 | * @dev
469 | * Requirements:
470 | * - `receiver` cannot be the zero address.
471 | * - the caller must have a balance of at least `amount`.
472 | * - `feePercent` should be given as multiplied by 100. Example: 9.5% should be as 950.
473 | */
474 | function setTokenRoyalty(
475 | uint256 tokenId,
476 | address receiver,
477 | uint96 feePercent
478 | ) external onlyOwner {
479 | if (receiver == address(0)) {
480 | receiver = msg.sender;
481 | }
482 | _setTokenRoyalty(tokenId, receiver, feePercent);
483 | emit TokenRotaltyUpdated(tokenId, receiver, feePercent);
484 | }
485 |
486 | // function to withdraw ethers to admin account or other acocunt
487 | function withdrawFunds(address to) external onlyOwner {
488 | require(to != address(0), "address can not be empty.");
489 | uint256 balance = address(this).balance;
490 | require(balance > 0, "No funds available for withdrawal.");
491 | emit FundsTransferred(to, balance); // Emit event before transfer for accurate logging
492 | (bool success, ) = to.call{value: balance}("");
493 | require(success, "Withdrawal failed");
494 | }
495 |
496 | // Function to add client with NFT tokenID
497 | /*
498 | * @dev
499 | * Requirements:
500 | * - `tokenId` NFT tokenId for which clients to add.
501 | * - `clientsToAdd` number of clients to add
502 | */
503 | function addClient(
504 | uint16 tokenId,
505 | uint256 clientsToAdd
506 | ) external onlyInventoryContract {
507 | _requireOwned(tokenId);
508 | require(clientsToAdd > 0, "clientsToAdd must be greater than 0");
509 |
510 | // Retrieve and parse current client count
511 | uint256 clientCount = _stringToUint256(
512 | string(
513 | abi.encodePacked(
514 | DynamicTraits.getTraitValue(tokenId, CLIENT_COUNT_KEY)
515 | )
516 | )
517 | );
518 |
519 | // Add new clients and convert directly to bytes32
520 | clientCount += clientsToAdd;
521 |
522 | bytes32 clientCountBytes = bytes32(bytes(clientCount.toString()));
523 |
524 | // Call the internal function to set the trait.
525 | DynamicTraits.setTrait(tokenId, CLIENT_COUNT_KEY, clientCountBytes);
526 | }
527 |
528 | // Function to add transaction count with NFT tokenID
529 | /*
530 | * @dev
531 | * Requirements:
532 | * - `tokenId` NFT tokenId for which clients to add.
533 | * - `txsToAdd` number of txs to add
534 | * - `txsAmtToAdd` total amount for txs
535 | * - `isRefunded` is this refund?
536 | */
537 | function addTransaction(
538 | uint16 tokenId,
539 | uint256 txsToAdd,
540 | uint256 txsAmtToAdd,
541 | bool isRefunded
542 | ) external onlyInventoryContract {
543 | _requireOwned(tokenId);
544 | require(
545 | txsToAdd > 0 && txsAmtToAdd > 0,
546 | "txsToAdd & txsAmtToAdd must be greater than 0"
547 | );
548 | // Get current transaction count and volume
549 | uint256 txCount = _stringToUint256(
550 | string(
551 | abi.encodePacked(
552 | DynamicTraits.getTraitValue(tokenId, TX_COUNT_KEY)
553 | )
554 | )
555 | );
556 | uint256 txVolume = _stringToUint256(
557 | string(
558 | abi.encodePacked(
559 | DynamicTraits.getTraitValue(tokenId, TX_VOLUME_KEY)
560 | )
561 | )
562 | );
563 |
564 | // Calculate new values based on isRefunded
565 | txCount = isRefunded ? txCount - txsToAdd : txCount + txsToAdd;
566 | txVolume = isRefunded ? txVolume - txsAmtToAdd : txVolume + txsAmtToAdd;
567 |
568 | bytes32 txCountBytes = bytes32(bytes(txCount.toString()));
569 | bytes32 txVolumeBytes = bytes32(bytes(txVolume.toString()));
570 |
571 | // Set the updated values
572 | DynamicTraits.setTrait(tokenId, TX_COUNT_KEY, txCountBytes);
573 | DynamicTraits.setTrait(tokenId, TX_VOLUME_KEY, txVolumeBytes);
574 | }
575 |
576 | function setTraitMetadataURI(string calldata uri) external onlyOwner {
577 | // Set the new metadata URI.
578 | _setTraitMetadataURI(uri);
579 | }
580 |
581 | function pauseMinting() public onlyOwner {
582 | _pause();
583 | }
584 |
585 | function unpauseMinting() public onlyOwner {
586 | _unpause();
587 | }
588 |
589 | function setBaseTokenURI(string memory baseTokenURI) external onlyOwner {
590 | require(
591 | bytes(baseTokenURI).length > 0,
592 | "Base token URI cannot be empty"
593 | );
594 | _baseTokenURI = baseTokenURI;
595 | emit BaseTokenURIUpdated(baseTokenURI);
596 | }
597 |
598 | // Override the grantRole function
599 | function grantRole(
600 | bytes32 role,
601 | address account
602 | ) public virtual override(AccessControl) {
603 | // Check if the role is one of the predefined roles
604 | require(
605 | role == WHITELIST_SIGNER_ROLE || role == NFT_UPDATE_SIGNER_ROLE,
606 | "Invalid role"
607 | );
608 |
609 | // Check if the account is not the zero address
610 | require(account != address(0), "Account cant be the zero address");
611 | require(
612 | hasRole(DEFAULT_ADMIN_ROLE, msg.sender),
613 | "you are not authorized."
614 | );
615 | _grantRole(role, account);
616 | }
617 |
618 | function updateTokenURI(
619 | uint16 tokenId,
620 | string calldata newMetadataUri,
621 | bytes calldata sig
622 | ) external {
623 | require(!_usedSignatures[sig], "Signature already used");
624 | require(
625 | _requireOwned(tokenId) == msg.sender,
626 | "you are not owner of the token."
627 | );
628 | require(bytes(newMetadataUri).length > 0, "Token URI cannot be empty");
629 | bytes32 digest = keccak256(
630 | abi.encodePacked(
631 | tokenId,
632 | newMetadataUri,
633 | address(this),
634 | block.chainid
635 | )
636 | );
637 | require(
638 | hasRole(NFT_UPDATE_SIGNER_ROLE, digest.recover(sig)),
639 | "signature validation failed"
640 | );
641 | _tokenURIs[tokenId] = newMetadataUri;
642 | _usedSignatures[sig] = true;
643 | emit UpdateTokenURI(tokenId, newMetadataUri, block.timestamp);
644 | }
645 |
646 | function tokenURI(
647 | uint256 tokenId
648 | ) public view override(ERC721) returns (string memory) {
649 | _requireOwned(tokenId);
650 | if (bytes(_tokenURIs[tokenId]).length > 0) {
651 | return _tokenURIs[tokenId];
652 | }
653 | return
654 | string(
655 | abi.encodePacked(
656 | _baseURI(),
657 | _planetsMetadata.getMetadataIdByToken(tokenId).toString(),
658 | ".json"
659 | )
660 | );
661 | }
662 |
663 | function supportsInterface(
664 | bytes4 interfaceId
665 | )
666 | public
667 | view
668 | override(ERC721, DynamicTraits, ERC2981, AccessControl)
669 | returns (bool)
670 | {
671 | return
672 | interfaceId == type(IERC721).interfaceId || // ERC-721 interface
673 | interfaceId == type(IERC721Metadata).interfaceId || // ERC-721 Metadata interface
674 | interfaceId == type(IERC7496).interfaceId || // Dynamic Trait
675 | interfaceId == type(IERC2981).interfaceId || // ERC-2981 Royalty interface
676 | interfaceId == type(IAccessControl).interfaceId || // AccessControl interface
677 | super.supportsInterface(interfaceId);
678 | }
679 |
680 | function maxSupply()
681 | external
682 | view
683 | virtual
684 | returns (uint8 gold, uint8 platinum, uint16 total)
685 | {
686 | return (GOLD_MAX_SUPPLY, PLATINUM_MAX_SUPPLY, MAX_SUPPLY);
687 | }
688 |
689 | // total current supply of gold+platinum+standard
690 | function totalSupply() public view returns (uint256) {
691 | return
692 | (_goldCurrIndex - 1) +
693 | (_platinumCurrIndex - _platinumStartIndex) +
694 | (_standardCurrIndex - _standardStartIndex);
695 | }
696 |
697 | function numberMinted(
698 | address walletAddress
699 | ) external view virtual returns (NftMinted memory) {
700 | return _numberMinted[walletAddress];
701 | }
702 |
703 | function totalMinted()
704 | external
705 | view
706 | returns (
707 | uint8 gold,
708 | uint8 platinum,
709 | uint16 standard,
710 | uint256[20] memory planetMinted
711 | )
712 | {
713 | uint256[20] memory planetMintedArray;
714 | for (uint8 i = 1; i <= 20; i++) {
715 | planetMintedArray[i - 1] =
716 | NFTS_PER_PLANET -
717 | _planetsMetadata.getAvailableNFTsbyPlanet(i);
718 | }
719 | return (
720 | _goldCurrIndex - 1,
721 | _platinumCurrIndex - _platinumStartIndex,
722 | _standardCurrIndex - _standardStartIndex,
723 | planetMintedArray
724 | );
725 | }
726 |
727 | // function to get current round
728 | function getCurrentRound() external view returns (Round memory) {
729 | return _rounds[_currentRoundId];
730 | }
731 |
732 | // function to get the minted count by planet
733 | function getMintedbyPlanet(
734 | uint8 planetNo
735 | ) external view virtual returns (uint256) {
736 | return
737 | NFTS_PER_PLANET -
738 | _planetsMetadata.getAvailableNFTsbyPlanet(planetNo);
739 | }
740 |
741 | //Internal Functions
742 |
743 | // The following functions are overrides required by Solidity.
744 | function _update(
745 | address to,
746 | uint256 tokenId,
747 | address auth
748 | ) internal override(ERC721, ERC721Pausable) returns (address) {
749 | return super._update(to, tokenId, auth);
750 | }
751 |
752 | function _baseURI() internal view virtual override returns (string memory) {
753 | return _baseTokenURI;
754 | }
755 |
756 | function _stringToUint256(
757 | string memory s
758 | ) internal pure returns (uint256 result) {
759 | bytes memory b = bytes(s);
760 | result = 0;
761 | for (uint256 i = 0; i < b.length; i++) {
762 | uint256 c = uint256(uint8(b[i]));
763 | if (c >= 48 && c <= 57) {
764 | result = result * 10 + (c - 48);
765 | }
766 | }
767 | return result;
768 | }
769 |
770 | function _isWhitelisted(
771 | string memory mintType,
772 | uint8 limit,
773 | bytes memory sig
774 | ) internal view returns (bool) {
775 | require(msg.sender != address(0), "Caller can't be null address");
776 | require(limit > 0, "Limit must be greater than 0");
777 | require(sig.length == 65, "Invalid signature length");
778 | bytes32 digest = keccak256(
779 | abi.encodePacked(
780 | "\x19Ethereum Signed Message:\n32",
781 | keccak256(
782 | abi.encodePacked(
783 | mintType,
784 | msg.sender,
785 | limit,
786 | address(this),
787 | block.chainid
788 | )
789 | )
790 | )
791 | );
792 | return hasRole(WHITELIST_SIGNER_ROLE, digest.recover(sig));
793 | }
794 |
795 | // Internal function for managing round creation and updates
796 | function _manageRound(
797 | uint8 roundId,
798 | uint256 startTime,
799 | uint256 endTime,
800 | uint256 price,
801 | uint8[] memory planets,
802 | uint16 supply,
803 | RoundType roundType,
804 | bytes32 merkleRoot,
805 | bool isPaused
806 | ) internal {
807 | require(startTime < endTime, "Start date must be before end date");
808 | require(price > 0, "Price must be greater than zero");
809 | require(planets.length > 0, "At least one planet must be specified");
810 | require(supply > 0, "Supply must be greater than zero");
811 |
812 | // Validate merkle root for Greenlist rounds.
813 | if (roundType == RoundType.Greenlist) {
814 | require(
815 | merkleRoot != bytes32(0),
816 | "Merkle root is required for Greenlist round"
817 | );
818 | }
819 |
820 | // Validate planet numbers and update their status.
821 | for (uint8 i = 0; i < planets.length; i++) {
822 | uint8 planetNo = planets[i];
823 | require(
824 | planetNo > 0 && planetNo <= 20,
825 | "Planet must be between 1 to 20."
826 | );
827 | _planetEnabled[planetNo] = true;
828 | }
829 |
830 | // Create or update the round.
831 | _rounds[roundId] = Round(
832 | startTime,
833 | endTime,
834 | price,
835 | planets,
836 | supply,
837 | roundType,
838 | merkleRoot,
839 | isPaused
840 | );
841 |
842 | // Set presale status for Presale rounds.
843 | if (roundType == RoundType.Presale) {
844 | _isPresaleActive = true;
845 | }
846 | }
847 | }
848 |
--------------------------------------------------------------------------------
/contracts/PlanetsMetadata.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity 0.8.22;
3 |
4 | import "@openzeppelin/contracts/access/Ownable.sol";
5 |
6 | contract PlanetsMetadata is Ownable {
7 | event NftAddressUpdated(address indexed nftContractAddress);
8 | event RandomMetadataAssigned(
9 | uint256 indexed tokenId,
10 | uint8 indexed planetNo,
11 | uint16 indexed metadataId
12 | );
13 | event MetadataIdSet(uint256 indexed tokenId, uint16 indexed metadataId);
14 |
15 | mapping(uint8 => uint16[]) private _planets;
16 | mapping(uint256 => uint16) private _metadataIds;
17 |
18 | address _nftAddr;
19 |
20 | constructor() Ownable(msg.sender) {
21 | _planets[1] = [
22 | 106,
23 | 107,
24 | 108,
25 | 109,
26 | 110,
27 | 111,
28 | 112,
29 | 113,
30 | 114,
31 | 115,
32 | 116,
33 | 117,
34 | 118,
35 | 119,
36 | 120,
37 | 121,
38 | 122,
39 | 123,
40 | 124,
41 | 125,
42 | 126,
43 | 127,
44 | 128,
45 | 129,
46 | 130,
47 | 131,
48 | 132,
49 | 133,
50 | 134,
51 | 135,
52 | 136,
53 | 137,
54 | 138,
55 | 139,
56 | 140,
57 | 141,
58 | 142,
59 | 143,
60 | 144,
61 | 145,
62 | 146,
63 | 147,
64 | 148,
65 | 149,
66 | 150,
67 | 151,
68 | 152,
69 | 153,
70 | 154,
71 | 155,
72 | 156,
73 | 157,
74 | 158,
75 | 159,
76 | 160,
77 | 161,
78 | 162,
79 | 163,
80 | 164,
81 | 165,
82 | 166,
83 | 167,
84 | 168,
85 | 169,
86 | 170,
87 | 171,
88 | 172,
89 | 173,
90 | 174,
91 | 175,
92 | 176,
93 | 177,
94 | 178,
95 | 179,
96 | 180,
97 | 181,
98 | 182,
99 | 183,
100 | 184,
101 | 185,
102 | 186,
103 | 187,
104 | 188,
105 | 189,
106 | 190,
107 | 191,
108 | 192,
109 | 193,
110 | 194,
111 | 195,
112 | 196,
113 | 197,
114 | 198,
115 | 199,
116 | 200,
117 | 201,
118 | 202,
119 | 203,
120 | 204,
121 | 205,
122 | 206,
123 | 207,
124 | 208,
125 | 209,
126 | 210,
127 | 211,
128 | 212,
129 | 213,
130 | 214,
131 | 215,
132 | 216,
133 | 217,
134 | 218,
135 | 219,
136 | 220,
137 | 221,
138 | 222,
139 | 223,
140 | 224,
141 | 225,
142 | 226,
143 | 227,
144 | 228,
145 | 229,
146 | 230,
147 | 231,
148 | 232,
149 | 233,
150 | 234,
151 | 235,
152 | 236,
153 | 237,
154 | 238,
155 | 239,
156 | 240,
157 | 241,
158 | 242,
159 | 243,
160 | 244,
161 | 245,
162 | 246,
163 | 247,
164 | 248,
165 | 249,
166 | 250,
167 | 251,
168 | 252,
169 | 253,
170 | 254,
171 | 255,
172 | 256,
173 | 257,
174 | 258,
175 | 259,
176 | 260,
177 | 261,
178 | 262,
179 | 263,
180 | 264,
181 | 265,
182 | 266,
183 | 267,
184 | 268,
185 | 269,
186 | 270,
187 | 271,
188 | 272,
189 | 273,
190 | 274,
191 | 275,
192 | 276,
193 | 277,
194 | 278,
195 | 279,
196 | 280,
197 | 281,
198 | 282,
199 | 283,
200 | 284,
201 | 285,
202 | 286,
203 | 287,
204 | 288,
205 | 289,
206 | 290,
207 | 291,
208 | 292,
209 | 293,
210 | 294,
211 | 295,
212 | 296,
213 | 297,
214 | 298,
215 | 299,
216 | 300,
217 | 301,
218 | 302,
219 | 303,
220 | 304,
221 | 305,
222 | 306,
223 | 307,
224 | 308,
225 | 309,
226 | 310,
227 | 311,
228 | 312,
229 | 313,
230 | 314,
231 | 315,
232 | 316,
233 | 317,
234 | 318,
235 | 319,
236 | 320,
237 | 321,
238 | 322,
239 | 323,
240 | 324,
241 | 325,
242 | 326,
243 | 327,
244 | 328,
245 | 329,
246 | 330,
247 | 331,
248 | 332,
249 | 333,
250 | 334,
251 | 335,
252 | 336,
253 | 337,
254 | 338,
255 | 339,
256 | 340,
257 | 341,
258 | 342,
259 | 343,
260 | 344,
261 | 345,
262 | 346,
263 | 347,
264 | 348,
265 | 349,
266 | 350,
267 | 351,
268 | 352,
269 | 353,
270 | 354,
271 | 355,
272 | 356,
273 | 357
274 | ];
275 | _planets[2] = [
276 | 358,
277 | 359,
278 | 360,
279 | 361,
280 | 362,
281 | 363,
282 | 364,
283 | 365,
284 | 366,
285 | 367,
286 | 368,
287 | 369,
288 | 370,
289 | 371,
290 | 372,
291 | 373,
292 | 374,
293 | 375,
294 | 376,
295 | 377,
296 | 378,
297 | 379,
298 | 380,
299 | 381,
300 | 382,
301 | 383,
302 | 384,
303 | 385,
304 | 386,
305 | 387,
306 | 388,
307 | 389,
308 | 390,
309 | 391,
310 | 392,
311 | 393,
312 | 394,
313 | 395,
314 | 396,
315 | 397,
316 | 398,
317 | 399,
318 | 400,
319 | 401,
320 | 402,
321 | 403,
322 | 404,
323 | 405,
324 | 406,
325 | 407,
326 | 408,
327 | 409,
328 | 410,
329 | 411,
330 | 412,
331 | 413,
332 | 414,
333 | 415,
334 | 416,
335 | 417,
336 | 418,
337 | 419,
338 | 420,
339 | 421,
340 | 422,
341 | 423,
342 | 424,
343 | 425,
344 | 426,
345 | 427,
346 | 428,
347 | 429,
348 | 430,
349 | 431,
350 | 432,
351 | 433,
352 | 434,
353 | 435,
354 | 436,
355 | 437,
356 | 438,
357 | 439,
358 | 440,
359 | 441,
360 | 442,
361 | 443,
362 | 444,
363 | 445,
364 | 446,
365 | 447,
366 | 448,
367 | 449,
368 | 450,
369 | 451,
370 | 452,
371 | 453,
372 | 454,
373 | 455,
374 | 456,
375 | 457,
376 | 458,
377 | 459,
378 | 460,
379 | 461,
380 | 462,
381 | 463,
382 | 464,
383 | 465,
384 | 466,
385 | 467,
386 | 468,
387 | 469,
388 | 470,
389 | 471,
390 | 472,
391 | 473,
392 | 474,
393 | 475,
394 | 476,
395 | 477,
396 | 478,
397 | 479,
398 | 480,
399 | 481,
400 | 482,
401 | 483,
402 | 484,
403 | 485,
404 | 486,
405 | 487,
406 | 488,
407 | 489,
408 | 490,
409 | 491,
410 | 492,
411 | 493,
412 | 494,
413 | 495,
414 | 496,
415 | 497,
416 | 498,
417 | 499,
418 | 500,
419 | 501,
420 | 502,
421 | 503,
422 | 504,
423 | 505,
424 | 506,
425 | 507,
426 | 508,
427 | 509,
428 | 510,
429 | 511,
430 | 512,
431 | 513,
432 | 514,
433 | 515,
434 | 516,
435 | 517,
436 | 518,
437 | 519,
438 | 520,
439 | 521,
440 | 522,
441 | 523,
442 | 524,
443 | 525,
444 | 526,
445 | 527,
446 | 528,
447 | 529,
448 | 530,
449 | 531,
450 | 532,
451 | 533,
452 | 534,
453 | 535,
454 | 536,
455 | 537,
456 | 538,
457 | 539,
458 | 540,
459 | 541,
460 | 542,
461 | 543,
462 | 544,
463 | 545,
464 | 546,
465 | 547,
466 | 548,
467 | 549,
468 | 550,
469 | 551,
470 | 552,
471 | 553,
472 | 554,
473 | 555,
474 | 556,
475 | 557,
476 | 558,
477 | 559,
478 | 560,
479 | 561,
480 | 562,
481 | 563,
482 | 564,
483 | 565,
484 | 566,
485 | 567,
486 | 568,
487 | 569,
488 | 570,
489 | 571,
490 | 572,
491 | 573,
492 | 574,
493 | 575,
494 | 576,
495 | 577,
496 | 578,
497 | 579,
498 | 580,
499 | 581,
500 | 582,
501 | 583,
502 | 584,
503 | 585,
504 | 586,
505 | 587,
506 | 588,
507 | 589,
508 | 590,
509 | 591,
510 | 592,
511 | 593,
512 | 594,
513 | 595,
514 | 596,
515 | 597,
516 | 598,
517 | 599,
518 | 600,
519 | 601,
520 | 602,
521 | 603,
522 | 604,
523 | 605,
524 | 606,
525 | 607,
526 | 608,
527 | 609
528 | ];
529 | _planets[3] = [
530 | 610,
531 | 611,
532 | 612,
533 | 613,
534 | 614,
535 | 615,
536 | 616,
537 | 617,
538 | 618,
539 | 619,
540 | 620,
541 | 621,
542 | 622,
543 | 623,
544 | 624,
545 | 625,
546 | 626,
547 | 627,
548 | 628,
549 | 629,
550 | 630,
551 | 631,
552 | 632,
553 | 633,
554 | 634,
555 | 635,
556 | 636,
557 | 637,
558 | 638,
559 | 639,
560 | 640,
561 | 641,
562 | 642,
563 | 643,
564 | 644,
565 | 645,
566 | 646,
567 | 647,
568 | 648,
569 | 649,
570 | 650,
571 | 651,
572 | 652,
573 | 653,
574 | 654,
575 | 655,
576 | 656,
577 | 657,
578 | 658,
579 | 659,
580 | 660,
581 | 661,
582 | 662,
583 | 663,
584 | 664,
585 | 665,
586 | 666,
587 | 667,
588 | 668,
589 | 669,
590 | 670,
591 | 671,
592 | 672,
593 | 673,
594 | 674,
595 | 675,
596 | 676,
597 | 677,
598 | 678,
599 | 679,
600 | 680,
601 | 681,
602 | 682,
603 | 683,
604 | 684,
605 | 685,
606 | 686,
607 | 687,
608 | 688,
609 | 689,
610 | 690,
611 | 691,
612 | 692,
613 | 693,
614 | 694,
615 | 695,
616 | 696,
617 | 697,
618 | 698,
619 | 699,
620 | 700,
621 | 701,
622 | 702,
623 | 703,
624 | 704,
625 | 705,
626 | 706,
627 | 707,
628 | 708,
629 | 709,
630 | 710,
631 | 711,
632 | 712,
633 | 713,
634 | 714,
635 | 715,
636 | 716,
637 | 717,
638 | 718,
639 | 719,
640 | 720,
641 | 721,
642 | 722,
643 | 723,
644 | 724,
645 | 725,
646 | 726,
647 | 727,
648 | 728,
649 | 729,
650 | 730,
651 | 731,
652 | 732,
653 | 733,
654 | 734,
655 | 735,
656 | 736,
657 | 737,
658 | 738,
659 | 739,
660 | 740,
661 | 741,
662 | 742,
663 | 743,
664 | 744,
665 | 745,
666 | 746,
667 | 747,
668 | 748,
669 | 749,
670 | 750,
671 | 751,
672 | 752,
673 | 753,
674 | 754,
675 | 755,
676 | 756,
677 | 757,
678 | 758,
679 | 759,
680 | 760,
681 | 761,
682 | 762,
683 | 763,
684 | 764,
685 | 765,
686 | 766,
687 | 767,
688 | 768,
689 | 769,
690 | 770,
691 | 771,
692 | 772,
693 | 773,
694 | 774,
695 | 775,
696 | 776,
697 | 777,
698 | 778,
699 | 779,
700 | 780,
701 | 781,
702 | 782,
703 | 783,
704 | 784,
705 | 785,
706 | 786,
707 | 787,
708 | 788,
709 | 789,
710 | 790,
711 | 791,
712 | 792,
713 | 793,
714 | 794,
715 | 795,
716 | 796,
717 | 797,
718 | 798,
719 | 799,
720 | 800,
721 | 801,
722 | 802,
723 | 803,
724 | 804,
725 | 805,
726 | 806,
727 | 807,
728 | 808,
729 | 809,
730 | 810,
731 | 811,
732 | 812,
733 | 813,
734 | 814,
735 | 815,
736 | 816,
737 | 817,
738 | 818,
739 | 819,
740 | 820,
741 | 821,
742 | 822,
743 | 823,
744 | 824,
745 | 825,
746 | 826,
747 | 827,
748 | 828,
749 | 829,
750 | 830,
751 | 831,
752 | 832,
753 | 833,
754 | 834,
755 | 835,
756 | 836,
757 | 837,
758 | 838,
759 | 839,
760 | 840,
761 | 841,
762 | 842,
763 | 843,
764 | 844,
765 | 845,
766 | 846,
767 | 847,
768 | 848,
769 | 849,
770 | 850,
771 | 851,
772 | 852,
773 | 853,
774 | 854,
775 | 855,
776 | 856,
777 | 857,
778 | 858,
779 | 859,
780 | 860,
781 | 861
782 | ];
783 | _planets[4] = [
784 | 862,
785 | 863,
786 | 864,
787 | 865,
788 | 866,
789 | 867,
790 | 868,
791 | 869,
792 | 870,
793 | 871,
794 | 872,
795 | 873,
796 | 874,
797 | 875,
798 | 876,
799 | 877,
800 | 878,
801 | 879,
802 | 880,
803 | 881,
804 | 882,
805 | 883,
806 | 884,
807 | 885,
808 | 886,
809 | 887,
810 | 888,
811 | 889,
812 | 890,
813 | 891,
814 | 892,
815 | 893,
816 | 894,
817 | 895,
818 | 896,
819 | 897,
820 | 898,
821 | 899,
822 | 900,
823 | 901,
824 | 902,
825 | 903,
826 | 904,
827 | 905,
828 | 906,
829 | 907,
830 | 908,
831 | 909,
832 | 910,
833 | 911,
834 | 912,
835 | 913,
836 | 914,
837 | 915,
838 | 916,
839 | 917,
840 | 918,
841 | 919,
842 | 920,
843 | 921,
844 | 922,
845 | 923,
846 | 924,
847 | 925,
848 | 926,
849 | 927,
850 | 928,
851 | 929,
852 | 930,
853 | 931,
854 | 932,
855 | 933,
856 | 934,
857 | 935,
858 | 936,
859 | 937,
860 | 938,
861 | 939,
862 | 940,
863 | 941,
864 | 942,
865 | 943,
866 | 944,
867 | 945,
868 | 946,
869 | 947,
870 | 948,
871 | 949,
872 | 950,
873 | 951,
874 | 952,
875 | 953,
876 | 954,
877 | 955,
878 | 956,
879 | 957,
880 | 958,
881 | 959,
882 | 960,
883 | 961,
884 | 962,
885 | 963,
886 | 964,
887 | 965,
888 | 966,
889 | 967,
890 | 968,
891 | 969,
892 | 970,
893 | 971,
894 | 972,
895 | 973,
896 | 974,
897 | 975,
898 | 976,
899 | 977,
900 | 978,
901 | 979,
902 | 980,
903 | 981,
904 | 982,
905 | 983,
906 | 984,
907 | 985,
908 | 986,
909 | 987,
910 | 988,
911 | 989,
912 | 990,
913 | 991,
914 | 992,
915 | 993,
916 | 994,
917 | 995,
918 | 996,
919 | 997,
920 | 998,
921 | 999,
922 | 1000,
923 | 1001,
924 | 1002,
925 | 1003,
926 | 1004,
927 | 1005,
928 | 1006,
929 | 1007,
930 | 1008,
931 | 1009,
932 | 1010,
933 | 1011,
934 | 1012,
935 | 1013,
936 | 1014,
937 | 1015,
938 | 1016,
939 | 1017,
940 | 1018,
941 | 1019,
942 | 1020,
943 | 1021,
944 | 1022,
945 | 1023,
946 | 1024,
947 | 1025,
948 | 1026,
949 | 1027,
950 | 1028,
951 | 1029,
952 | 1030,
953 | 1031,
954 | 1032,
955 | 1033,
956 | 1034,
957 | 1035,
958 | 1036,
959 | 1037,
960 | 1038,
961 | 1039,
962 | 1040,
963 | 1041,
964 | 1042,
965 | 1043,
966 | 1044,
967 | 1045,
968 | 1046,
969 | 1047,
970 | 1048,
971 | 1049,
972 | 1050,
973 | 1051,
974 | 1052,
975 | 1053,
976 | 1054,
977 | 1055,
978 | 1056,
979 | 1057,
980 | 1058,
981 | 1059,
982 | 1060,
983 | 1061,
984 | 1062,
985 | 1063,
986 | 1064,
987 | 1065,
988 | 1066,
989 | 1067,
990 | 1068,
991 | 1069,
992 | 1070,
993 | 1071,
994 | 1072,
995 | 1073,
996 | 1074,
997 | 1075,
998 | 1076,
999 | 1077,
1000 | 1078,
1001 | 1079,
1002 | 1080,
1003 | 1081,
1004 | 1082,
1005 | 1083,
1006 | 1084,
1007 | 1085,
1008 | 1086,
1009 | 1087,
1010 | 1088,
1011 | 1089,
1012 | 1090,
1013 | 1091,
1014 | 1092,
1015 | 1093,
1016 | 1094,
1017 | 1095,
1018 | 1096,
1019 | 1097,
1020 | 1098,
1021 | 1099,
1022 | 1100,
1023 | 1101,
1024 | 1102,
1025 | 1103,
1026 | 1104,
1027 | 1105,
1028 | 1106,
1029 | 1107,
1030 | 1108,
1031 | 1109,
1032 | 1110,
1033 | 1111,
1034 | 1112,
1035 | 1113
1036 | ];
1037 | _planets[5] = [
1038 | 1114,
1039 | 1115,
1040 | 1116,
1041 | 1117,
1042 | 1118,
1043 | 1119,
1044 | 1120,
1045 | 1121,
1046 | 1122,
1047 | 1123,
1048 | 1124,
1049 | 1125,
1050 | 1126,
1051 | 1127,
1052 | 1128,
1053 | 1129,
1054 | 1130,
1055 | 1131,
1056 | 1132,
1057 | 1133,
1058 | 1134,
1059 | 1135,
1060 | 1136,
1061 | 1137,
1062 | 1138,
1063 | 1139,
1064 | 1140,
1065 | 1141,
1066 | 1142,
1067 | 1143,
1068 | 1144,
1069 | 1145,
1070 | 1146,
1071 | 1147,
1072 | 1148,
1073 | 1149,
1074 | 1150,
1075 | 1151,
1076 | 1152,
1077 | 1153,
1078 | 1154,
1079 | 1155,
1080 | 1156,
1081 | 1157,
1082 | 1158,
1083 | 1159,
1084 | 1160,
1085 | 1161,
1086 | 1162,
1087 | 1163,
1088 | 1164,
1089 | 1165,
1090 | 1166,
1091 | 1167,
1092 | 1168,
1093 | 1169,
1094 | 1170,
1095 | 1171,
1096 | 1172,
1097 | 1173,
1098 | 1174,
1099 | 1175,
1100 | 1176,
1101 | 1177,
1102 | 1178,
1103 | 1179,
1104 | 1180,
1105 | 1181,
1106 | 1182,
1107 | 1183,
1108 | 1184,
1109 | 1185,
1110 | 1186,
1111 | 1187,
1112 | 1188,
1113 | 1189,
1114 | 1190,
1115 | 1191,
1116 | 1192,
1117 | 1193,
1118 | 1194,
1119 | 1195,
1120 | 1196,
1121 | 1197,
1122 | 1198,
1123 | 1199,
1124 | 1200,
1125 | 1201,
1126 | 1202,
1127 | 1203,
1128 | 1204,
1129 | 1205,
1130 | 1206,
1131 | 1207,
1132 | 1208,
1133 | 1209,
1134 | 1210,
1135 | 1211,
1136 | 1212,
1137 | 1213,
1138 | 1214,
1139 | 1215,
1140 | 1216,
1141 | 1217,
1142 | 1218,
1143 | 1219,
1144 | 1220,
1145 | 1221,
1146 | 1222,
1147 | 1223,
1148 | 1224,
1149 | 1225,
1150 | 1226,
1151 | 1227,
1152 | 1228,
1153 | 1229,
1154 | 1230,
1155 | 1231,
1156 | 1232,
1157 | 1233,
1158 | 1234,
1159 | 1235,
1160 | 1236,
1161 | 1237,
1162 | 1238,
1163 | 1239,
1164 | 1240,
1165 | 1241,
1166 | 1242,
1167 | 1243,
1168 | 1244,
1169 | 1245,
1170 | 1246,
1171 | 1247,
1172 | 1248,
1173 | 1249,
1174 | 1250,
1175 | 1251,
1176 | 1252,
1177 | 1253,
1178 | 1254,
1179 | 1255,
1180 | 1256,
1181 | 1257,
1182 | 1258,
1183 | 1259,
1184 | 1260,
1185 | 1261,
1186 | 1262,
1187 | 1263,
1188 | 1264,
1189 | 1265,
1190 | 1266,
1191 | 1267,
1192 | 1268,
1193 | 1269,
1194 | 1270,
1195 | 1271,
1196 | 1272,
1197 | 1273,
1198 | 1274,
1199 | 1275,
1200 | 1276,
1201 | 1277,
1202 | 1278,
1203 | 1279,
1204 | 1280,
1205 | 1281,
1206 | 1282,
1207 | 1283,
1208 | 1284,
1209 | 1285,
1210 | 1286,
1211 | 1287,
1212 | 1288,
1213 | 1289,
1214 | 1290,
1215 | 1291,
1216 | 1292,
1217 | 1293,
1218 | 1294,
1219 | 1295,
1220 | 1296,
1221 | 1297,
1222 | 1298,
1223 | 1299,
1224 | 1300,
1225 | 1301,
1226 | 1302,
1227 | 1303,
1228 | 1304,
1229 | 1305,
1230 | 1306,
1231 | 1307,
1232 | 1308,
1233 | 1309,
1234 | 1310,
1235 | 1311,
1236 | 1312,
1237 | 1313,
1238 | 1314,
1239 | 1315,
1240 | 1316,
1241 | 1317,
1242 | 1318,
1243 | 1319,
1244 | 1320,
1245 | 1321,
1246 | 1322,
1247 | 1323,
1248 | 1324,
1249 | 1325,
1250 | 1326,
1251 | 1327,
1252 | 1328,
1253 | 1329,
1254 | 1330,
1255 | 1331,
1256 | 1332,
1257 | 1333,
1258 | 1334,
1259 | 1335,
1260 | 1336,
1261 | 1337,
1262 | 1338,
1263 | 1339,
1264 | 1340,
1265 | 1341,
1266 | 1342,
1267 | 1343,
1268 | 1344,
1269 | 1345,
1270 | 1346,
1271 | 1347,
1272 | 1348,
1273 | 1349,
1274 | 1350,
1275 | 1351,
1276 | 1352,
1277 | 1353,
1278 | 1354,
1279 | 1355,
1280 | 1356,
1281 | 1357,
1282 | 1358,
1283 | 1359,
1284 | 1360,
1285 | 1361,
1286 | 1362,
1287 | 1363,
1288 | 1364,
1289 | 1365
1290 | ];
1291 | _planets[6] = [
1292 | 1366,
1293 | 1367,
1294 | 1368,
1295 | 1369,
1296 | 1370,
1297 | 1371,
1298 | 1372,
1299 | 1373,
1300 | 1374,
1301 | 1375,
1302 | 1376,
1303 | 1377,
1304 | 1378,
1305 | 1379,
1306 | 1380,
1307 | 1381,
1308 | 1382,
1309 | 1383,
1310 | 1384,
1311 | 1385,
1312 | 1386,
1313 | 1387,
1314 | 1388,
1315 | 1389,
1316 | 1390,
1317 | 1391,
1318 | 1392,
1319 | 1393,
1320 | 1394,
1321 | 1395,
1322 | 1396,
1323 | 1397,
1324 | 1398,
1325 | 1399,
1326 | 1400,
1327 | 1401,
1328 | 1402,
1329 | 1403,
1330 | 1404,
1331 | 1405,
1332 | 1406,
1333 | 1407,
1334 | 1408,
1335 | 1409,
1336 | 1410,
1337 | 1411,
1338 | 1412,
1339 | 1413,
1340 | 1414,
1341 | 1415,
1342 | 1416,
1343 | 1417,
1344 | 1418,
1345 | 1419,
1346 | 1420,
1347 | 1421,
1348 | 1422,
1349 | 1423,
1350 | 1424,
1351 | 1425,
1352 | 1426,
1353 | 1427,
1354 | 1428,
1355 | 1429,
1356 | 1430,
1357 | 1431,
1358 | 1432,
1359 | 1433,
1360 | 1434,
1361 | 1435,
1362 | 1436,
1363 | 1437,
1364 | 1438,
1365 | 1439,
1366 | 1440,
1367 | 1441,
1368 | 1442,
1369 | 1443,
1370 | 1444,
1371 | 1445,
1372 | 1446,
1373 | 1447,
1374 | 1448,
1375 | 1449,
1376 | 1450,
1377 | 1451,
1378 | 1452,
1379 | 1453,
1380 | 1454,
1381 | 1455,
1382 | 1456,
1383 | 1457,
1384 | 1458,
1385 | 1459,
1386 | 1460,
1387 | 1461,
1388 | 1462,
1389 | 1463,
1390 | 1464,
1391 | 1465,
1392 | 1466,
1393 | 1467,
1394 | 1468,
1395 | 1469,
1396 | 1470,
1397 | 1471,
1398 | 1472,
1399 | 1473,
1400 | 1474,
1401 | 1475,
1402 | 1476,
1403 | 1477,
1404 | 1478,
1405 | 1479,
1406 | 1480,
1407 | 1481,
1408 | 1482,
1409 | 1483,
1410 | 1484,
1411 | 1485,
1412 | 1486,
1413 | 1487,
1414 | 1488,
1415 | 1489,
1416 | 1490,
1417 | 1491,
1418 | 1492,
1419 | 1493,
1420 | 1494,
1421 | 1495,
1422 | 1496,
1423 | 1497,
1424 | 1498,
1425 | 1499,
1426 | 1500,
1427 | 1501,
1428 | 1502,
1429 | 1503,
1430 | 1504,
1431 | 1505,
1432 | 1506,
1433 | 1507,
1434 | 1508,
1435 | 1509,
1436 | 1510,
1437 | 1511,
1438 | 1512,
1439 | 1513,
1440 | 1514,
1441 | 1515,
1442 | 1516,
1443 | 1517,
1444 | 1518,
1445 | 1519,
1446 | 1520,
1447 | 1521,
1448 | 1522,
1449 | 1523,
1450 | 1524,
1451 | 1525,
1452 | 1526,
1453 | 1527,
1454 | 1528,
1455 | 1529,
1456 | 1530,
1457 | 1531,
1458 | 1532,
1459 | 1533,
1460 | 1534,
1461 | 1535,
1462 | 1536,
1463 | 1537,
1464 | 1538,
1465 | 1539,
1466 | 1540,
1467 | 1541,
1468 | 1542,
1469 | 1543,
1470 | 1544,
1471 | 1545,
1472 | 1546,
1473 | 1547,
1474 | 1548,
1475 | 1549,
1476 | 1550,
1477 | 1551,
1478 | 1552,
1479 | 1553,
1480 | 1554,
1481 | 1555,
1482 | 1556,
1483 | 1557,
1484 | 1558,
1485 | 1559,
1486 | 1560,
1487 | 1561,
1488 | 1562,
1489 | 1563,
1490 | 1564,
1491 | 1565,
1492 | 1566,
1493 | 1567,
1494 | 1568,
1495 | 1569,
1496 | 1570,
1497 | 1571,
1498 | 1572,
1499 | 1573,
1500 | 1574,
1501 | 1575,
1502 | 1576,
1503 | 1577,
1504 | 1578,
1505 | 1579,
1506 | 1580,
1507 | 1581,
1508 | 1582,
1509 | 1583,
1510 | 1584,
1511 | 1585,
1512 | 1586,
1513 | 1587,
1514 | 1588,
1515 | 1589,
1516 | 1590,
1517 | 1591,
1518 | 1592,
1519 | 1593,
1520 | 1594,
1521 | 1595,
1522 | 1596,
1523 | 1597,
1524 | 1598,
1525 | 1599,
1526 | 1600,
1527 | 1601,
1528 | 1602,
1529 | 1603,
1530 | 1604,
1531 | 1605,
1532 | 1606,
1533 | 1607,
1534 | 1608,
1535 | 1609,
1536 | 1610,
1537 | 1611,
1538 | 1612,
1539 | 1613,
1540 | 1614,
1541 | 1615,
1542 | 1616,
1543 | 1617
1544 | ];
1545 | _planets[7] = [
1546 | 1618,
1547 | 1619,
1548 | 1620,
1549 | 1621,
1550 | 1622,
1551 | 1623,
1552 | 1624,
1553 | 1625,
1554 | 1626,
1555 | 1627,
1556 | 1628,
1557 | 1629,
1558 | 1630,
1559 | 1631,
1560 | 1632,
1561 | 1633,
1562 | 1634,
1563 | 1635,
1564 | 1636,
1565 | 1637,
1566 | 1638,
1567 | 1639,
1568 | 1640,
1569 | 1641,
1570 | 1642,
1571 | 1643,
1572 | 1644,
1573 | 1645,
1574 | 1646,
1575 | 1647,
1576 | 1648,
1577 | 1649,
1578 | 1650,
1579 | 1651,
1580 | 1652,
1581 | 1653,
1582 | 1654,
1583 | 1655,
1584 | 1656,
1585 | 1657,
1586 | 1658,
1587 | 1659,
1588 | 1660,
1589 | 1661,
1590 | 1662,
1591 | 1663,
1592 | 1664,
1593 | 1665,
1594 | 1666,
1595 | 1667,
1596 | 1668,
1597 | 1669,
1598 | 1670,
1599 | 1671,
1600 | 1672,
1601 | 1673,
1602 | 1674,
1603 | 1675,
1604 | 1676,
1605 | 1677,
1606 | 1678,
1607 | 1679,
1608 | 1680,
1609 | 1681,
1610 | 1682,
1611 | 1683,
1612 | 1684,
1613 | 1685,
1614 | 1686,
1615 | 1687,
1616 | 1688,
1617 | 1689,
1618 | 1690,
1619 | 1691,
1620 | 1692,
1621 | 1693,
1622 | 1694,
1623 | 1695,
1624 | 1696,
1625 | 1697,
1626 | 1698,
1627 | 1699,
1628 | 1700,
1629 | 1701,
1630 | 1702,
1631 | 1703,
1632 | 1704,
1633 | 1705,
1634 | 1706,
1635 | 1707,
1636 | 1708,
1637 | 1709,
1638 | 1710,
1639 | 1711,
1640 | 1712,
1641 | 1713,
1642 | 1714,
1643 | 1715,
1644 | 1716,
1645 | 1717,
1646 | 1718,
1647 | 1719,
1648 | 1720,
1649 | 1721,
1650 | 1722,
1651 | 1723,
1652 | 1724,
1653 | 1725,
1654 | 1726,
1655 | 1727,
1656 | 1728,
1657 | 1729,
1658 | 1730,
1659 | 1731,
1660 | 1732,
1661 | 1733,
1662 | 1734,
1663 | 1735,
1664 | 1736,
1665 | 1737,
1666 | 1738,
1667 | 1739,
1668 | 1740,
1669 | 1741,
1670 | 1742,
1671 | 1743,
1672 | 1744,
1673 | 1745,
1674 | 1746,
1675 | 1747,
1676 | 1748,
1677 | 1749,
1678 | 1750,
1679 | 1751,
1680 | 1752,
1681 | 1753,
1682 | 1754,
1683 | 1755,
1684 | 1756,
1685 | 1757,
1686 | 1758,
1687 | 1759,
1688 | 1760,
1689 | 1761,
1690 | 1762,
1691 | 1763,
1692 | 1764,
1693 | 1765,
1694 | 1766,
1695 | 1767,
1696 | 1768,
1697 | 1769,
1698 | 1770,
1699 | 1771,
1700 | 1772,
1701 | 1773,
1702 | 1774,
1703 | 1775,
1704 | 1776,
1705 | 1777,
1706 | 1778,
1707 | 1779,
1708 | 1780,
1709 | 1781,
1710 | 1782,
1711 | 1783,
1712 | 1784,
1713 | 1785,
1714 | 1786,
1715 | 1787,
1716 | 1788,
1717 | 1789,
1718 | 1790,
1719 | 1791,
1720 | 1792,
1721 | 1793,
1722 | 1794,
1723 | 1795,
1724 | 1796,
1725 | 1797,
1726 | 1798,
1727 | 1799,
1728 | 1800,
1729 | 1801,
1730 | 1802,
1731 | 1803,
1732 | 1804,
1733 | 1805,
1734 | 1806,
1735 | 1807,
1736 | 1808,
1737 | 1809,
1738 | 1810,
1739 | 1811,
1740 | 1812,
1741 | 1813,
1742 | 1814,
1743 | 1815,
1744 | 1816,
1745 | 1817,
1746 | 1818,
1747 | 1819,
1748 | 1820,
1749 | 1821,
1750 | 1822,
1751 | 1823,
1752 | 1824,
1753 | 1825,
1754 | 1826,
1755 | 1827,
1756 | 1828,
1757 | 1829,
1758 | 1830,
1759 | 1831,
1760 | 1832,
1761 | 1833,
1762 | 1834,
1763 | 1835,
1764 | 1836,
1765 | 1837,
1766 | 1838,
1767 | 1839,
1768 | 1840,
1769 | 1841,
1770 | 1842,
1771 | 1843,
1772 | 1844,
1773 | 1845,
1774 | 1846,
1775 | 1847,
1776 | 1848,
1777 | 1849,
1778 | 1850,
1779 | 1851,
1780 | 1852,
1781 | 1853,
1782 | 1854,
1783 | 1855,
1784 | 1856,
1785 | 1857,
1786 | 1858,
1787 | 1859,
1788 | 1860,
1789 | 1861,
1790 | 1862,
1791 | 1863,
1792 | 1864,
1793 | 1865,
1794 | 1866,
1795 | 1867,
1796 | 1868,
1797 | 1869
1798 | ];
1799 | _planets[8] = [
1800 | 1870,
1801 | 1871,
1802 | 1872,
1803 | 1873,
1804 | 1874,
1805 | 1875,
1806 | 1876,
1807 | 1877,
1808 | 1878,
1809 | 1879,
1810 | 1880,
1811 | 1881,
1812 | 1882,
1813 | 1883,
1814 | 1884,
1815 | 1885,
1816 | 1886,
1817 | 1887,
1818 | 1888,
1819 | 1889,
1820 | 1890,
1821 | 1891,
1822 | 1892,
1823 | 1893,
1824 | 1894,
1825 | 1895,
1826 | 1896,
1827 | 1897,
1828 | 1898,
1829 | 1899,
1830 | 1900,
1831 | 1901,
1832 | 1902,
1833 | 1903,
1834 | 1904,
1835 | 1905,
1836 | 1906,
1837 | 1907,
1838 | 1908,
1839 | 1909,
1840 | 1910,
1841 | 1911,
1842 | 1912,
1843 | 1913,
1844 | 1914,
1845 | 1915,
1846 | 1916,
1847 | 1917,
1848 | 1918,
1849 | 1919,
1850 | 1920,
1851 | 1921,
1852 | 1922,
1853 | 1923,
1854 | 1924,
1855 | 1925,
1856 | 1926,
1857 | 1927,
1858 | 1928,
1859 | 1929,
1860 | 1930,
1861 | 1931,
1862 | 1932,
1863 | 1933,
1864 | 1934,
1865 | 1935,
1866 | 1936,
1867 | 1937,
1868 | 1938,
1869 | 1939,
1870 | 1940,
1871 | 1941,
1872 | 1942,
1873 | 1943,
1874 | 1944,
1875 | 1945,
1876 | 1946,
1877 | 1947,
1878 | 1948,
1879 | 1949,
1880 | 1950,
1881 | 1951,
1882 | 1952,
1883 | 1953,
1884 | 1954,
1885 | 1955,
1886 | 1956,
1887 | 1957,
1888 | 1958,
1889 | 1959,
1890 | 1960,
1891 | 1961,
1892 | 1962,
1893 | 1963,
1894 | 1964,
1895 | 1965,
1896 | 1966,
1897 | 1967,
1898 | 1968,
1899 | 1969,
1900 | 1970,
1901 | 1971,
1902 | 1972,
1903 | 1973,
1904 | 1974,
1905 | 1975,
1906 | 1976,
1907 | 1977,
1908 | 1978,
1909 | 1979,
1910 | 1980,
1911 | 1981,
1912 | 1982,
1913 | 1983,
1914 | 1984,
1915 | 1985,
1916 | 1986,
1917 | 1987,
1918 | 1988,
1919 | 1989,
1920 | 1990,
1921 | 1991,
1922 | 1992,
1923 | 1993,
1924 | 1994,
1925 | 1995,
1926 | 1996,
1927 | 1997,
1928 | 1998,
1929 | 1999,
1930 | 2000,
1931 | 2001,
1932 | 2002,
1933 | 2003,
1934 | 2004,
1935 | 2005,
1936 | 2006,
1937 | 2007,
1938 | 2008,
1939 | 2009,
1940 | 2010,
1941 | 2011,
1942 | 2012,
1943 | 2013,
1944 | 2014,
1945 | 2015,
1946 | 2016,
1947 | 2017,
1948 | 2018,
1949 | 2019,
1950 | 2020,
1951 | 2021,
1952 | 2022,
1953 | 2023,
1954 | 2024,
1955 | 2025,
1956 | 2026,
1957 | 2027,
1958 | 2028,
1959 | 2029,
1960 | 2030,
1961 | 2031,
1962 | 2032,
1963 | 2033,
1964 | 2034,
1965 | 2035,
1966 | 2036,
1967 | 2037,
1968 | 2038,
1969 | 2039,
1970 | 2040,
1971 | 2041,
1972 | 2042,
1973 | 2043,
1974 | 2044,
1975 | 2045,
1976 | 2046,
1977 | 2047,
1978 | 2048,
1979 | 2049,
1980 | 2050,
1981 | 2051,
1982 | 2052,
1983 | 2053,
1984 | 2054,
1985 | 2055,
1986 | 2056,
1987 | 2057,
1988 | 2058,
1989 | 2059,
1990 | 2060,
1991 | 2061,
1992 | 2062,
1993 | 2063,
1994 | 2064,
1995 | 2065,
1996 | 2066,
1997 | 2067,
1998 | 2068,
1999 | 2069,
2000 | 2070,
2001 | 2071,
2002 | 2072,
2003 | 2073,
2004 | 2074,
2005 | 2075,
2006 | 2076,
2007 | 2077,
2008 | 2078,
2009 | 2079,
2010 | 2080,
2011 | 2081,
2012 | 2082,
2013 | 2083,
2014 | 2084,
2015 | 2085,
2016 | 2086,
2017 | 2087,
2018 | 2088,
2019 | 2089,
2020 | 2090,
2021 | 2091,
2022 | 2092,
2023 | 2093,
2024 | 2094,
2025 | 2095,
2026 | 2096,
2027 | 2097,
2028 | 2098,
2029 | 2099,
2030 | 2100,
2031 | 2101,
2032 | 2102,
2033 | 2103,
2034 | 2104,
2035 | 2105,
2036 | 2106,
2037 | 2107,
2038 | 2108,
2039 | 2109,
2040 | 2110,
2041 | 2111,
2042 | 2112,
2043 | 2113,
2044 | 2114,
2045 | 2115,
2046 | 2116,
2047 | 2117,
2048 | 2118,
2049 | 2119,
2050 | 2120,
2051 | 2121
2052 | ];
2053 | _planets[9] = [
2054 | 2122,
2055 | 2123,
2056 | 2124,
2057 | 2125,
2058 | 2126,
2059 | 2127,
2060 | 2128,
2061 | 2129,
2062 | 2130,
2063 | 2131,
2064 | 2132,
2065 | 2133,
2066 | 2134,
2067 | 2135,
2068 | 2136,
2069 | 2137,
2070 | 2138,
2071 | 2139,
2072 | 2140,
2073 | 2141,
2074 | 2142,
2075 | 2143,
2076 | 2144,
2077 | 2145,
2078 | 2146,
2079 | 2147,
2080 | 2148,
2081 | 2149,
2082 | 2150,
2083 | 2151,
2084 | 2152,
2085 | 2153,
2086 | 2154,
2087 | 2155,
2088 | 2156,
2089 | 2157,
2090 | 2158,
2091 | 2159,
2092 | 2160,
2093 | 2161,
2094 | 2162,
2095 | 2163,
2096 | 2164,
2097 | 2165,
2098 | 2166,
2099 | 2167,
2100 | 2168,
2101 | 2169,
2102 | 2170,
2103 | 2171,
2104 | 2172,
2105 | 2173,
2106 | 2174,
2107 | 2175,
2108 | 2176,
2109 | 2177,
2110 | 2178,
2111 | 2179,
2112 | 2180,
2113 | 2181,
2114 | 2182,
2115 | 2183,
2116 | 2184,
2117 | 2185,
2118 | 2186,
2119 | 2187,
2120 | 2188,
2121 | 2189,
2122 | 2190,
2123 | 2191,
2124 | 2192,
2125 | 2193,
2126 | 2194,
2127 | 2195,
2128 | 2196,
2129 | 2197,
2130 | 2198,
2131 | 2199,
2132 | 2200,
2133 | 2201,
2134 | 2202,
2135 | 2203,
2136 | 2204,
2137 | 2205,
2138 | 2206,
2139 | 2207,
2140 | 2208,
2141 | 2209,
2142 | 2210,
2143 | 2211,
2144 | 2212,
2145 | 2213,
2146 | 2214,
2147 | 2215,
2148 | 2216,
2149 | 2217,
2150 | 2218,
2151 | 2219,
2152 | 2220,
2153 | 2221,
2154 | 2222,
2155 | 2223,
2156 | 2224,
2157 | 2225,
2158 | 2226,
2159 | 2227,
2160 | 2228,
2161 | 2229,
2162 | 2230,
2163 | 2231,
2164 | 2232,
2165 | 2233,
2166 | 2234,
2167 | 2235,
2168 | 2236,
2169 | 2237,
2170 | 2238,
2171 | 2239,
2172 | 2240,
2173 | 2241,
2174 | 2242,
2175 | 2243,
2176 | 2244,
2177 | 2245,
2178 | 2246,
2179 | 2247,
2180 | 2248,
2181 | 2249,
2182 | 2250,
2183 | 2251,
2184 | 2252,
2185 | 2253,
2186 | 2254,
2187 | 2255,
2188 | 2256,
2189 | 2257,
2190 | 2258,
2191 | 2259,
2192 | 2260,
2193 | 2261,
2194 | 2262,
2195 | 2263,
2196 | 2264,
2197 | 2265,
2198 | 2266,
2199 | 2267,
2200 | 2268,
2201 | 2269,
2202 | 2270,
2203 | 2271,
2204 | 2272,
2205 | 2273,
2206 | 2274,
2207 | 2275,
2208 | 2276,
2209 | 2277,
2210 | 2278,
2211 | 2279,
2212 | 2280,
2213 | 2281,
2214 | 2282,
2215 | 2283,
2216 | 2284,
2217 | 2285,
2218 | 2286,
2219 | 2287,
2220 | 2288,
2221 | 2289,
2222 | 2290,
2223 | 2291,
2224 | 2292,
2225 | 2293,
2226 | 2294,
2227 | 2295,
2228 | 2296,
2229 | 2297,
2230 | 2298,
2231 | 2299,
2232 | 2300,
2233 | 2301,
2234 | 2302,
2235 | 2303,
2236 | 2304,
2237 | 2305,
2238 | 2306,
2239 | 2307,
2240 | 2308,
2241 | 2309,
2242 | 2310,
2243 | 2311,
2244 | 2312,
2245 | 2313,
2246 | 2314,
2247 | 2315,
2248 | 2316,
2249 | 2317,
2250 | 2318,
2251 | 2319,
2252 | 2320,
2253 | 2321,
2254 | 2322,
2255 | 2323,
2256 | 2324,
2257 | 2325,
2258 | 2326,
2259 | 2327,
2260 | 2328,
2261 | 2329,
2262 | 2330,
2263 | 2331,
2264 | 2332,
2265 | 2333,
2266 | 2334,
2267 | 2335,
2268 | 2336,
2269 | 2337,
2270 | 2338,
2271 | 2339,
2272 | 2340,
2273 | 2341,
2274 | 2342,
2275 | 2343,
2276 | 2344,
2277 | 2345,
2278 | 2346,
2279 | 2347,
2280 | 2348,
2281 | 2349,
2282 | 2350,
2283 | 2351,
2284 | 2352,
2285 | 2353,
2286 | 2354,
2287 | 2355,
2288 | 2356,
2289 | 2357,
2290 | 2358,
2291 | 2359,
2292 | 2360,
2293 | 2361,
2294 | 2362,
2295 | 2363,
2296 | 2364,
2297 | 2365,
2298 | 2366,
2299 | 2367,
2300 | 2368,
2301 | 2369,
2302 | 2370,
2303 | 2371,
2304 | 2372,
2305 | 2373
2306 | ];
2307 | _planets[10] = [
2308 | 2374,
2309 | 2375,
2310 | 2376,
2311 | 2377,
2312 | 2378,
2313 | 2379,
2314 | 2380,
2315 | 2381,
2316 | 2382,
2317 | 2383,
2318 | 2384,
2319 | 2385,
2320 | 2386,
2321 | 2387,
2322 | 2388,
2323 | 2389,
2324 | 2390,
2325 | 2391,
2326 | 2392,
2327 | 2393,
2328 | 2394,
2329 | 2395,
2330 | 2396,
2331 | 2397,
2332 | 2398,
2333 | 2399,
2334 | 2400,
2335 | 2401,
2336 | 2402,
2337 | 2403,
2338 | 2404,
2339 | 2405,
2340 | 2406,
2341 | 2407,
2342 | 2408,
2343 | 2409,
2344 | 2410,
2345 | 2411,
2346 | 2412,
2347 | 2413,
2348 | 2414,
2349 | 2415,
2350 | 2416,
2351 | 2417,
2352 | 2418,
2353 | 2419,
2354 | 2420,
2355 | 2421,
2356 | 2422,
2357 | 2423,
2358 | 2424,
2359 | 2425,
2360 | 2426,
2361 | 2427,
2362 | 2428,
2363 | 2429,
2364 | 2430,
2365 | 2431,
2366 | 2432,
2367 | 2433,
2368 | 2434,
2369 | 2435,
2370 | 2436,
2371 | 2437,
2372 | 2438,
2373 | 2439,
2374 | 2440,
2375 | 2441,
2376 | 2442,
2377 | 2443,
2378 | 2444,
2379 | 2445,
2380 | 2446,
2381 | 2447,
2382 | 2448,
2383 | 2449,
2384 | 2450,
2385 | 2451,
2386 | 2452,
2387 | 2453,
2388 | 2454,
2389 | 2455,
2390 | 2456,
2391 | 2457,
2392 | 2458,
2393 | 2459,
2394 | 2460,
2395 | 2461,
2396 | 2462,
2397 | 2463,
2398 | 2464,
2399 | 2465,
2400 | 2466,
2401 | 2467,
2402 | 2468,
2403 | 2469,
2404 | 2470,
2405 | 2471,
2406 | 2472,
2407 | 2473,
2408 | 2474,
2409 | 2475,
2410 | 2476,
2411 | 2477,
2412 | 2478,
2413 | 2479,
2414 | 2480,
2415 | 2481,
2416 | 2482,
2417 | 2483,
2418 | 2484,
2419 | 2485,
2420 | 2486,
2421 | 2487,
2422 | 2488,
2423 | 2489,
2424 | 2490,
2425 | 2491,
2426 | 2492,
2427 | 2493,
2428 | 2494,
2429 | 2495,
2430 | 2496,
2431 | 2497,
2432 | 2498,
2433 | 2499,
2434 | 2500,
2435 | 2501,
2436 | 2502,
2437 | 2503,
2438 | 2504,
2439 | 2505,
2440 | 2506,
2441 | 2507,
2442 | 2508,
2443 | 2509,
2444 | 2510,
2445 | 2511,
2446 | 2512,
2447 | 2513,
2448 | 2514,
2449 | 2515,
2450 | 2516,
2451 | 2517,
2452 | 2518,
2453 | 2519,
2454 | 2520,
2455 | 2521,
2456 | 2522,
2457 | 2523,
2458 | 2524,
2459 | 2525,
2460 | 2526,
2461 | 2527,
2462 | 2528,
2463 | 2529,
2464 | 2530,
2465 | 2531,
2466 | 2532,
2467 | 2533,
2468 | 2534,
2469 | 2535,
2470 | 2536,
2471 | 2537,
2472 | 2538,
2473 | 2539,
2474 | 2540,
2475 | 2541,
2476 | 2542,
2477 | 2543,
2478 | 2544,
2479 | 2545,
2480 | 2546,
2481 | 2547,
2482 | 2548,
2483 | 2549,
2484 | 2550,
2485 | 2551,
2486 | 2552,
2487 | 2553,
2488 | 2554,
2489 | 2555,
2490 | 2556,
2491 | 2557,
2492 | 2558,
2493 | 2559,
2494 | 2560,
2495 | 2561,
2496 | 2562,
2497 | 2563,
2498 | 2564,
2499 | 2565,
2500 | 2566,
2501 | 2567,
2502 | 2568,
2503 | 2569,
2504 | 2570,
2505 | 2571,
2506 | 2572,
2507 | 2573,
2508 | 2574,
2509 | 2575,
2510 | 2576,
2511 | 2577,
2512 | 2578,
2513 | 2579,
2514 | 2580,
2515 | 2581,
2516 | 2582,
2517 | 2583,
2518 | 2584,
2519 | 2585,
2520 | 2586,
2521 | 2587,
2522 | 2588,
2523 | 2589,
2524 | 2590,
2525 | 2591,
2526 | 2592,
2527 | 2593,
2528 | 2594,
2529 | 2595,
2530 | 2596,
2531 | 2597,
2532 | 2598,
2533 | 2599,
2534 | 2600,
2535 | 2601,
2536 | 2602,
2537 | 2603,
2538 | 2604,
2539 | 2605,
2540 | 2606,
2541 | 2607,
2542 | 2608,
2543 | 2609,
2544 | 2610,
2545 | 2611,
2546 | 2612,
2547 | 2613,
2548 | 2614,
2549 | 2615,
2550 | 2616,
2551 | 2617,
2552 | 2618,
2553 | 2619,
2554 | 2620,
2555 | 2621,
2556 | 2622,
2557 | 2623,
2558 | 2624,
2559 | 2625
2560 | ];
2561 | _planets[11] = [
2562 | 2626,
2563 | 2627,
2564 | 2628,
2565 | 2629,
2566 | 2630,
2567 | 2631,
2568 | 2632,
2569 | 2633,
2570 | 2634,
2571 | 2635,
2572 | 2636,
2573 | 2637,
2574 | 2638,
2575 | 2639,
2576 | 2640,
2577 | 2641,
2578 | 2642,
2579 | 2643,
2580 | 2644,
2581 | 2645,
2582 | 2646,
2583 | 2647,
2584 | 2648,
2585 | 2649,
2586 | 2650,
2587 | 2651,
2588 | 2652,
2589 | 2653,
2590 | 2654,
2591 | 2655,
2592 | 2656,
2593 | 2657,
2594 | 2658,
2595 | 2659,
2596 | 2660,
2597 | 2661,
2598 | 2662,
2599 | 2663,
2600 | 2664,
2601 | 2665,
2602 | 2666,
2603 | 2667,
2604 | 2668,
2605 | 2669,
2606 | 2670,
2607 | 2671,
2608 | 2672,
2609 | 2673,
2610 | 2674,
2611 | 2675,
2612 | 2676,
2613 | 2677,
2614 | 2678,
2615 | 2679,
2616 | 2680,
2617 | 2681,
2618 | 2682,
2619 | 2683,
2620 | 2684,
2621 | 2685,
2622 | 2686,
2623 | 2687,
2624 | 2688,
2625 | 2689,
2626 | 2690,
2627 | 2691,
2628 | 2692,
2629 | 2693,
2630 | 2694,
2631 | 2695,
2632 | 2696,
2633 | 2697,
2634 | 2698,
2635 | 2699,
2636 | 2700,
2637 | 2701,
2638 | 2702,
2639 | 2703,
2640 | 2704,
2641 | 2705,
2642 | 2706,
2643 | 2707,
2644 | 2708,
2645 | 2709,
2646 | 2710,
2647 | 2711,
2648 | 2712,
2649 | 2713,
2650 | 2714,
2651 | 2715,
2652 | 2716,
2653 | 2717,
2654 | 2718,
2655 | 2719,
2656 | 2720,
2657 | 2721,
2658 | 2722,
2659 | 2723,
2660 | 2724,
2661 | 2725,
2662 | 2726,
2663 | 2727,
2664 | 2728,
2665 | 2729,
2666 | 2730,
2667 | 2731,
2668 | 2732,
2669 | 2733,
2670 | 2734,
2671 | 2735,
2672 | 2736,
2673 | 2737,
2674 | 2738,
2675 | 2739,
2676 | 2740,
2677 | 2741,
2678 | 2742,
2679 | 2743,
2680 | 2744,
2681 | 2745,
2682 | 2746,
2683 | 2747,
2684 | 2748,
2685 | 2749,
2686 | 2750,
2687 | 2751,
2688 | 2752,
2689 | 2753,
2690 | 2754,
2691 | 2755,
2692 | 2756,
2693 | 2757,
2694 | 2758,
2695 | 2759,
2696 | 2760,
2697 | 2761,
2698 | 2762,
2699 | 2763,
2700 | 2764,
2701 | 2765,
2702 | 2766,
2703 | 2767,
2704 | 2768,
2705 | 2769,
2706 | 2770,
2707 | 2771,
2708 | 2772,
2709 | 2773,
2710 | 2774,
2711 | 2775,
2712 | 2776,
2713 | 2777,
2714 | 2778,
2715 | 2779,
2716 | 2780,
2717 | 2781,
2718 | 2782,
2719 | 2783,
2720 | 2784,
2721 | 2785,
2722 | 2786,
2723 | 2787,
2724 | 2788,
2725 | 2789,
2726 | 2790,
2727 | 2791,
2728 | 2792,
2729 | 2793,
2730 | 2794,
2731 | 2795,
2732 | 2796,
2733 | 2797,
2734 | 2798,
2735 | 2799,
2736 | 2800,
2737 | 2801,
2738 | 2802,
2739 | 2803,
2740 | 2804,
2741 | 2805,
2742 | 2806,
2743 | 2807,
2744 | 2808,
2745 | 2809,
2746 | 2810,
2747 | 2811,
2748 | 2812,
2749 | 2813,
2750 | 2814,
2751 | 2815,
2752 | 2816,
2753 | 2817,
2754 | 2818,
2755 | 2819,
2756 | 2820,
2757 | 2821,
2758 | 2822,
2759 | 2823,
2760 | 2824,
2761 | 2825,
2762 | 2826,
2763 | 2827,
2764 | 2828,
2765 | 2829,
2766 | 2830,
2767 | 2831,
2768 | 2832,
2769 | 2833,
2770 | 2834,
2771 | 2835,
2772 | 2836,
2773 | 2837,
2774 | 2838,
2775 | 2839,
2776 | 2840,
2777 | 2841,
2778 | 2842,
2779 | 2843,
2780 | 2844,
2781 | 2845,
2782 | 2846,
2783 | 2847,
2784 | 2848,
2785 | 2849,
2786 | 2850,
2787 | 2851,
2788 | 2852,
2789 | 2853,
2790 | 2854,
2791 | 2855,
2792 | 2856,
2793 | 2857,
2794 | 2858,
2795 | 2859,
2796 | 2860,
2797 | 2861,
2798 | 2862,
2799 | 2863,
2800 | 2864,
2801 | 2865,
2802 | 2866,
2803 | 2867,
2804 | 2868,
2805 | 2869,
2806 | 2870,
2807 | 2871,
2808 | 2872,
2809 | 2873,
2810 | 2874,
2811 | 2875,
2812 | 2876,
2813 | 2877
2814 | ];
2815 | _planets[12] = [
2816 | 2878,
2817 | 2879,
2818 | 2880,
2819 | 2881,
2820 | 2882,
2821 | 2883,
2822 | 2884,
2823 | 2885,
2824 | 2886,
2825 | 2887,
2826 | 2888,
2827 | 2889,
2828 | 2890,
2829 | 2891,
2830 | 2892,
2831 | 2893,
2832 | 2894,
2833 | 2895,
2834 | 2896,
2835 | 2897,
2836 | 2898,
2837 | 2899,
2838 | 2900,
2839 | 2901,
2840 | 2902,
2841 | 2903,
2842 | 2904,
2843 | 2905,
2844 | 2906,
2845 | 2907,
2846 | 2908,
2847 | 2909,
2848 | 2910,
2849 | 2911,
2850 | 2912,
2851 | 2913,
2852 | 2914,
2853 | 2915,
2854 | 2916,
2855 | 2917,
2856 | 2918,
2857 | 2919,
2858 | 2920,
2859 | 2921,
2860 | 2922,
2861 | 2923,
2862 | 2924,
2863 | 2925,
2864 | 2926,
2865 | 2927,
2866 | 2928,
2867 | 2929,
2868 | 2930,
2869 | 2931,
2870 | 2932,
2871 | 2933,
2872 | 2934,
2873 | 2935,
2874 | 2936,
2875 | 2937,
2876 | 2938,
2877 | 2939,
2878 | 2940,
2879 | 2941,
2880 | 2942,
2881 | 2943,
2882 | 2944,
2883 | 2945,
2884 | 2946,
2885 | 2947,
2886 | 2948,
2887 | 2949,
2888 | 2950,
2889 | 2951,
2890 | 2952,
2891 | 2953,
2892 | 2954,
2893 | 2955,
2894 | 2956,
2895 | 2957,
2896 | 2958,
2897 | 2959,
2898 | 2960,
2899 | 2961,
2900 | 2962,
2901 | 2963,
2902 | 2964,
2903 | 2965,
2904 | 2966,
2905 | 2967,
2906 | 2968,
2907 | 2969,
2908 | 2970,
2909 | 2971,
2910 | 2972,
2911 | 2973,
2912 | 2974,
2913 | 2975,
2914 | 2976,
2915 | 2977,
2916 | 2978,
2917 | 2979,
2918 | 2980,
2919 | 2981,
2920 | 2982,
2921 | 2983,
2922 | 2984,
2923 | 2985,
2924 | 2986,
2925 | 2987,
2926 | 2988,
2927 | 2989,
2928 | 2990,
2929 | 2991,
2930 | 2992,
2931 | 2993,
2932 | 2994,
2933 | 2995,
2934 | 2996,
2935 | 2997,
2936 | 2998,
2937 | 2999,
2938 | 3000,
2939 | 3001,
2940 | 3002,
2941 | 3003,
2942 | 3004,
2943 | 3005,
2944 | 3006,
2945 | 3007,
2946 | 3008,
2947 | 3009,
2948 | 3010,
2949 | 3011,
2950 | 3012,
2951 | 3013,
2952 | 3014,
2953 | 3015,
2954 | 3016,
2955 | 3017,
2956 | 3018,
2957 | 3019,
2958 | 3020,
2959 | 3021,
2960 | 3022,
2961 | 3023,
2962 | 3024,
2963 | 3025,
2964 | 3026,
2965 | 3027,
2966 | 3028,
2967 | 3029,
2968 | 3030,
2969 | 3031,
2970 | 3032,
2971 | 3033,
2972 | 3034,
2973 | 3035,
2974 | 3036,
2975 | 3037,
2976 | 3038,
2977 | 3039,
2978 | 3040,
2979 | 3041,
2980 | 3042,
2981 | 3043,
2982 | 3044,
2983 | 3045,
2984 | 3046,
2985 | 3047,
2986 | 3048,
2987 | 3049,
2988 | 3050,
2989 | 3051,
2990 | 3052,
2991 | 3053,
2992 | 3054,
2993 | 3055,
2994 | 3056,
2995 | 3057,
2996 | 3058,
2997 | 3059,
2998 | 3060,
2999 | 3061,
3000 | 3062,
3001 | 3063,
3002 | 3064,
3003 | 3065,
3004 | 3066,
3005 | 3067,
3006 | 3068,
3007 | 3069,
3008 | 3070,
3009 | 3071,
3010 | 3072,
3011 | 3073,
3012 | 3074,
3013 | 3075,
3014 | 3076,
3015 | 3077,
3016 | 3078,
3017 | 3079,
3018 | 3080,
3019 | 3081,
3020 | 3082,
3021 | 3083,
3022 | 3084,
3023 | 3085,
3024 | 3086,
3025 | 3087,
3026 | 3088,
3027 | 3089,
3028 | 3090,
3029 | 3091,
3030 | 3092,
3031 | 3093,
3032 | 3094,
3033 | 3095,
3034 | 3096,
3035 | 3097,
3036 | 3098,
3037 | 3099,
3038 | 3100,
3039 | 3101,
3040 | 3102,
3041 | 3103,
3042 | 3104,
3043 | 3105,
3044 | 3106,
3045 | 3107,
3046 | 3108,
3047 | 3109,
3048 | 3110,
3049 | 3111,
3050 | 3112,
3051 | 3113,
3052 | 3114,
3053 | 3115,
3054 | 3116,
3055 | 3117,
3056 | 3118,
3057 | 3119,
3058 | 3120,
3059 | 3121,
3060 | 3122,
3061 | 3123,
3062 | 3124,
3063 | 3125,
3064 | 3126,
3065 | 3127,
3066 | 3128,
3067 | 3129
3068 | ];
3069 | _planets[13] = [
3070 | 3130,
3071 | 3131,
3072 | 3132,
3073 | 3133,
3074 | 3134,
3075 | 3135,
3076 | 3136,
3077 | 3137,
3078 | 3138,
3079 | 3139,
3080 | 3140,
3081 | 3141,
3082 | 3142,
3083 | 3143,
3084 | 3144,
3085 | 3145,
3086 | 3146,
3087 | 3147,
3088 | 3148,
3089 | 3149,
3090 | 3150,
3091 | 3151,
3092 | 3152,
3093 | 3153,
3094 | 3154,
3095 | 3155,
3096 | 3156,
3097 | 3157,
3098 | 3158,
3099 | 3159,
3100 | 3160,
3101 | 3161,
3102 | 3162,
3103 | 3163,
3104 | 3164,
3105 | 3165,
3106 | 3166,
3107 | 3167,
3108 | 3168,
3109 | 3169,
3110 | 3170,
3111 | 3171,
3112 | 3172,
3113 | 3173,
3114 | 3174,
3115 | 3175,
3116 | 3176,
3117 | 3177,
3118 | 3178,
3119 | 3179,
3120 | 3180,
3121 | 3181,
3122 | 3182,
3123 | 3183,
3124 | 3184,
3125 | 3185,
3126 | 3186,
3127 | 3187,
3128 | 3188,
3129 | 3189,
3130 | 3190,
3131 | 3191,
3132 | 3192,
3133 | 3193,
3134 | 3194,
3135 | 3195,
3136 | 3196,
3137 | 3197,
3138 | 3198,
3139 | 3199,
3140 | 3200,
3141 | 3201,
3142 | 3202,
3143 | 3203,
3144 | 3204,
3145 | 3205,
3146 | 3206,
3147 | 3207,
3148 | 3208,
3149 | 3209,
3150 | 3210,
3151 | 3211,
3152 | 3212,
3153 | 3213,
3154 | 3214,
3155 | 3215,
3156 | 3216,
3157 | 3217,
3158 | 3218,
3159 | 3219,
3160 | 3220,
3161 | 3221,
3162 | 3222,
3163 | 3223,
3164 | 3224,
3165 | 3225,
3166 | 3226,
3167 | 3227,
3168 | 3228,
3169 | 3229,
3170 | 3230,
3171 | 3231,
3172 | 3232,
3173 | 3233,
3174 | 3234,
3175 | 3235,
3176 | 3236,
3177 | 3237,
3178 | 3238,
3179 | 3239,
3180 | 3240,
3181 | 3241,
3182 | 3242,
3183 | 3243,
3184 | 3244,
3185 | 3245,
3186 | 3246,
3187 | 3247,
3188 | 3248,
3189 | 3249,
3190 | 3250,
3191 | 3251,
3192 | 3252,
3193 | 3253,
3194 | 3254,
3195 | 3255,
3196 | 3256,
3197 | 3257,
3198 | 3258,
3199 | 3259,
3200 | 3260,
3201 | 3261,
3202 | 3262,
3203 | 3263,
3204 | 3264,
3205 | 3265,
3206 | 3266,
3207 | 3267,
3208 | 3268,
3209 | 3269,
3210 | 3270,
3211 | 3271,
3212 | 3272,
3213 | 3273,
3214 | 3274,
3215 | 3275,
3216 | 3276,
3217 | 3277,
3218 | 3278,
3219 | 3279,
3220 | 3280,
3221 | 3281,
3222 | 3282,
3223 | 3283,
3224 | 3284,
3225 | 3285,
3226 | 3286,
3227 | 3287,
3228 | 3288,
3229 | 3289,
3230 | 3290,
3231 | 3291,
3232 | 3292,
3233 | 3293,
3234 | 3294,
3235 | 3295,
3236 | 3296,
3237 | 3297,
3238 | 3298,
3239 | 3299,
3240 | 3300,
3241 | 3301,
3242 | 3302,
3243 | 3303,
3244 | 3304,
3245 | 3305,
3246 | 3306,
3247 | 3307,
3248 | 3308,
3249 | 3309,
3250 | 3310,
3251 | 3311,
3252 | 3312,
3253 | 3313,
3254 | 3314,
3255 | 3315,
3256 | 3316,
3257 | 3317,
3258 | 3318,
3259 | 3319,
3260 | 3320,
3261 | 3321,
3262 | 3322,
3263 | 3323,
3264 | 3324,
3265 | 3325,
3266 | 3326,
3267 | 3327,
3268 | 3328,
3269 | 3329,
3270 | 3330,
3271 | 3331,
3272 | 3332,
3273 | 3333,
3274 | 3334,
3275 | 3335,
3276 | 3336,
3277 | 3337,
3278 | 3338,
3279 | 3339,
3280 | 3340,
3281 | 3341,
3282 | 3342,
3283 | 3343,
3284 | 3344,
3285 | 3345,
3286 | 3346,
3287 | 3347,
3288 | 3348,
3289 | 3349,
3290 | 3350,
3291 | 3351,
3292 | 3352,
3293 | 3353,
3294 | 3354,
3295 | 3355,
3296 | 3356,
3297 | 3357,
3298 | 3358,
3299 | 3359,
3300 | 3360,
3301 | 3361,
3302 | 3362,
3303 | 3363,
3304 | 3364,
3305 | 3365,
3306 | 3366,
3307 | 3367,
3308 | 3368,
3309 | 3369,
3310 | 3370,
3311 | 3371,
3312 | 3372,
3313 | 3373,
3314 | 3374,
3315 | 3375,
3316 | 3376,
3317 | 3377,
3318 | 3378,
3319 | 3379,
3320 | 3380,
3321 | 3381
3322 | ];
3323 | _planets[14] = [
3324 | 3382,
3325 | 3383,
3326 | 3384,
3327 | 3385,
3328 | 3386,
3329 | 3387,
3330 | 3388,
3331 | 3389,
3332 | 3390,
3333 | 3391,
3334 | 3392,
3335 | 3393,
3336 | 3394,
3337 | 3395,
3338 | 3396,
3339 | 3397,
3340 | 3398,
3341 | 3399,
3342 | 3400,
3343 | 3401,
3344 | 3402,
3345 | 3403,
3346 | 3404,
3347 | 3405,
3348 | 3406,
3349 | 3407,
3350 | 3408,
3351 | 3409,
3352 | 3410,
3353 | 3411,
3354 | 3412,
3355 | 3413,
3356 | 3414,
3357 | 3415,
3358 | 3416,
3359 | 3417,
3360 | 3418,
3361 | 3419,
3362 | 3420,
3363 | 3421,
3364 | 3422,
3365 | 3423,
3366 | 3424,
3367 | 3425,
3368 | 3426,
3369 | 3427,
3370 | 3428,
3371 | 3429,
3372 | 3430,
3373 | 3431,
3374 | 3432,
3375 | 3433,
3376 | 3434,
3377 | 3435,
3378 | 3436,
3379 | 3437,
3380 | 3438,
3381 | 3439,
3382 | 3440,
3383 | 3441,
3384 | 3442,
3385 | 3443,
3386 | 3444,
3387 | 3445,
3388 | 3446,
3389 | 3447,
3390 | 3448,
3391 | 3449,
3392 | 3450,
3393 | 3451,
3394 | 3452,
3395 | 3453,
3396 | 3454,
3397 | 3455,
3398 | 3456,
3399 | 3457,
3400 | 3458,
3401 | 3459,
3402 | 3460,
3403 | 3461,
3404 | 3462,
3405 | 3463,
3406 | 3464,
3407 | 3465,
3408 | 3466,
3409 | 3467,
3410 | 3468,
3411 | 3469,
3412 | 3470,
3413 | 3471,
3414 | 3472,
3415 | 3473,
3416 | 3474,
3417 | 3475,
3418 | 3476,
3419 | 3477,
3420 | 3478,
3421 | 3479,
3422 | 3480,
3423 | 3481,
3424 | 3482,
3425 | 3483,
3426 | 3484,
3427 | 3485,
3428 | 3486,
3429 | 3487,
3430 | 3488,
3431 | 3489,
3432 | 3490,
3433 | 3491,
3434 | 3492,
3435 | 3493,
3436 | 3494,
3437 | 3495,
3438 | 3496,
3439 | 3497,
3440 | 3498,
3441 | 3499,
3442 | 3500,
3443 | 3501,
3444 | 3502,
3445 | 3503,
3446 | 3504,
3447 | 3505,
3448 | 3506,
3449 | 3507,
3450 | 3508,
3451 | 3509,
3452 | 3510,
3453 | 3511,
3454 | 3512,
3455 | 3513,
3456 | 3514,
3457 | 3515,
3458 | 3516,
3459 | 3517,
3460 | 3518,
3461 | 3519,
3462 | 3520,
3463 | 3521,
3464 | 3522,
3465 | 3523,
3466 | 3524,
3467 | 3525,
3468 | 3526,
3469 | 3527,
3470 | 3528,
3471 | 3529,
3472 | 3530,
3473 | 3531,
3474 | 3532,
3475 | 3533,
3476 | 3534,
3477 | 3535,
3478 | 3536,
3479 | 3537,
3480 | 3538,
3481 | 3539,
3482 | 3540,
3483 | 3541,
3484 | 3542,
3485 | 3543,
3486 | 3544,
3487 | 3545,
3488 | 3546,
3489 | 3547,
3490 | 3548,
3491 | 3549,
3492 | 3550,
3493 | 3551,
3494 | 3552,
3495 | 3553,
3496 | 3554,
3497 | 3555,
3498 | 3556,
3499 | 3557,
3500 | 3558,
3501 | 3559,
3502 | 3560,
3503 | 3561,
3504 | 3562,
3505 | 3563,
3506 | 3564,
3507 | 3565,
3508 | 3566,
3509 | 3567,
3510 | 3568,
3511 | 3569,
3512 | 3570,
3513 | 3571,
3514 | 3572,
3515 | 3573,
3516 | 3574,
3517 | 3575,
3518 | 3576,
3519 | 3577,
3520 | 3578,
3521 | 3579,
3522 | 3580,
3523 | 3581,
3524 | 3582,
3525 | 3583,
3526 | 3584,
3527 | 3585,
3528 | 3586,
3529 | 3587,
3530 | 3588,
3531 | 3589,
3532 | 3590,
3533 | 3591,
3534 | 3592,
3535 | 3593,
3536 | 3594,
3537 | 3595,
3538 | 3596,
3539 | 3597,
3540 | 3598,
3541 | 3599,
3542 | 3600,
3543 | 3601,
3544 | 3602,
3545 | 3603,
3546 | 3604,
3547 | 3605,
3548 | 3606,
3549 | 3607,
3550 | 3608,
3551 | 3609,
3552 | 3610,
3553 | 3611,
3554 | 3612,
3555 | 3613,
3556 | 3614,
3557 | 3615,
3558 | 3616,
3559 | 3617,
3560 | 3618,
3561 | 3619,
3562 | 3620,
3563 | 3621,
3564 | 3622,
3565 | 3623,
3566 | 3624,
3567 | 3625,
3568 | 3626,
3569 | 3627,
3570 | 3628,
3571 | 3629,
3572 | 3630,
3573 | 3631,
3574 | 3632,
3575 | 3633
3576 | ];
3577 | _planets[15] = [
3578 | 3634,
3579 | 3635,
3580 | 3636,
3581 | 3637,
3582 | 3638,
3583 | 3639,
3584 | 3640,
3585 | 3641,
3586 | 3642,
3587 | 3643,
3588 | 3644,
3589 | 3645,
3590 | 3646,
3591 | 3647,
3592 | 3648,
3593 | 3649,
3594 | 3650,
3595 | 3651,
3596 | 3652,
3597 | 3653,
3598 | 3654,
3599 | 3655,
3600 | 3656,
3601 | 3657,
3602 | 3658,
3603 | 3659,
3604 | 3660,
3605 | 3661,
3606 | 3662,
3607 | 3663,
3608 | 3664,
3609 | 3665,
3610 | 3666,
3611 | 3667,
3612 | 3668,
3613 | 3669,
3614 | 3670,
3615 | 3671,
3616 | 3672,
3617 | 3673,
3618 | 3674,
3619 | 3675,
3620 | 3676,
3621 | 3677,
3622 | 3678,
3623 | 3679,
3624 | 3680,
3625 | 3681,
3626 | 3682,
3627 | 3683,
3628 | 3684,
3629 | 3685,
3630 | 3686,
3631 | 3687,
3632 | 3688,
3633 | 3689,
3634 | 3690,
3635 | 3691,
3636 | 3692,
3637 | 3693,
3638 | 3694,
3639 | 3695,
3640 | 3696,
3641 | 3697,
3642 | 3698,
3643 | 3699,
3644 | 3700,
3645 | 3701,
3646 | 3702,
3647 | 3703,
3648 | 3704,
3649 | 3705,
3650 | 3706,
3651 | 3707,
3652 | 3708,
3653 | 3709,
3654 | 3710,
3655 | 3711,
3656 | 3712,
3657 | 3713,
3658 | 3714,
3659 | 3715,
3660 | 3716,
3661 | 3717,
3662 | 3718,
3663 | 3719,
3664 | 3720,
3665 | 3721,
3666 | 3722,
3667 | 3723,
3668 | 3724,
3669 | 3725,
3670 | 3726,
3671 | 3727,
3672 | 3728,
3673 | 3729,
3674 | 3730,
3675 | 3731,
3676 | 3732,
3677 | 3733,
3678 | 3734,
3679 | 3735,
3680 | 3736,
3681 | 3737,
3682 | 3738,
3683 | 3739,
3684 | 3740,
3685 | 3741,
3686 | 3742,
3687 | 3743,
3688 | 3744,
3689 | 3745,
3690 | 3746,
3691 | 3747,
3692 | 3748,
3693 | 3749,
3694 | 3750,
3695 | 3751,
3696 | 3752,
3697 | 3753,
3698 | 3754,
3699 | 3755,
3700 | 3756,
3701 | 3757,
3702 | 3758,
3703 | 3759,
3704 | 3760,
3705 | 3761,
3706 | 3762,
3707 | 3763,
3708 | 3764,
3709 | 3765,
3710 | 3766,
3711 | 3767,
3712 | 3768,
3713 | 3769,
3714 | 3770,
3715 | 3771,
3716 | 3772,
3717 | 3773,
3718 | 3774,
3719 | 3775,
3720 | 3776,
3721 | 3777,
3722 | 3778,
3723 | 3779,
3724 | 3780,
3725 | 3781,
3726 | 3782,
3727 | 3783,
3728 | 3784,
3729 | 3785,
3730 | 3786,
3731 | 3787,
3732 | 3788,
3733 | 3789,
3734 | 3790,
3735 | 3791,
3736 | 3792,
3737 | 3793,
3738 | 3794,
3739 | 3795,
3740 | 3796,
3741 | 3797,
3742 | 3798,
3743 | 3799,
3744 | 3800,
3745 | 3801,
3746 | 3802,
3747 | 3803,
3748 | 3804,
3749 | 3805,
3750 | 3806,
3751 | 3807,
3752 | 3808,
3753 | 3809,
3754 | 3810,
3755 | 3811,
3756 | 3812,
3757 | 3813,
3758 | 3814,
3759 | 3815,
3760 | 3816,
3761 | 3817,
3762 | 3818,
3763 | 3819,
3764 | 3820,
3765 | 3821,
3766 | 3822,
3767 | 3823,
3768 | 3824,
3769 | 3825,
3770 | 3826,
3771 | 3827,
3772 | 3828,
3773 | 3829,
3774 | 3830,
3775 | 3831,
3776 | 3832,
3777 | 3833,
3778 | 3834,
3779 | 3835,
3780 | 3836,
3781 | 3837,
3782 | 3838,
3783 | 3839,
3784 | 3840,
3785 | 3841,
3786 | 3842,
3787 | 3843,
3788 | 3844,
3789 | 3845,
3790 | 3846,
3791 | 3847,
3792 | 3848,
3793 | 3849,
3794 | 3850,
3795 | 3851,
3796 | 3852,
3797 | 3853,
3798 | 3854,
3799 | 3855,
3800 | 3856,
3801 | 3857,
3802 | 3858,
3803 | 3859,
3804 | 3860,
3805 | 3861,
3806 | 3862,
3807 | 3863,
3808 | 3864,
3809 | 3865,
3810 | 3866,
3811 | 3867,
3812 | 3868,
3813 | 3869,
3814 | 3870,
3815 | 3871,
3816 | 3872,
3817 | 3873,
3818 | 3874,
3819 | 3875,
3820 | 3876,
3821 | 3877,
3822 | 3878,
3823 | 3879,
3824 | 3880,
3825 | 3881,
3826 | 3882,
3827 | 3883,
3828 | 3884,
3829 | 3885
3830 | ];
3831 | _planets[16] = [
3832 | 3886,
3833 | 3887,
3834 | 3888,
3835 | 3889,
3836 | 3890,
3837 | 3891,
3838 | 3892,
3839 | 3893,
3840 | 3894,
3841 | 3895,
3842 | 3896,
3843 | 3897,
3844 | 3898,
3845 | 3899,
3846 | 3900,
3847 | 3901,
3848 | 3902,
3849 | 3903,
3850 | 3904,
3851 | 3905,
3852 | 3906,
3853 | 3907,
3854 | 3908,
3855 | 3909,
3856 | 3910,
3857 | 3911,
3858 | 3912,
3859 | 3913,
3860 | 3914,
3861 | 3915,
3862 | 3916,
3863 | 3917,
3864 | 3918,
3865 | 3919,
3866 | 3920,
3867 | 3921,
3868 | 3922,
3869 | 3923,
3870 | 3924,
3871 | 3925,
3872 | 3926,
3873 | 3927,
3874 | 3928,
3875 | 3929,
3876 | 3930,
3877 | 3931,
3878 | 3932,
3879 | 3933,
3880 | 3934,
3881 | 3935,
3882 | 3936,
3883 | 3937,
3884 | 3938,
3885 | 3939,
3886 | 3940,
3887 | 3941,
3888 | 3942,
3889 | 3943,
3890 | 3944,
3891 | 3945,
3892 | 3946,
3893 | 3947,
3894 | 3948,
3895 | 3949,
3896 | 3950,
3897 | 3951,
3898 | 3952,
3899 | 3953,
3900 | 3954,
3901 | 3955,
3902 | 3956,
3903 | 3957,
3904 | 3958,
3905 | 3959,
3906 | 3960,
3907 | 3961,
3908 | 3962,
3909 | 3963,
3910 | 3964,
3911 | 3965,
3912 | 3966,
3913 | 3967,
3914 | 3968,
3915 | 3969,
3916 | 3970,
3917 | 3971,
3918 | 3972,
3919 | 3973,
3920 | 3974,
3921 | 3975,
3922 | 3976,
3923 | 3977,
3924 | 3978,
3925 | 3979,
3926 | 3980,
3927 | 3981,
3928 | 3982,
3929 | 3983,
3930 | 3984,
3931 | 3985,
3932 | 3986,
3933 | 3987,
3934 | 3988,
3935 | 3989,
3936 | 3990,
3937 | 3991,
3938 | 3992,
3939 | 3993,
3940 | 3994,
3941 | 3995,
3942 | 3996,
3943 | 3997,
3944 | 3998,
3945 | 3999,
3946 | 4000,
3947 | 4001,
3948 | 4002,
3949 | 4003,
3950 | 4004,
3951 | 4005,
3952 | 4006,
3953 | 4007,
3954 | 4008,
3955 | 4009,
3956 | 4010,
3957 | 4011,
3958 | 4012,
3959 | 4013,
3960 | 4014,
3961 | 4015,
3962 | 4016,
3963 | 4017,
3964 | 4018,
3965 | 4019,
3966 | 4020,
3967 | 4021,
3968 | 4022,
3969 | 4023,
3970 | 4024,
3971 | 4025,
3972 | 4026,
3973 | 4027,
3974 | 4028,
3975 | 4029,
3976 | 4030,
3977 | 4031,
3978 | 4032,
3979 | 4033,
3980 | 4034,
3981 | 4035,
3982 | 4036,
3983 | 4037,
3984 | 4038,
3985 | 4039,
3986 | 4040,
3987 | 4041,
3988 | 4042,
3989 | 4043,
3990 | 4044,
3991 | 4045,
3992 | 4046,
3993 | 4047,
3994 | 4048,
3995 | 4049,
3996 | 4050,
3997 | 4051,
3998 | 4052,
3999 | 4053,
4000 | 4054,
4001 | 4055,
4002 | 4056,
4003 | 4057,
4004 | 4058,
4005 | 4059,
4006 | 4060,
4007 | 4061,
4008 | 4062,
4009 | 4063,
4010 | 4064,
4011 | 4065,
4012 | 4066,
4013 | 4067,
4014 | 4068,
4015 | 4069,
4016 | 4070,
4017 | 4071,
4018 | 4072,
4019 | 4073,
4020 | 4074,
4021 | 4075,
4022 | 4076,
4023 | 4077,
4024 | 4078,
4025 | 4079,
4026 | 4080,
4027 | 4081,
4028 | 4082,
4029 | 4083,
4030 | 4084,
4031 | 4085,
4032 | 4086,
4033 | 4087,
4034 | 4088,
4035 | 4089,
4036 | 4090,
4037 | 4091,
4038 | 4092,
4039 | 4093,
4040 | 4094,
4041 | 4095,
4042 | 4096,
4043 | 4097,
4044 | 4098,
4045 | 4099,
4046 | 4100,
4047 | 4101,
4048 | 4102,
4049 | 4103,
4050 | 4104,
4051 | 4105,
4052 | 4106,
4053 | 4107,
4054 | 4108,
4055 | 4109,
4056 | 4110,
4057 | 4111,
4058 | 4112,
4059 | 4113,
4060 | 4114,
4061 | 4115,
4062 | 4116,
4063 | 4117,
4064 | 4118,
4065 | 4119,
4066 | 4120,
4067 | 4121,
4068 | 4122,
4069 | 4123,
4070 | 4124,
4071 | 4125,
4072 | 4126,
4073 | 4127,
4074 | 4128,
4075 | 4129,
4076 | 4130,
4077 | 4131,
4078 | 4132,
4079 | 4133,
4080 | 4134,
4081 | 4135,
4082 | 4136,
4083 | 4137
4084 | ];
4085 | _planets[17] = [
4086 | 4138,
4087 | 4139,
4088 | 4140,
4089 | 4141,
4090 | 4142,
4091 | 4143,
4092 | 4144,
4093 | 4145,
4094 | 4146,
4095 | 4147,
4096 | 4148,
4097 | 4149,
4098 | 4150,
4099 | 4151,
4100 | 4152,
4101 | 4153,
4102 | 4154,
4103 | 4155,
4104 | 4156,
4105 | 4157,
4106 | 4158,
4107 | 4159,
4108 | 4160,
4109 | 4161,
4110 | 4162,
4111 | 4163,
4112 | 4164,
4113 | 4165,
4114 | 4166,
4115 | 4167,
4116 | 4168,
4117 | 4169,
4118 | 4170,
4119 | 4171,
4120 | 4172,
4121 | 4173,
4122 | 4174,
4123 | 4175,
4124 | 4176,
4125 | 4177,
4126 | 4178,
4127 | 4179,
4128 | 4180,
4129 | 4181,
4130 | 4182,
4131 | 4183,
4132 | 4184,
4133 | 4185,
4134 | 4186,
4135 | 4187,
4136 | 4188,
4137 | 4189,
4138 | 4190,
4139 | 4191,
4140 | 4192,
4141 | 4193,
4142 | 4194,
4143 | 4195,
4144 | 4196,
4145 | 4197,
4146 | 4198,
4147 | 4199,
4148 | 4200,
4149 | 4201,
4150 | 4202,
4151 | 4203,
4152 | 4204,
4153 | 4205,
4154 | 4206,
4155 | 4207,
4156 | 4208,
4157 | 4209,
4158 | 4210,
4159 | 4211,
4160 | 4212,
4161 | 4213,
4162 | 4214,
4163 | 4215,
4164 | 4216,
4165 | 4217,
4166 | 4218,
4167 | 4219,
4168 | 4220,
4169 | 4221,
4170 | 4222,
4171 | 4223,
4172 | 4224,
4173 | 4225,
4174 | 4226,
4175 | 4227,
4176 | 4228,
4177 | 4229,
4178 | 4230,
4179 | 4231,
4180 | 4232,
4181 | 4233,
4182 | 4234,
4183 | 4235,
4184 | 4236,
4185 | 4237,
4186 | 4238,
4187 | 4239,
4188 | 4240,
4189 | 4241,
4190 | 4242,
4191 | 4243,
4192 | 4244,
4193 | 4245,
4194 | 4246,
4195 | 4247,
4196 | 4248,
4197 | 4249,
4198 | 4250,
4199 | 4251,
4200 | 4252,
4201 | 4253,
4202 | 4254,
4203 | 4255,
4204 | 4256,
4205 | 4257,
4206 | 4258,
4207 | 4259,
4208 | 4260,
4209 | 4261,
4210 | 4262,
4211 | 4263,
4212 | 4264,
4213 | 4265,
4214 | 4266,
4215 | 4267,
4216 | 4268,
4217 | 4269,
4218 | 4270,
4219 | 4271,
4220 | 4272,
4221 | 4273,
4222 | 4274,
4223 | 4275,
4224 | 4276,
4225 | 4277,
4226 | 4278,
4227 | 4279,
4228 | 4280,
4229 | 4281,
4230 | 4282,
4231 | 4283,
4232 | 4284,
4233 | 4285,
4234 | 4286,
4235 | 4287,
4236 | 4288,
4237 | 4289,
4238 | 4290,
4239 | 4291,
4240 | 4292,
4241 | 4293,
4242 | 4294,
4243 | 4295,
4244 | 4296,
4245 | 4297,
4246 | 4298,
4247 | 4299,
4248 | 4300,
4249 | 4301,
4250 | 4302,
4251 | 4303,
4252 | 4304,
4253 | 4305,
4254 | 4306,
4255 | 4307,
4256 | 4308,
4257 | 4309,
4258 | 4310,
4259 | 4311,
4260 | 4312,
4261 | 4313,
4262 | 4314,
4263 | 4315,
4264 | 4316,
4265 | 4317,
4266 | 4318,
4267 | 4319,
4268 | 4320,
4269 | 4321,
4270 | 4322,
4271 | 4323,
4272 | 4324,
4273 | 4325,
4274 | 4326,
4275 | 4327,
4276 | 4328,
4277 | 4329,
4278 | 4330,
4279 | 4331,
4280 | 4332,
4281 | 4333,
4282 | 4334,
4283 | 4335,
4284 | 4336,
4285 | 4337,
4286 | 4338,
4287 | 4339,
4288 | 4340,
4289 | 4341,
4290 | 4342,
4291 | 4343,
4292 | 4344,
4293 | 4345,
4294 | 4346,
4295 | 4347,
4296 | 4348,
4297 | 4349,
4298 | 4350,
4299 | 4351,
4300 | 4352,
4301 | 4353,
4302 | 4354,
4303 | 4355,
4304 | 4356,
4305 | 4357,
4306 | 4358,
4307 | 4359,
4308 | 4360,
4309 | 4361,
4310 | 4362,
4311 | 4363,
4312 | 4364,
4313 | 4365,
4314 | 4366,
4315 | 4367,
4316 | 4368,
4317 | 4369,
4318 | 4370,
4319 | 4371,
4320 | 4372,
4321 | 4373,
4322 | 4374,
4323 | 4375,
4324 | 4376,
4325 | 4377,
4326 | 4378,
4327 | 4379,
4328 | 4380,
4329 | 4381,
4330 | 4382,
4331 | 4383,
4332 | 4384,
4333 | 4385,
4334 | 4386,
4335 | 4387,
4336 | 4388,
4337 | 4389
4338 | ];
4339 | _planets[18] = [
4340 | 4390,
4341 | 4391,
4342 | 4392,
4343 | 4393,
4344 | 4394,
4345 | 4395,
4346 | 4396,
4347 | 4397,
4348 | 4398,
4349 | 4399,
4350 | 4400,
4351 | 4401,
4352 | 4402,
4353 | 4403,
4354 | 4404,
4355 | 4405,
4356 | 4406,
4357 | 4407,
4358 | 4408,
4359 | 4409,
4360 | 4410,
4361 | 4411,
4362 | 4412,
4363 | 4413,
4364 | 4414,
4365 | 4415,
4366 | 4416,
4367 | 4417,
4368 | 4418,
4369 | 4419,
4370 | 4420,
4371 | 4421,
4372 | 4422,
4373 | 4423,
4374 | 4424,
4375 | 4425,
4376 | 4426,
4377 | 4427,
4378 | 4428,
4379 | 4429,
4380 | 4430,
4381 | 4431,
4382 | 4432,
4383 | 4433,
4384 | 4434,
4385 | 4435,
4386 | 4436,
4387 | 4437,
4388 | 4438,
4389 | 4439,
4390 | 4440,
4391 | 4441,
4392 | 4442,
4393 | 4443,
4394 | 4444,
4395 | 4445,
4396 | 4446,
4397 | 4447,
4398 | 4448,
4399 | 4449,
4400 | 4450,
4401 | 4451,
4402 | 4452,
4403 | 4453,
4404 | 4454,
4405 | 4455,
4406 | 4456,
4407 | 4457,
4408 | 4458,
4409 | 4459,
4410 | 4460,
4411 | 4461,
4412 | 4462,
4413 | 4463,
4414 | 4464,
4415 | 4465,
4416 | 4466,
4417 | 4467,
4418 | 4468,
4419 | 4469,
4420 | 4470,
4421 | 4471,
4422 | 4472,
4423 | 4473,
4424 | 4474,
4425 | 4475,
4426 | 4476,
4427 | 4477,
4428 | 4478,
4429 | 4479,
4430 | 4480,
4431 | 4481,
4432 | 4482,
4433 | 4483,
4434 | 4484,
4435 | 4485,
4436 | 4486,
4437 | 4487,
4438 | 4488,
4439 | 4489,
4440 | 4490,
4441 | 4491,
4442 | 4492,
4443 | 4493,
4444 | 4494,
4445 | 4495,
4446 | 4496,
4447 | 4497,
4448 | 4498,
4449 | 4499,
4450 | 4500,
4451 | 4501,
4452 | 4502,
4453 | 4503,
4454 | 4504,
4455 | 4505,
4456 | 4506,
4457 | 4507,
4458 | 4508,
4459 | 4509,
4460 | 4510,
4461 | 4511,
4462 | 4512,
4463 | 4513,
4464 | 4514,
4465 | 4515,
4466 | 4516,
4467 | 4517,
4468 | 4518,
4469 | 4519,
4470 | 4520,
4471 | 4521,
4472 | 4522,
4473 | 4523,
4474 | 4524,
4475 | 4525,
4476 | 4526,
4477 | 4527,
4478 | 4528,
4479 | 4529,
4480 | 4530,
4481 | 4531,
4482 | 4532,
4483 | 4533,
4484 | 4534,
4485 | 4535,
4486 | 4536,
4487 | 4537,
4488 | 4538,
4489 | 4539,
4490 | 4540,
4491 | 4541,
4492 | 4542,
4493 | 4543,
4494 | 4544,
4495 | 4545,
4496 | 4546,
4497 | 4547,
4498 | 4548,
4499 | 4549,
4500 | 4550,
4501 | 4551,
4502 | 4552,
4503 | 4553,
4504 | 4554,
4505 | 4555,
4506 | 4556,
4507 | 4557,
4508 | 4558,
4509 | 4559,
4510 | 4560,
4511 | 4561,
4512 | 4562,
4513 | 4563,
4514 | 4564,
4515 | 4565,
4516 | 4566,
4517 | 4567,
4518 | 4568,
4519 | 4569,
4520 | 4570,
4521 | 4571,
4522 | 4572,
4523 | 4573,
4524 | 4574,
4525 | 4575,
4526 | 4576,
4527 | 4577,
4528 | 4578,
4529 | 4579,
4530 | 4580,
4531 | 4581,
4532 | 4582,
4533 | 4583,
4534 | 4584,
4535 | 4585,
4536 | 4586,
4537 | 4587,
4538 | 4588,
4539 | 4589,
4540 | 4590,
4541 | 4591,
4542 | 4592,
4543 | 4593,
4544 | 4594,
4545 | 4595,
4546 | 4596,
4547 | 4597,
4548 | 4598,
4549 | 4599,
4550 | 4600,
4551 | 4601,
4552 | 4602,
4553 | 4603,
4554 | 4604,
4555 | 4605,
4556 | 4606,
4557 | 4607,
4558 | 4608,
4559 | 4609,
4560 | 4610,
4561 | 4611,
4562 | 4612,
4563 | 4613,
4564 | 4614,
4565 | 4615,
4566 | 4616,
4567 | 4617,
4568 | 4618,
4569 | 4619,
4570 | 4620,
4571 | 4621,
4572 | 4622,
4573 | 4623,
4574 | 4624,
4575 | 4625,
4576 | 4626,
4577 | 4627,
4578 | 4628,
4579 | 4629,
4580 | 4630,
4581 | 4631,
4582 | 4632,
4583 | 4633,
4584 | 4634,
4585 | 4635,
4586 | 4636,
4587 | 4637,
4588 | 4638,
4589 | 4639,
4590 | 4640,
4591 | 4641
4592 | ];
4593 | _planets[19] = [
4594 | 4642,
4595 | 4643,
4596 | 4644,
4597 | 4645,
4598 | 4646,
4599 | 4647,
4600 | 4648,
4601 | 4649,
4602 | 4650,
4603 | 4651,
4604 | 4652,
4605 | 4653,
4606 | 4654,
4607 | 4655,
4608 | 4656,
4609 | 4657,
4610 | 4658,
4611 | 4659,
4612 | 4660,
4613 | 4661,
4614 | 4662,
4615 | 4663,
4616 | 4664,
4617 | 4665,
4618 | 4666,
4619 | 4667,
4620 | 4668,
4621 | 4669,
4622 | 4670,
4623 | 4671,
4624 | 4672,
4625 | 4673,
4626 | 4674,
4627 | 4675,
4628 | 4676,
4629 | 4677,
4630 | 4678,
4631 | 4679,
4632 | 4680,
4633 | 4681,
4634 | 4682,
4635 | 4683,
4636 | 4684,
4637 | 4685,
4638 | 4686,
4639 | 4687,
4640 | 4688,
4641 | 4689,
4642 | 4690,
4643 | 4691,
4644 | 4692,
4645 | 4693,
4646 | 4694,
4647 | 4695,
4648 | 4696,
4649 | 4697,
4650 | 4698,
4651 | 4699,
4652 | 4700,
4653 | 4701,
4654 | 4702,
4655 | 4703,
4656 | 4704,
4657 | 4705,
4658 | 4706,
4659 | 4707,
4660 | 4708,
4661 | 4709,
4662 | 4710,
4663 | 4711,
4664 | 4712,
4665 | 4713,
4666 | 4714,
4667 | 4715,
4668 | 4716,
4669 | 4717,
4670 | 4718,
4671 | 4719,
4672 | 4720,
4673 | 4721,
4674 | 4722,
4675 | 4723,
4676 | 4724,
4677 | 4725,
4678 | 4726,
4679 | 4727,
4680 | 4728,
4681 | 4729,
4682 | 4730,
4683 | 4731,
4684 | 4732,
4685 | 4733,
4686 | 4734,
4687 | 4735,
4688 | 4736,
4689 | 4737,
4690 | 4738,
4691 | 4739,
4692 | 4740,
4693 | 4741,
4694 | 4742,
4695 | 4743,
4696 | 4744,
4697 | 4745,
4698 | 4746,
4699 | 4747,
4700 | 4748,
4701 | 4749,
4702 | 4750,
4703 | 4751,
4704 | 4752,
4705 | 4753,
4706 | 4754,
4707 | 4755,
4708 | 4756,
4709 | 4757,
4710 | 4758,
4711 | 4759,
4712 | 4760,
4713 | 4761,
4714 | 4762,
4715 | 4763,
4716 | 4764,
4717 | 4765,
4718 | 4766,
4719 | 4767,
4720 | 4768,
4721 | 4769,
4722 | 4770,
4723 | 4771,
4724 | 4772,
4725 | 4773,
4726 | 4774,
4727 | 4775,
4728 | 4776,
4729 | 4777,
4730 | 4778,
4731 | 4779,
4732 | 4780,
4733 | 4781,
4734 | 4782,
4735 | 4783,
4736 | 4784,
4737 | 4785,
4738 | 4786,
4739 | 4787,
4740 | 4788,
4741 | 4789,
4742 | 4790,
4743 | 4791,
4744 | 4792,
4745 | 4793,
4746 | 4794,
4747 | 4795,
4748 | 4796,
4749 | 4797,
4750 | 4798,
4751 | 4799,
4752 | 4800,
4753 | 4801,
4754 | 4802,
4755 | 4803,
4756 | 4804,
4757 | 4805,
4758 | 4806,
4759 | 4807,
4760 | 4808,
4761 | 4809,
4762 | 4810,
4763 | 4811,
4764 | 4812,
4765 | 4813,
4766 | 4814,
4767 | 4815,
4768 | 4816,
4769 | 4817,
4770 | 4818,
4771 | 4819,
4772 | 4820,
4773 | 4821,
4774 | 4822,
4775 | 4823,
4776 | 4824,
4777 | 4825,
4778 | 4826,
4779 | 4827,
4780 | 4828,
4781 | 4829,
4782 | 4830,
4783 | 4831,
4784 | 4832,
4785 | 4833,
4786 | 4834,
4787 | 4835,
4788 | 4836,
4789 | 4837,
4790 | 4838,
4791 | 4839,
4792 | 4840,
4793 | 4841,
4794 | 4842,
4795 | 4843,
4796 | 4844,
4797 | 4845,
4798 | 4846,
4799 | 4847,
4800 | 4848,
4801 | 4849,
4802 | 4850,
4803 | 4851,
4804 | 4852,
4805 | 4853,
4806 | 4854,
4807 | 4855,
4808 | 4856,
4809 | 4857,
4810 | 4858,
4811 | 4859,
4812 | 4860,
4813 | 4861,
4814 | 4862,
4815 | 4863,
4816 | 4864,
4817 | 4865,
4818 | 4866,
4819 | 4867,
4820 | 4868,
4821 | 4869,
4822 | 4870,
4823 | 4871,
4824 | 4872,
4825 | 4873,
4826 | 4874,
4827 | 4875,
4828 | 4876,
4829 | 4877,
4830 | 4878,
4831 | 4879,
4832 | 4880,
4833 | 4881,
4834 | 4882,
4835 | 4883,
4836 | 4884,
4837 | 4885,
4838 | 4886,
4839 | 4887,
4840 | 4888,
4841 | 4889,
4842 | 4890,
4843 | 4891,
4844 | 4892,
4845 | 4893
4846 | ];
4847 | _planets[20] = [
4848 | 4894,
4849 | 4895,
4850 | 4896,
4851 | 4897,
4852 | 4898,
4853 | 4899,
4854 | 4900,
4855 | 4901,
4856 | 4902,
4857 | 4903,
4858 | 4904,
4859 | 4905,
4860 | 4906,
4861 | 4907,
4862 | 4908,
4863 | 4909,
4864 | 4910,
4865 | 4911,
4866 | 4912,
4867 | 4913,
4868 | 4914,
4869 | 4915,
4870 | 4916,
4871 | 4917,
4872 | 4918,
4873 | 4919,
4874 | 4920,
4875 | 4921,
4876 | 4922,
4877 | 4923,
4878 | 4924,
4879 | 4925,
4880 | 4926,
4881 | 4927,
4882 | 4928,
4883 | 4929,
4884 | 4930,
4885 | 4931,
4886 | 4932,
4887 | 4933,
4888 | 4934,
4889 | 4935,
4890 | 4936,
4891 | 4937,
4892 | 4938,
4893 | 4939,
4894 | 4940,
4895 | 4941,
4896 | 4942,
4897 | 4943,
4898 | 4944,
4899 | 4945,
4900 | 4946,
4901 | 4947,
4902 | 4948,
4903 | 4949,
4904 | 4950,
4905 | 4951,
4906 | 4952,
4907 | 4953,
4908 | 4954,
4909 | 4955,
4910 | 4956,
4911 | 4957,
4912 | 4958,
4913 | 4959,
4914 | 4960,
4915 | 4961,
4916 | 4962,
4917 | 4963,
4918 | 4964,
4919 | 4965,
4920 | 4966,
4921 | 4967,
4922 | 4968,
4923 | 4969,
4924 | 4970,
4925 | 4971,
4926 | 4972,
4927 | 4973,
4928 | 4974,
4929 | 4975,
4930 | 4976,
4931 | 4977,
4932 | 4978,
4933 | 4979,
4934 | 4980,
4935 | 4981,
4936 | 4982,
4937 | 4983,
4938 | 4984,
4939 | 4985,
4940 | 4986,
4941 | 4987,
4942 | 4988,
4943 | 4989,
4944 | 4990,
4945 | 4991,
4946 | 4992,
4947 | 4993,
4948 | 4994,
4949 | 4995,
4950 | 4996,
4951 | 4997,
4952 | 4998,
4953 | 4999,
4954 | 5000,
4955 | 5001,
4956 | 5002,
4957 | 5003,
4958 | 5004,
4959 | 5005,
4960 | 5006,
4961 | 5007,
4962 | 5008,
4963 | 5009,
4964 | 5010,
4965 | 5011,
4966 | 5012,
4967 | 5013,
4968 | 5014,
4969 | 5015,
4970 | 5016,
4971 | 5017,
4972 | 5018,
4973 | 5019,
4974 | 5020,
4975 | 5021,
4976 | 5022,
4977 | 5023,
4978 | 5024,
4979 | 5025,
4980 | 5026,
4981 | 5027,
4982 | 5028,
4983 | 5029,
4984 | 5030,
4985 | 5031,
4986 | 5032,
4987 | 5033,
4988 | 5034,
4989 | 5035,
4990 | 5036,
4991 | 5037,
4992 | 5038,
4993 | 5039,
4994 | 5040,
4995 | 5041,
4996 | 5042,
4997 | 5043,
4998 | 5044,
4999 | 5045,
5000 | 5046,
5001 | 5047,
5002 | 5048,
5003 | 5049,
5004 | 5050,
5005 | 5051,
5006 | 5052,
5007 | 5053,
5008 | 5054,
5009 | 5055,
5010 | 5056,
5011 | 5057,
5012 | 5058,
5013 | 5059,
5014 | 5060,
5015 | 5061,
5016 | 5062,
5017 | 5063,
5018 | 5064,
5019 | 5065,
5020 | 5066,
5021 | 5067,
5022 | 5068,
5023 | 5069,
5024 | 5070,
5025 | 5071,
5026 | 5072,
5027 | 5073,
5028 | 5074,
5029 | 5075,
5030 | 5076,
5031 | 5077,
5032 | 5078,
5033 | 5079,
5034 | 5080,
5035 | 5081,
5036 | 5082,
5037 | 5083,
5038 | 5084,
5039 | 5085,
5040 | 5086,
5041 | 5087,
5042 | 5088,
5043 | 5089,
5044 | 5090,
5045 | 5091,
5046 | 5092,
5047 | 5093,
5048 | 5094,
5049 | 5095,
5050 | 5096,
5051 | 5097,
5052 | 5098,
5053 | 5099,
5054 | 5100,
5055 | 5101,
5056 | 5102,
5057 | 5103,
5058 | 5104,
5059 | 5105,
5060 | 5106,
5061 | 5107,
5062 | 5108,
5063 | 5109,
5064 | 5110,
5065 | 5111,
5066 | 5112,
5067 | 5113,
5068 | 5114,
5069 | 5115,
5070 | 5116,
5071 | 5117,
5072 | 5118,
5073 | 5119,
5074 | 5120,
5075 | 5121,
5076 | 5122,
5077 | 5123,
5078 | 5124,
5079 | 5125,
5080 | 5126,
5081 | 5127,
5082 | 5128,
5083 | 5129,
5084 | 5130,
5085 | 5131,
5086 | 5132,
5087 | 5133,
5088 | 5134,
5089 | 5135,
5090 | 5136,
5091 | 5137,
5092 | 5138,
5093 | 5139,
5094 | 5140,
5095 | 5141,
5096 | 5142,
5097 | 5143,
5098 | 5144,
5099 | 5145
5100 | ];
5101 | }
5102 |
5103 | // set modifier to call the function by nft smart contract only
5104 | modifier onlyNFTContract() {
5105 | require(msg.sender == _nftAddr, "unauthorized");
5106 | _;
5107 | }
5108 |
5109 | // function to set nft smart contract address by admin
5110 | function setNftAddress(address nftAddress) external onlyOwner {
5111 | require(nftAddress != address(0), "Addr cant be empty");
5112 | _nftAddr = nftAddress;
5113 | emit NftAddressUpdated(nftAddress);
5114 | }
5115 |
5116 | function setRandomTokenMetadata(
5117 | uint8 planetNo,
5118 | uint256 tokenId
5119 | ) external onlyNFTContract returns (uint16) {
5120 | require(planetNo > 0 && planetNo <= 20, "Invalid planet");
5121 | uint256 planetLength = getAvailableNFTsbyPlanet(planetNo);
5122 | require(planetLength > 0, "No NFT available");
5123 | uint256 randomNftIndex = _getRandomNumber(planetLength);
5124 | uint16 metadataId = _getNFTMetadataIdByIndex(planetNo, randomNftIndex);
5125 | _metadataIds[tokenId] = metadataId;
5126 | emit RandomMetadataAssigned(tokenId, planetNo, metadataId);
5127 | return metadataId;
5128 | }
5129 |
5130 | // function to be called by NFT smart contract for platinum and gold minting
5131 | function setTokenMetadataId(
5132 | uint256 tokenId,
5133 | uint16 metadataId
5134 | ) external onlyNFTContract {
5135 | _metadataIds[tokenId] = metadataId;
5136 | emit MetadataIdSet(tokenId, metadataId);
5137 | }
5138 |
5139 | // function to get metadataId of the token
5140 | function getMetadataIdByToken(
5141 | uint256 tokenId
5142 | ) external view returns (uint256) {
5143 | return _metadataIds[tokenId];
5144 | }
5145 |
5146 | // Internal function to get planetNFTsIndex
5147 | function _getNFTMetadataIdByIndex(
5148 | uint8 planetNo,
5149 | uint256 index
5150 | ) internal returns (uint16) {
5151 | // Store `_planets[planetNo]` in memory for reduced storage access
5152 | uint16[] storage planetData = _planets[planetNo];
5153 | // Fetch and remove metadataId in a single read
5154 | uint16 metadataId = planetData[index];
5155 | // Replace with last element and pop only if necessary
5156 | uint256 lastIndex = planetData.length - 1;
5157 | if (index != lastIndex) {
5158 | planetData[index] = planetData[lastIndex];
5159 | }
5160 | planetData.pop();
5161 | return metadataId;
5162 | }
5163 |
5164 | // public function to get the length of a planet
5165 | function getAvailableNFTsbyPlanet(
5166 | uint8 planetNo
5167 | ) public view returns (uint256) {
5168 | return _planets[planetNo].length;
5169 | }
5170 |
5171 | // internal function for Random Number Generation
5172 | function _getRandomNumber(uint256 range) internal view returns (uint256) {
5173 | return
5174 | uint256(
5175 | keccak256(
5176 | abi.encodePacked(
5177 | block.timestamp,
5178 | block.prevrandao,
5179 | blockhash(block.gaslimit),
5180 | block.number,
5181 | msg.sender
5182 | )
5183 | )
5184 | ) % range;
5185 | }
5186 | }
5187 |
--------------------------------------------------------------------------------