class SnowflakeIdGenerator extends IdGenerator {

    private static final Logger LOG = Log.logger(SnowflakeIdGenerator.class);

    private static final Map<String, SnowflakeIdGenerator> INSTANCES =
            new ConcurrentHashMap<>();

    private final boolean forceString;
    private final IdWorker idWorker;

    public static SnowflakeIdGenerator init(HugeGraphParams graph) {
        String graphName = graph.name();
        SnowflakeIdGenerator generator = INSTANCES.get(graphName);
        if (generator == null) {
            synchronized (INSTANCES) {
                if (!INSTANCES.containsKey(graphName)) {
                    HugeConfig conf = graph.configuration();
                    INSTANCES.put(graphName, new SnowflakeIdGenerator(conf));
                }
                generator = INSTANCES.get(graphName);
                assert generator != null;
            }
        }
        return generator;
    }

    public static SnowflakeIdGenerator instance(HugeGraph graph) {<FILL_FUNCTION_BODY>}

    private SnowflakeIdGenerator(HugeConfig config) {
        long workerId = config.get(CoreOptions.SNOWFLAKE_WORKER_ID);
        long datacenterId = config.get(CoreOptions.SNOWFLAKE_DATACENTER_ID);
        this.forceString = config.get(CoreOptions.SNOWFLAKE_FORCE_STRING);
        this.idWorker = new IdWorker(workerId, datacenterId);
        LOG.debug("SnowflakeId Worker started: datacenter id {}, " +
                  "worker id {}, forced string id {}",
                  datacenterId, workerId, this.forceString);
    }

    public Id generate() {
        if (this.idWorker == null) {
            throw new HugeException("Please initialize before using");
        }
        Id id = of(this.idWorker.nextId());
        if (!this.forceString) {
            return id;
        } else {
            return IdGenerator.of(id.asString());
        }
    }

    @Override
    public Id generate(HugeVertex vertex) {
        return this.generate();
    }

    private static class IdWorker {

        private final long workerId;
        private final long datacenterId;
        private long sequence = 0L; // AtomicLong
        private long lastTimestamp = -1L;

        private static final long WORKER_BIT = 5L;
        private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_BIT);

        private static final long DC_BIT = 5L;
        private static final long MAX_DC_ID = -1L ^ (-1L << DC_BIT);

        private static final long SEQUENCE_BIT = 12L;
        private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BIT);

        private static final long WORKER_SHIFT = SEQUENCE_BIT;
        private static final long DC_SHIFT = WORKER_SHIFT + WORKER_BIT;
        private static final long TIMESTAMP_SHIFT = DC_SHIFT + DC_BIT;

        public IdWorker(long workerId, long datacenterId) {
            // Sanity check for workerId
            if (workerId > MAX_WORKER_ID || workerId < 0) {
                throw new IllegalArgumentException(String.format(
                        "Worker id can't > %d or < 0",
                        MAX_WORKER_ID));
            }
            if (datacenterId > MAX_DC_ID || datacenterId < 0) {
                throw new IllegalArgumentException(String.format(
                        "Datacenter id can't > %d or < 0",
                        MAX_DC_ID));
            }
            this.workerId = workerId;
            this.datacenterId = datacenterId;
            LOG.debug("Id Worker starting. timestamp left shift {}," +
                      "datacenter id bits {}, worker id bits {}," +
                      "sequence bits {}",
                      TIMESTAMP_SHIFT, DC_BIT, WORKER_BIT, SEQUENCE_BIT);
        }

        public synchronized long nextId() {
            long timestamp = TimeUtil.timeGen();

            if (timestamp > this.lastTimestamp) {
                this.sequence = 0L;
            } else if (timestamp == this.lastTimestamp) {
                this.sequence = (this.sequence + 1) & SEQUENCE_MASK;
                if (this.sequence == 0) {
                    timestamp = TimeUtil.tillNextMillis(this.lastTimestamp);
                }
            } else {
                LOG.error("Clock is moving backwards, " +
                          "rejecting requests until {}.",
                          this.lastTimestamp);
                throw new HugeException("Clock moved backwards. Refusing to " +
                                        "generate id for %d milliseconds",
                                        this.lastTimestamp - timestamp);
            }

            this.lastTimestamp = timestamp;

            return (timestamp << TIMESTAMP_SHIFT) |
                   (this.datacenterId << DC_SHIFT) |
                   (this.workerId << WORKER_SHIFT) |
                   (this.sequence);
        }
    }
}

        String graphName = graph.name();
        SnowflakeIdGenerator generator = INSTANCES.get(graphName);
        E.checkState(generator != null,
                     "SnowflakeIdGenerator of graph '%s' is not initialized",
                     graphName);
        return generator;
    
<methods>public non-sealed void <init>() ,public static java.lang.String asStoredString(org.apache.hugegraph.backend.id.Id) ,public abstract org.apache.hugegraph.backend.id.Id generate(org.apache.hugegraph.structure.HugeVertex) ,public static org.apache.hugegraph.backend.id.Id.IdType idType(org.apache.hugegraph.backend.id.Id) ,public static org.apache.hugegraph.backend.id.Id of(java.lang.String) ,public static org.apache.hugegraph.backend.id.Id of(java.util.UUID) ,public static org.apache.hugegraph.backend.id.Id of(java.lang.String, boolean) ,public static org.apache.hugegraph.backend.id.Id of(long) ,public static org.apache.hugegraph.backend.id.Id of(java.lang.Object) ,public static org.apache.hugegraph.backend.id.Id of(byte[], org.apache.hugegraph.backend.id.Id.IdType) ,public static org.apache.hugegraph.backend.id.Id ofStoredString(java.lang.String, org.apache.hugegraph.backend.id.Id.IdType) <variables>public static final org.apache.hugegraph.backend.id.Id ZERO