@Test
public void CanFormatWithOneAttribute() {
    AnsiFormat fInfo = new AnsiFormat(CYAN_BACK);
    String formattedText = fInfo.format("This text will have a cyan back");
    String expectedCode = Ansi.generateCode(fInfo);
    assertThat(formattedText, startsWith(expectedCode));
    assertThat("Message should clear its format", formattedText, endsWith(Ansi.RESET));
}

@Test
public void CanFormatWithOneAttribute() {
    AnsiFormat fInfo = new AnsiFormat(CYAN_BACK());
    String formattedText = fInfo.format("This text will have a cyan back");
    String expectedCode = Ansi.generateCode(fInfo);
    assertThat(formattedText, startsWith(expectedCode));
    assertThat("Message should clear its format", formattedText, endsWith(Ansi.RESET));
}

public static Attribute CYAN_BACK() {
    return new SimpleAttribute("46");
}

@Test
public void CanMakeItFabulous() {
    String text = "This text will be magenta";
    String formattedText = makeItFabulous(text, MAGENTA_TEXT);
    assertThat(formattedText, equalTo(colorize(text, MAGENTA_TEXT)));
}

@Test
public void CanMakeItFabulous() {
    String text = "This text will be magenta";
    String formattedText = makeItFabulous(text, MAGENTA_TEXT());
    assertThat(formattedText, equalTo(colorize(text, MAGENTA_TEXT())));
}

public static Attribute MAGENTA_TEXT() {
    return new SimpleAttribute("35");
}

@Test
public void CanFormatWithMultipleAttributes() {
    AnsiFormat fWarning = new AnsiFormat(RED_TEXT, YELLOW_BACK, BOLD);
    String formattedText = fWarning.format("This bold text will be red on yellow");
    String expectedCode = Ansi.generateCode(fWarning);
    assertThat(formattedText, startsWith(expectedCode));
    assertThat("Message should clear its format", formattedText, endsWith(Ansi.RESET));
}

@Test
public void CanFormatWithMultipleAttributes() {
    AnsiFormat fWarning = new AnsiFormat(RED_TEXT(), YELLOW_BACK(), BOLD());
    String formattedText = fWarning.format("This bold text will be red on yellow");
    String expectedCode = Ansi.generateCode(fWarning);
    assertThat(formattedText, startsWith(expectedCode));
    assertThat("Message should clear its format", formattedText, endsWith(Ansi.RESET));
}

public static Attribute RED_TEXT() {
    return new SimpleAttribute("31");
}

@Test
public void CanUseAnsiFormatOrArray() {
    AnsiFormat formatNotation = new AnsiFormat(BLACK_TEXT, BLACK_BACK);
    Attribute[] arrayNotation = new Attribute[] { BLACK_TEXT, BLACK_BACK };
    String text = "This text will be black on black";
    String formatNotationOutput = formatNotation.format(text);
    String arrayNotationOutput = Ansi.colorize(text, arrayNotation);
    assertThat(formatNotationOutput, equalTo(arrayNotationOutput));
    assertThat(formatNotationOutput.compareTo(arrayNotationOutput), equalTo(0));
}

@Test
public void CanUseAnsiFormatOrArray() {
    AnsiFormat formatNotation = new AnsiFormat(BLACK_TEXT(), BLACK_BACK());
    Attribute[] arrayNotation = new Attribute[] { BLACK_TEXT(), BLACK_BACK() };
    String text = "This text will be black on black";
    String formatNotationOutput = formatNotation.format(text);
    String arrayNotationOutput = Ansi.colorize(text, arrayNotation);
    assertThat(formatNotationOutput, equalTo(arrayNotationOutput));
    assertThat(formatNotationOutput.compareTo(arrayNotationOutput), equalTo(0));
}

public static Attribute BLACK_TEXT() {
    return new SimpleAttribute("30");
}

@Test
public void CanFormatInline() {
    String formattedText = colorize("This text will be yellow on magenta", YELLOW_TEXT, MAGENTA_BACK);
    String expectedCode = Ansi.generateCode(YELLOW_TEXT, MAGENTA_BACK);
    assertThat(formattedText, startsWith(expectedCode));
    assertThat("Message should clear its format", formattedText, endsWith(Ansi.RESET));
}

@Test
public void CanFormatInline() {
    String formattedText = colorize("This text will be yellow on magenta", YELLOW_TEXT(), MAGENTA_BACK());
    String expectedCode = Ansi.generateCode(YELLOW_TEXT(), MAGENTA_BACK());
    assertThat(formattedText, startsWith(expectedCode));
    assertThat("Message should clear its format", formattedText, endsWith(Ansi.RESET));
}

public static Attribute YELLOW_TEXT() {
    return new SimpleAttribute("33");
}


@Test
public void testEncodeAndDecode() {
    final BinaryCodec codec = new BinaryCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid = UUID.randomUUID();
        byte[] bytes = codec.encode(uuid);
        assertEquals(uuid, codec.decode(bytes));
    }
}

@Test
public void testEncodeAndDecode() {
    final StandardBinaryCodec codec = new StandardBinaryCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid = UUID.randomUUID();
        byte[] bytes = codec.encode(uuid);
        assertEquals(uuid, codec.decode(bytes));
    }
}

@Override
public byte[] encode(final UUID uuid) {
    UuidValidator.validate(uuid);
    final byte[] bytes = new byte[16];
    final long msb = uuid.getMostSignificantBits();
    final long lsb = uuid.getLeastSignificantBits();
    bytes[0x0] = (byte) (msb >>> 56);
    bytes[0x1] = (byte) (msb >>> 48);
    bytes[0x2] = (byte) (msb >>> 40);
    bytes[0x3] = (byte) (msb >>> 32);
    bytes[0x4] = (byte) (msb >>> 24);
    bytes[0x5] = (byte) (msb >>> 16);
    bytes[0x6] = (byte) (msb >>> 8);
    bytes[0x7] = (byte) (msb);
    bytes[0x8] = (byte) (lsb >>> 56);
    bytes[0x9] = (byte) (lsb >>> 48);
    bytes[0xa] = (byte) (lsb >>> 40);
    bytes[0xb] = (byte) (lsb >>> 32);
    bytes[0xc] = (byte) (lsb >>> 24);
    bytes[0xd] = (byte) (lsb >>> 16);
    bytes[0xe] = (byte) (lsb >>> 8);
    bytes[0xf] = (byte) (lsb);
    return bytes;
}

@Test
public void testEncode() {
    BinaryCodec codec = new BinaryCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid1 = UUID.randomUUID();
        byte[] bytes = UuidCreator.toBytes(uuid1);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        long msb = buffer.getLong(0);
        long lsb = buffer.getLong(8);
        UUID uuid2 = new UUID(msb, lsb);
        assertEquals(uuid1, uuid2);
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid1 = UUID.randomUUID();
        byte[] bytes = codec.encode(uuid1);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        long msb = buffer.getLong(0);
        long lsb = buffer.getLong(8);
        UUID uuid2 = new UUID(msb, lsb);
        assertEquals(uuid1, uuid2);
    }
}

@Test
public void testEncode() {
    StandardBinaryCodec codec = new StandardBinaryCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid1 = UUID.randomUUID();
        byte[] bytes = UuidCreator.toBytes(uuid1);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        long msb = buffer.getLong(0);
        long lsb = buffer.getLong(8);
        UUID uuid2 = new UUID(msb, lsb);
        assertEquals(uuid1, uuid2);
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid1 = UUID.randomUUID();
        byte[] bytes = codec.encode(uuid1);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        long msb = buffer.getLong(0);
        long lsb = buffer.getLong(8);
        UUID uuid2 = new UUID(msb, lsb);
        assertEquals(uuid1, uuid2);
    }
}

@Test
public void testEncodeInvalidUuidException() {
    BinaryCodec codec = new BinaryCodec();
    {
        try {
            UUID uuid = new UUID(0L, 0L);
            codec.encode(uuid);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
        try {
            UUID uuid = UUID.randomUUID();
            codec.encode(uuid);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
    }
    {
        try {
            UUID uuid = null;
            codec.encode(uuid);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
    }
}

@Test
public void testEncodeInvalidUuidException() {
    StandardBinaryCodec codec = new StandardBinaryCodec();
    {
        try {
            UUID uuid = new UUID(0L, 0L);
            codec.encode(uuid);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
        try {
            UUID uuid = UUID.randomUUID();
            codec.encode(uuid);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
    }
    {
        try {
            UUID uuid = null;
            codec.encode(uuid);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
    }
}

@Test
public void testDecodeInvalidUuidException() {
    BinaryCodec codec = new BinaryCodec();
    {
        try {
            byte[] bytes = new byte[16];
            codec.decode(bytes);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
    }
    {
        try {
            byte[] bytes = null;
            codec.decode(bytes);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
        try {
            byte[] bytes = new byte[15];
            codec.decode(bytes);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
        try {
            byte[] bytes = new byte[17];
            codec.decode(bytes);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
    }
}

@Test
public void testDecodeInvalidUuidException() {
    StandardBinaryCodec codec = new StandardBinaryCodec();
    {
        try {
            byte[] bytes = new byte[16];
            codec.decode(bytes);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
    }
    {
        try {
            byte[] bytes = null;
            codec.decode(bytes);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
        try {
            byte[] bytes = new byte[15];
            codec.decode(bytes);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
        try {
            byte[] bytes = new byte[17];
            codec.decode(bytes);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
    }
}

@Override
public UUID decode(final byte[] bytes) {
    UuidValidator.validate(bytes);
    long msb = 0;
    long lsb = 0;
    msb |= (bytes[0x0] & 0xffL) << 56;
    msb |= (bytes[0x1] & 0xffL) << 48;
    msb |= (bytes[0x2] & 0xffL) << 40;
    msb |= (bytes[0x3] & 0xffL) << 32;
    msb |= (bytes[0x4] & 0xffL) << 24;
    msb |= (bytes[0x5] & 0xffL) << 16;
    msb |= (bytes[0x6] & 0xffL) << 8;
    msb |= (bytes[0x7] & 0xffL);
    lsb |= (bytes[0x8] & 0xffL) << 56;
    lsb |= (bytes[0x9] & 0xffL) << 48;
    lsb |= (bytes[0xa] & 0xffL) << 40;
    lsb |= (bytes[0xb] & 0xffL) << 32;
    lsb |= (bytes[0xc] & 0xffL) << 24;
    lsb |= (bytes[0xd] & 0xffL) << 16;
    lsb |= (bytes[0xe] & 0xffL) << 8;
    lsb |= (bytes[0xf] & 0xffL);
    return new UUID(msb, lsb);
}

@Test
public void testDecode() {
    BinaryCodec codec = new BinaryCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid1 = UUID.randomUUID();
        ByteBuffer buffer = ByteBuffer.allocate(16);
        buffer.putLong(uuid1.getMostSignificantBits());
        buffer.putLong(uuid1.getLeastSignificantBits());
        byte[] bytes = buffer.array();
        UUID uuid2 = UuidCreator.fromBytes(bytes);
        assertEquals(uuid1, uuid2);
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid1 = UUID.randomUUID();
        ByteBuffer buffer = ByteBuffer.allocate(16);
        buffer.putLong(uuid1.getMostSignificantBits());
        buffer.putLong(uuid1.getLeastSignificantBits());
        byte[] bytes = buffer.array();
        UUID uuid2 = codec.decode(bytes);
        assertEquals(uuid1, uuid2);
    }
}

@Test
public void testDecode() {
    StandardBinaryCodec codec = new StandardBinaryCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid1 = UUID.randomUUID();
        ByteBuffer buffer = ByteBuffer.allocate(16);
        buffer.putLong(uuid1.getMostSignificantBits());
        buffer.putLong(uuid1.getLeastSignificantBits());
        byte[] bytes = buffer.array();
        UUID uuid2 = UuidCreator.fromBytes(bytes);
        assertEquals(uuid1, uuid2);
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid1 = UUID.randomUUID();
        ByteBuffer buffer = ByteBuffer.allocate(16);
        buffer.putLong(uuid1.getMostSignificantBits());
        buffer.putLong(uuid1.getLeastSignificantBits());
        byte[] bytes = buffer.array();
        UUID uuid2 = codec.decode(bytes);
        assertEquals(uuid1, uuid2);
    }
}


@Test
public void testDecodeInvalidUuidException() {
    StringCodec codec = new StringCodec();
    try {
        String string = new UUID(0L, 0L).toString();
        codec.decode(string);
    } catch (InvalidUuidException e) {
        fail("Should not throw exception");
    }
    try {
        String string = UUID.randomUUID().toString();
        codec.decode(string);
    } catch (InvalidUuidException e) {
        fail("Should not throw exception");
    }
    try {
        String string = URN_PREFIX + UUID.randomUUID().toString();
        codec.decode(string);
    } catch (InvalidUuidException e) {
        fail("Should not throw exception");
    }
    try {
        String string = "{" + UUID.randomUUID().toString() + "}";
        codec.decode(string);
    } catch (InvalidUuidException e) {
        fail("Should not throw exception");
    }
    try {
        String string = null;
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
    try {
        String string = "";
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
    try {
        String string = "INVALID";
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
    try {
        String string = UUID.randomUUID().toString() + "x";
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
    try {
        String string = UUID.randomUUID().toString().substring(0, 35);
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
}

@Test
public void testDecodeInvalidUuidException() {
    StandardStringCodec codec = new StandardStringCodec();
    try {
        String string = new UUID(0L, 0L).toString();
        codec.decode(string);
    } catch (InvalidUuidException e) {
        fail("Should not throw exception");
    }
    try {
        String string = UUID.randomUUID().toString();
        codec.decode(string);
    } catch (InvalidUuidException e) {
        fail("Should not throw exception");
    }
    try {
        String string = URN_PREFIX + UUID.randomUUID().toString();
        codec.decode(string);
    } catch (InvalidUuidException e) {
        fail("Should not throw exception");
    }
    try {
        String string = "{" + UUID.randomUUID().toString() + "}";
        codec.decode(string);
    } catch (InvalidUuidException e) {
        fail("Should not throw exception");
    }
    try {
        String string = null;
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
    try {
        String string = "";
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
    try {
        String string = "INVALID";
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
    try {
        String string = UUID.randomUUID().toString() + "x";
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
    try {
        String string = UUID.randomUUID().toString().substring(0, 35);
        codec.decode(string);
        fail("Should throw exception");
    } catch (InvalidUuidException e) {
    }
}

@Override
public UUID decode(final String string) {
    if (string == null) {
        throw InvalidUuidException.newInstance(string);
    }
    final String modified = modify(string);
    if (modified.length() == LENGTH_WITH_DASH) {
        validate(modified);
        return parse(modified);
    }
    if (modified.length() == LENGTH_WITHOUT_DASH) {
        return Base16Codec.INSTANCE.decode(modified);
    }
    throw InvalidUuidException.newInstance(modified);
}

@Test
public void testEncodeInvalidUuidException() {
    StringCodec codec = new StringCodec();
    {
        try {
            UUID uuid = new UUID(0L, 0L);
            codec.encode(uuid);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
        try {
            UUID uuid = UUID.randomUUID();
            codec.encode(uuid);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
    }
    {
        UUID uuid = null;
        try {
            codec.encode(uuid);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
    }
}

@Test
public void testEncodeInvalidUuidException() {
    StandardStringCodec codec = new StandardStringCodec();
    {
        try {
            UUID uuid = new UUID(0L, 0L);
            codec.encode(uuid);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
        try {
            UUID uuid = UUID.randomUUID();
            codec.encode(uuid);
        } catch (InvalidUuidException e) {
            fail("Should not throw exception");
        }
    }
    {
        UUID uuid = null;
        try {
            codec.encode(uuid);
            fail("Should throw exception");
        } catch (InvalidUuidException e) {
        }
    }
}

@Override
public String encode(UUID uuid) {
    UuidValidator.validate(uuid);
    if (JAVA_VERSION_GREATER_THAN_8) {
        return uuid.toString();
    }
    final char[] chars = new char[36];
    final long msb = uuid.getMostSignificantBits();
    final long lsb = uuid.getLeastSignificantBits();
    chars[0x00] = ALPHABET.get((int) (msb >>> 0x3c & 0xf));
    chars[0x01] = ALPHABET.get((int) (msb >>> 0x38 & 0xf));
    chars[0x02] = ALPHABET.get((int) (msb >>> 0x34 & 0xf));
    chars[0x03] = ALPHABET.get((int) (msb >>> 0x30 & 0xf));
    chars[0x04] = ALPHABET.get((int) (msb >>> 0x2c & 0xf));
    chars[0x05] = ALPHABET.get((int) (msb >>> 0x28 & 0xf));
    chars[0x06] = ALPHABET.get((int) (msb >>> 0x24 & 0xf));
    chars[0x07] = ALPHABET.get((int) (msb >>> 0x20 & 0xf));
    chars[0x08] = '-';
    chars[0x09] = ALPHABET.get((int) (msb >>> 0x1c & 0xf));
    chars[0x0a] = ALPHABET.get((int) (msb >>> 0x18 & 0xf));
    chars[0x0b] = ALPHABET.get((int) (msb >>> 0x14 & 0xf));
    chars[0x0c] = ALPHABET.get((int) (msb >>> 0x10 & 0xf));
    chars[0x0d] = '-';
    chars[0x0e] = ALPHABET.get((int) (msb >>> 0x0c & 0xf));
    chars[0x0f] = ALPHABET.get((int) (msb >>> 0x08 & 0xf));
    chars[0x10] = ALPHABET.get((int) (msb >>> 0x04 & 0xf));
    chars[0x11] = ALPHABET.get((int) (msb & 0xf));
    chars[0x12] = '-';
    chars[0x13] = ALPHABET.get((int) (lsb >>> 0x3c & 0xf));
    chars[0x14] = ALPHABET.get((int) (lsb >>> 0x38 & 0xf));
    chars[0x15] = ALPHABET.get((int) (lsb >>> 0x34 & 0xf));
    chars[0x16] = ALPHABET.get((int) (lsb >>> 0x30 & 0xf));
    chars[0x17] = '-';
    chars[0x18] = ALPHABET.get((int) (lsb >>> 0x2c & 0xf));
    chars[0x19] = ALPHABET.get((int) (lsb >>> 0x28 & 0xf));
    chars[0x1a] = ALPHABET.get((int) (lsb >>> 0x24 & 0xf));
    chars[0x1b] = ALPHABET.get((int) (lsb >>> 0x20 & 0xf));
    chars[0x1c] = ALPHABET.get((int) (lsb >>> 0x1c & 0xf));
    chars[0x1d] = ALPHABET.get((int) (lsb >>> 0x18 & 0xf));
    chars[0x1e] = ALPHABET.get((int) (lsb >>> 0x14 & 0xf));
    chars[0x1f] = ALPHABET.get((int) (lsb >>> 0x10 & 0xf));
    chars[0x20] = ALPHABET.get((int) (lsb >>> 0x0c & 0xf));
    chars[0x21] = ALPHABET.get((int) (lsb >>> 0x08 & 0xf));
    chars[0x22] = ALPHABET.get((int) (lsb >>> 0x04 & 0xf));
    chars[0x23] = ALPHABET.get((int) (lsb & 0xf));
    return new String(chars);
}

@Test
public void testModifyString() {
    {
        String string = UUID.randomUUID().toString();
        assertEquals(string, StringCodec.modify(string));
    }
    {
        String string = UUID.randomUUID().toString();
        assertEquals(string, StringCodec.modify(URN_PREFIX + string));
    }
    {
        String string = UUID.randomUUID().toString();
        assertEquals(string, StringCodec.modify("{" + string + "}"));
    }
}

@Test
public void testModifyString() {
    {
        String string = UUID.randomUUID().toString();
        assertEquals(string, StandardStringCodec.modify(string));
    }
    {
        String string = UUID.randomUUID().toString();
        assertEquals(string, StandardStringCodec.modify(URN_PREFIX + string));
    }
    {
        String string = UUID.randomUUID().toString();
        assertEquals(string, StandardStringCodec.modify("{" + string + "}"));
    }
}

protected static String modify(final String string) {
    if (string.length() == LENGTH_WITH_URN_PREFIX && string.startsWith(URN_PREFIX)) {
        return string.substring(URN_PREFIX.length());
    }
    if (string.length() == LENGTH_WITH_CURLY_BRACES && string.startsWith("{") && string.endsWith("}")) {
        return string.substring(1, string.length() - 1);
    }
    return string;
}

@Test
public void testEncode() {
    StringCodec codec = new StringCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID random = UUID.randomUUID();
        String expected = random.toString();
        String actual = UuidCreator.toString(random);
        checkPattern(actual, RFC4122_PATTERN);
        assertEquals(expected, actual);
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID random = UUID.randomUUID();
        String expected = random.toString();
        String actual = codec.encode(random);
        checkPattern(actual, RFC4122_PATTERN);
        assertEquals(expected, actual);
    }
}

@Test
public void testEncode() {
    StandardStringCodec codec = new StandardStringCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID random = UUID.randomUUID();
        String expected = random.toString();
        String actual = UuidCreator.toString(random);
        checkPattern(actual, RFC4122_PATTERN);
        assertEquals(expected, actual);
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID random = UUID.randomUUID();
        String expected = random.toString();
        String actual = codec.encode(random);
        checkPattern(actual, RFC4122_PATTERN);
        assertEquals(expected, actual);
    }
}

@Test
public void testDecode() {
    StringCodec codec = new StringCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = UuidCreator.fromString(string);
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(string);
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(string.replace("-", ""));
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(string.toUpperCase());
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(string.toUpperCase().replace("-", ""));
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(URN_PREFIX + string);
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode("{" + string + "}");
        assertEquals(string, uuid.toString());
    }
}

@Test
public void testDecode() {
    StandardStringCodec codec = new StandardStringCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = UuidCreator.fromString(string);
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(string);
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(string.replace("-", ""));
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(string.toUpperCase());
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(string.toUpperCase().replace("-", ""));
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode(URN_PREFIX + string);
        assertEquals(string, uuid.toString());
    }
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        String string = UUID.randomUUID().toString();
        UUID uuid = codec.decode("{" + string + "}");
        assertEquals(string, uuid.toString());
    }
}

@Test
public void testEncodeAndDecode() {
    final StringCodec codec = new StringCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid = UUID.randomUUID();
        String string = codec.encode(uuid);
        assertEquals(uuid, codec.decode(string));
    }
}

@Test
public void testEncodeAndDecode() {
    final StandardStringCodec codec = new StandardStringCodec();
    for (int i = 0; i < DEFAULT_LOOP_LIMIT; i++) {
        UUID uuid = UUID.randomUUID();
        String string = codec.encode(uuid);
        assertEquals(uuid, codec.decode(string));
    }
}


@Test
public void testDecode() {
    for (int i = 0; i < 1000; i++) {
        String string = getRandomString(Base62Codec.INSTANCE.getBase());
        UUID uuid = Base62Codec.INSTANCE.decode(string);
        byte[] bytes = BinaryCodec.INSTANCE.encode(uuid);
        assertEquals(Arrays.toString(decode(Base62Codec.INSTANCE.getBase(), string)), Arrays.toString(bytes));
    }
}

@Test
public void testDecode() {
    for (int i = 0; i < 1000; i++) {
        String string = getRandomString(Base62Codec.INSTANCE.getBase());
        UUID uuid = Base62Codec.INSTANCE.decode(string);
        byte[] bytes = StandardBinaryCodec.INSTANCE.encode(uuid);
        assertEquals(Arrays.toString(decode(Base62Codec.INSTANCE.getBase(), string)), Arrays.toString(bytes));
    }
}


@Test
public void testEncode() {
    SplittableRandom seeder = new SplittableRandom(1);
    for (int i = 0; i < 1000; i++) {
        byte[] bytes = new byte[UUID_BYTES];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        UUID uuid = BinaryCodec.INSTANCE.decode(bytes);
        String string = Base62Codec.INSTANCE.encode(uuid);
        assertEquals(encode(Base62Codec.INSTANCE.getBase(), bytes), string);
    }
}

@Test
public void testEncode() {
    SplittableRandom seeder = new SplittableRandom(1);
    for (int i = 0; i < 1000; i++) {
        byte[] bytes = new byte[UUID_BYTES];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        UUID uuid = StandardBinaryCodec.INSTANCE.decode(bytes);
        String string = Base62Codec.INSTANCE.encode(uuid);
        assertEquals(encode(Base62Codec.INSTANCE.getBase(), bytes), string);
    }
}

@Override
public UUID decode(final byte[] bytes) {
    UuidValidator.validate(bytes);
    long msb = 0;
    long lsb = 0;
    msb |= (bytes[0x0] & 0xffL) << 56;
    msb |= (bytes[0x1] & 0xffL) << 48;
    msb |= (bytes[0x2] & 0xffL) << 40;
    msb |= (bytes[0x3] & 0xffL) << 32;
    msb |= (bytes[0x4] & 0xffL) << 24;
    msb |= (bytes[0x5] & 0xffL) << 16;
    msb |= (bytes[0x6] & 0xffL) << 8;
    msb |= (bytes[0x7] & 0xffL);
    lsb |= (bytes[0x8] & 0xffL) << 56;
    lsb |= (bytes[0x9] & 0xffL) << 48;
    lsb |= (bytes[0xa] & 0xffL) << 40;
    lsb |= (bytes[0xb] & 0xffL) << 32;
    lsb |= (bytes[0xc] & 0xffL) << 24;
    lsb |= (bytes[0xd] & 0xffL) << 16;
    lsb |= (bytes[0xe] & 0xffL) << 8;
    lsb |= (bytes[0xf] & 0xffL);
    return new UUID(msb, lsb);
}


@Test
public void testCompareOpaque() {
    final long zero = 0L;
    byte[] bytes = new byte[16];
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(random.nextLong()).array();
        UUID uuid1 = BinaryCodec.INSTANCE.decode(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(random.nextLong()).array();
        UUID uuid2 = BinaryCodec.INSTANCE.decode(bytes);
        UUID uuid3 = BinaryCodec.INSTANCE.decode(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, UuidComparator.opaqueCompare(uuid1, uuid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, UuidComparator.opaqueCompare(uuid1, uuid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, UuidComparator.opaqueCompare(uuid2, uuid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, uuid1.toString().compareTo(uuid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, uuid1.toString().compareTo(uuid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, uuid2.toString().compareTo(uuid3.toString()) == 0);
    }
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(zero).putLong(random.nextLong()).array();
        UUID uuid1 = BinaryCodec.INSTANCE.decode(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(zero).putLong(random.nextLong()).array();
        UUID uuid2 = BinaryCodec.INSTANCE.decode(bytes);
        UUID uuid3 = BinaryCodec.INSTANCE.decode(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, UuidComparator.opaqueCompare(uuid1, uuid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, UuidComparator.opaqueCompare(uuid1, uuid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, UuidComparator.opaqueCompare(uuid2, uuid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, uuid1.toString().compareTo(uuid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, uuid1.toString().compareTo(uuid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, uuid2.toString().compareTo(uuid3.toString()) == 0);
    }
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(zero).array();
        UUID uuid1 = BinaryCodec.INSTANCE.decode(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(zero).array();
        UUID uuid2 = BinaryCodec.INSTANCE.decode(bytes);
        UUID uuid3 = BinaryCodec.INSTANCE.decode(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, UuidComparator.opaqueCompare(uuid1, uuid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, UuidComparator.opaqueCompare(uuid1, uuid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, UuidComparator.opaqueCompare(uuid2, uuid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, uuid1.toString().compareTo(uuid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, uuid1.toString().compareTo(uuid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, uuid2.toString().compareTo(uuid3.toString()) == 0);
    }
}

@Test
public void testCompareOpaque() {
    final long zero = 0L;
    byte[] bytes = new byte[16];
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(random.nextLong()).array();
        UUID uuid1 = StandardBinaryCodec.INSTANCE.decode(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(random.nextLong()).array();
        UUID uuid2 = StandardBinaryCodec.INSTANCE.decode(bytes);
        UUID uuid3 = StandardBinaryCodec.INSTANCE.decode(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, UuidComparator.opaqueCompare(uuid1, uuid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, UuidComparator.opaqueCompare(uuid1, uuid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, UuidComparator.opaqueCompare(uuid2, uuid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, uuid1.toString().compareTo(uuid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, uuid1.toString().compareTo(uuid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, uuid2.toString().compareTo(uuid3.toString()) == 0);
    }
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(zero).putLong(random.nextLong()).array();
        UUID uuid1 = StandardBinaryCodec.INSTANCE.decode(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(zero).putLong(random.nextLong()).array();
        UUID uuid2 = StandardBinaryCodec.INSTANCE.decode(bytes);
        UUID uuid3 = StandardBinaryCodec.INSTANCE.decode(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, UuidComparator.opaqueCompare(uuid1, uuid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, UuidComparator.opaqueCompare(uuid1, uuid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, UuidComparator.opaqueCompare(uuid2, uuid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, uuid1.toString().compareTo(uuid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, uuid1.toString().compareTo(uuid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, uuid2.toString().compareTo(uuid3.toString()) == 0);
    }
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(zero).array();
        UUID uuid1 = StandardBinaryCodec.INSTANCE.decode(bytes);
        BigInteger number1 = new BigInteger(1, bytes);
        bytes = ByteBuffer.allocate(16).putLong(random.nextLong()).putLong(zero).array();
        UUID uuid2 = StandardBinaryCodec.INSTANCE.decode(bytes);
        UUID uuid3 = StandardBinaryCodec.INSTANCE.decode(bytes);
        BigInteger number2 = new BigInteger(1, bytes);
        BigInteger number3 = new BigInteger(1, bytes);
        assertEquals(number1.compareTo(number2) > 0, UuidComparator.opaqueCompare(uuid1, uuid2) > 0);
        assertEquals(number1.compareTo(number2) < 0, UuidComparator.opaqueCompare(uuid1, uuid2) < 0);
        assertEquals(number2.compareTo(number3) == 0, UuidComparator.opaqueCompare(uuid2, uuid3) == 0);
        assertEquals(number1.compareTo(number2) > 0, uuid1.toString().compareTo(uuid2.toString()) > 0);
        assertEquals(number1.compareTo(number2) < 0, uuid1.toString().compareTo(uuid2.toString()) < 0);
        assertEquals(number2.compareTo(number3) == 0, uuid2.toString().compareTo(uuid3.toString()) == 0);
    }
}


@Test
public void testGetTimestampMillisecond() {
    DefaultTimeFunction supplier = new DefaultTimeFunction();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long m1 = System.currentTimeMillis();
        long ts = supplier.getAsLong() / 10000L;
        long m2 = System.currentTimeMillis();
        assertTrue("The current timstamp millisecond is incorrect", ts >= m1 && ts <= m2);
    }
}

@Test
public void testGetTimestampMillisecond() {
    DefaultTimeFunction supplier = new DefaultTimeFunction();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long m1 = System.currentTimeMillis();
        long ts = supplier.getAsLong() / 10000L;
        long m2 = System.currentTimeMillis() + 1;
        assertTrue("The current timstamp millisecond is incorrect", ts >= m1 && ts <= m2);
    }
}

@Override
public long getAsLong() {
    final long time = System.currentTimeMillis();
    if (time == prevTime) {
        if (++counter >= TICKS_PER_MILLI) {
            while (time == System.currentTimeMillis()) {
            }
        }
    } else {
        counter = ++counter % TICKS_PER_MILLI;
    }
    prevTime = time;
    return (time * TICKS_PER_MILLI) + counter;
}

@Override
public long getAsLong() {
    counter++;
    final long time = System.currentTimeMillis();
    if (time == prevTime) {
        if (counter >= counterMax) {
            while (time == System.currentTimeMillis()) {
            }
        }
    } else {
        counter = counter % TICKS_PER_MILLI;
        counterMax = counter + TICKS_PER_MILLI;
    }
    prevTime = time;
    return (time * TICKS_PER_MILLI) + counter;
}

@Test
public void testGetTimestampTicks() {
    DefaultTimeFunction supplier = new DefaultTimeFunction();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long m1 = System.currentTimeMillis() * 10000L;
        long ts = supplier.getAsLong();
        long m2 = System.currentTimeMillis() * 10000L + 10000L;
        assertTrue("The current timstamp millisecond is incorrect", ts >= m1 && ts <= m2);
    }
}

@Test
public void testGetTimestampTicks() {
    DefaultTimeFunction supplier = new DefaultTimeFunction();
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long m1 = System.currentTimeMillis() * 10000L;
        long ts = supplier.getAsLong();
        long m2 = System.currentTimeMillis() * 10000L + 20000L;
        assertTrue("The current timstamp millisecond is incorrect", ts >= m1 && ts <= m2);
    }
}


@Test
public void testV3() {
    GUID prev = GUID.v3(GUID.NIL, "");
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        GUID namespace = new GUID(UUID.randomUUID());
        String name = UUID.randomUUID().toString();
        GUID uuid = GUID.v3(namespace, name);
        assertNotNull(uuid);
        assertNotEquals(prev, uuid);
        assertEquals(3, uuid.version());
        assertNotEquals(GUID.NIL, uuid);
        assertNotEquals(GUID.MAX, uuid);
        assertNotEquals(GUID.v3(null, name), GUID.v3(namespace, name));
        assertNotEquals(GUID.v3(GUID.NIL, name), GUID.v3(namespace, name));
        assertNotEquals(GUID.v3(namespace, name), GUID.v5(namespace, name));
        assertNotEquals(GUID.v3(namespace, name), GUID.v8(namespace, name));
        assertEquals(GUID.v3(null, name), GUID.v3(null, name));
        assertEquals(GUID.v3(GUID.NIL, name), GUID.v3(GUID.NIL, name));
        assertEquals(GUID.v3(namespace, name), GUID.v3(namespace, name));
        assertEquals(UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8)), GUID.v3(null, name).get());
        prev = uuid;
    }
    {
        GUID guid = GUID.v3(GUID.NAMESPACE_DNS, "www.example.com");
        assertEquals("5df41881-3aed-3515-88a7-2f4a814cf09e", guid.toString());
    }
}

@Test
public void testV3() {
    GUID prev = GUID.v3(GUID.NIL, "");
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        GUID namespace = new GUID(UUID.randomUUID());
        String name = UUID.randomUUID().toString();
        GUID guid = GUID.v3(namespace, name);
        assertNotNull(guid);
        assertNotEquals(prev, guid);
        assertEquals(3, guid.version());
        assertNotEquals(GUID.NIL, guid);
        assertNotEquals(GUID.MAX, guid);
        assertNotEquals(GUID.v3(null, name), GUID.v3(namespace, name));
        assertNotEquals(GUID.v3(GUID.NIL, name), GUID.v3(namespace, name));
        assertNotEquals(GUID.v3(namespace, name), GUID.v5(namespace, name));
        assertNotEquals(GUID.v3(namespace, name), GUID.v8(namespace, name));
        assertEquals(GUID.v3(null, name), GUID.v3(null, name));
        assertEquals(GUID.v3(GUID.NIL, name), GUID.v3(GUID.NIL, name));
        assertEquals(GUID.v3(namespace, name), GUID.v3(namespace, name));
        assertEquals(UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8)), GUID.v3(null, name).toUUID());
        prev = guid;
    }
    {
        GUID guid = GUID.v3(GUID.NAMESPACE_DNS, "www.example.com");
        assertEquals("5df41881-3aed-3515-88a7-2f4a814cf09e", guid.toString());
    }
}

public int version() {
    return get().version();
}

public int version() {
    return toUUID().version();
}


@Test
public void testGetPrefixCombWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    RandomFunction randomFunction = x -> {
        final byte[] bytes = new byte[x];
        ThreadLocalRandom.current().nextBytes(bytes);
        return bytes;
    };
    PrefixCombFactory factory = new PrefixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
        assertNotNull("UUID is null", list[i]);
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}

@Test
public void testGetPrefixCombWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> ThreadLocalRandom.current().nextLong();
    PrefixCombFactory factory = new PrefixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
        assertNotNull("UUID is null", list[i]);
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong();
        if (this.random instanceof ByteRandom) {
            final byte[] bytes = this.random.nextBytes(10);
            final long long1 = ByteUtil.toNumber(bytes, 0, 2);
            final long long2 = ByteUtil.toNumber(bytes, 2, 10);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong();
        if (this.random instanceof SafeRandom) {
            final byte[] bytes = this.random.nextBytes(10);
            final long long1 = ByteUtil.toNumber(bytes, 0, 2);
            final long long2 = ByteUtil.toNumber(bytes, 2, 10);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}


@Test
public void testGetShortPrefixCombWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    RandomFunction randomFunction = x -> {
        final byte[] bytes = new byte[x];
        ThreadLocalRandom.current().nextBytes(bytes);
        return bytes;
    };
    ShortPrefixCombFactory factory = new ShortPrefixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}

@Test
public void testGetShortPrefixCombWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> ThreadLocalRandom.current().nextLong();
    ShortPrefixCombFactory factory = new ShortPrefixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong() / interval;
        if (this.random instanceof ByteRandom) {
            final byte[] bytes = this.random.nextBytes(14);
            final long long1 = ByteUtil.toNumber(bytes, 0, 6);
            final long long2 = ByteUtil.toNumber(bytes, 6, 14);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong() / interval;
        if (this.random instanceof SafeRandom) {
            final byte[] bytes = this.random.nextBytes(14);
            final long long1 = ByteUtil.toNumber(bytes, 0, 6);
            final long long2 = ByteUtil.toNumber(bytes, 6, 14);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}


@Test
public void testGetShortSuffixCombWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    RandomFunction randomFunction = x -> {
        final byte[] bytes = new byte[x];
        ThreadLocalRandom.current().nextBytes(bytes);
        return bytes;
    };
    ShortSuffixCombFactory factory = new ShortSuffixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}

@Test
public void testGetShortSuffixCombWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> ThreadLocalRandom.current().nextLong();
    ShortSuffixCombFactory factory = new ShortSuffixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong() / interval;
        if (this.random instanceof ByteRandom) {
            final byte[] bytes = this.random.nextBytes(14);
            final long long1 = ByteUtil.toNumber(bytes, 0, 8);
            final long long2 = ByteUtil.toNumber(bytes, 8, 14);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong() / interval;
        if (this.random instanceof SafeRandom) {
            final byte[] bytes = this.random.nextBytes(14);
            final long long1 = ByteUtil.toNumber(bytes, 0, 8);
            final long long2 = ByteUtil.toNumber(bytes, 8, 14);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}


@Test
public void testGetSuffixCombWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    RandomFunction randomFunction = x -> {
        final byte[] bytes = new byte[x];
        ThreadLocalRandom.current().nextBytes(bytes);
        return bytes;
    };
    SuffixCombFactory factory = new SuffixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}

@Test
public void testGetSuffixCombWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> ThreadLocalRandom.current().nextLong();
    SuffixCombFactory factory = new SuffixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong();
        if (this.random instanceof ByteRandom) {
            final byte[] bytes = this.random.nextBytes(10);
            final long long1 = ByteUtil.toNumber(bytes, 0, 8);
            final long long2 = ByteUtil.toNumber(bytes, 8, 10);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        final long time = timeFunction.getAsLong();
        if (this.random instanceof SafeRandom) {
            final byte[] bytes = this.random.nextBytes(10);
            final long long1 = ByteUtil.toNumber(bytes, 0, 8);
            final long long2 = ByteUtil.toNumber(bytes, 8, 10);
            return make(time, long1, long2);
        } else {
            final long long1 = this.random.nextLong();
            final long long2 = this.random.nextLong();
            return make(time, long1, long2);
        }
    } finally {
        lock.unlock();
    }
}


@Test
public void testGetRandomBasedWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    RandomFunction randomFunction = x -> {
        final byte[] bytes = new byte[x];
        ThreadLocalRandom.current().nextBytes(bytes);
        return bytes;
    };
    RandomBasedFactory factory = new RandomBasedFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkUniqueness(list);
    checkVersion(list, UuidVersion.VERSION_RANDOM_BASED.getValue());
}

@Test
public void testGetRandomBasedWithRandomFunction() {
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> ThreadLocalRandom.current().nextLong();
    RandomBasedFactory factory = new RandomBasedFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkUniqueness(list);
    checkVersion(list, UuidVersion.VERSION_RANDOM_BASED.getValue());
}

@Override
public UUID create() {
    lock.lock();
    try {
        if (this.random instanceof ByteRandom) {
            final byte[] bytes = this.random.nextBytes(16);
            final long msb = ByteUtil.toNumber(bytes, 0, 8);
            final long lsb = ByteUtil.toNumber(bytes, 8, 16);
            return toUuid(msb, lsb);
        } else {
            final long msb = this.random.nextLong();
            final long lsb = this.random.nextLong();
            return toUuid(msb, lsb);
        }
    } finally {
        lock.unlock();
    }
}

@Override
public UUID create() {
    lock.lock();
    try {
        if (this.random instanceof SafeRandom) {
            final byte[] bytes = this.random.nextBytes(16);
            final long msb = ByteUtil.toNumber(bytes, 0, 8);
            final long lsb = ByteUtil.toNumber(bytes, 8, 16);
            return toUuid(msb, lsb);
        } else {
            final long msb = this.random.nextLong();
            final long lsb = this.random.nextLong();
            return toUuid(msb, lsb);
        }
    } finally {
        lock.unlock();
    }
}


@Test
public void testByteRandomNextLong() {
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random()).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.ByteRandom((x) -> bytes);
        assertEquals(number, random.nextLong());
    }
    for (int i = 0; i < 10; i++) {
        int longs = 10;
        int size = Long.BYTES * longs;
        byte[] bytes = new byte[size];
        (new Random()).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.ByteRandom((x) -> {
            byte[] octects = new byte[x];
            buffer1.get(octects);
            return octects;
        });
        for (int j = 0; j < longs; j++) {
            assertEquals(buffer2.getLong(), random.nextLong());
        }
    }
}

@Test
public void testByteRandomNextLong() {
    SplittableRandom seeder = new SplittableRandom(1);
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.SafeRandom((x) -> bytes);
        assertEquals(number, random.nextLong());
    }
    for (int i = 0; i < 10; i++) {
        int longs = 10;
        int size = Long.BYTES * longs;
        byte[] bytes = new byte[size];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.SafeRandom((x) -> {
            byte[] octects = new byte[x];
            buffer1.get(octects);
            return octects;
        });
        for (int j = 0; j < longs; j++) {
            assertEquals(buffer2.getLong(), random.nextLong());
        }
    }
}

@Override
public long nextLong() {
    byte[] bytes = this.randomFunction.apply(Long.BYTES);
    return ByteUtil.toNumber(bytes);
}

@Test
public void testLongRandom() {
    {
        long nextLong = 0x1122334455667788L;
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.LongRandom(() -> nextLong);
        byte[] bytes = random.nextBytes(Long.BYTES);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        assertEquals(nextLong, buffer.getLong());
    }
    {
        long nextLong = ThreadLocalRandom.current().nextLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.LongRandom(() -> nextLong);
        byte[] bytes = random.nextBytes(Long.BYTES);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        assertEquals(nextLong, buffer.getLong());
    }
    {
        long[] nextLong = { 0x0123456789abcdefL, 0xfedcba9876543210L, 0x1122334455667788L, 0x8877665544332211L };
        int octects = nextLong.length * Byte.SIZE;
        ByteBuffer buffer1 = ByteBuffer.allocate(octects);
        ByteBuffer buffer2 = ByteBuffer.allocate(octects);
        AtomicInteger x = new AtomicInteger();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.LongRandom(() -> nextLong[x.getAndIncrement()]);
        for (int i = 0; i < nextLong.length; i++) {
            buffer1.putLong(nextLong[i]);
            buffer2.put(random.nextBytes(8));
        }
        assertEquals(new BigInteger(1, buffer1.array()).toString(16), new BigInteger(1, buffer2.array()).toString(16));
    }
    {
        long[] nextLong = { ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong() };
        int octects = nextLong.length * Byte.SIZE;
        ByteBuffer buffer1 = ByteBuffer.allocate(octects);
        ByteBuffer buffer2 = ByteBuffer.allocate(octects);
        AtomicInteger x = new AtomicInteger();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.LongRandom(() -> nextLong[x.getAndIncrement()]);
        for (int i = 0; i < nextLong.length; i++) {
            buffer1.putLong(nextLong[i]);
            buffer2.put(random.nextBytes(8));
        }
        assertEquals(Arrays.toString(buffer1.array()), Arrays.toString(buffer2.array()));
    }
}

@Test
public void testLongRandom() {
    {
        long nextLong = 0x1122334455667788L;
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> nextLong);
        byte[] bytes = random.nextBytes(Long.BYTES);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        assertEquals(nextLong, buffer.getLong());
    }
    {
        long nextLong = new SplittableRandom(1).nextLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> nextLong);
        byte[] bytes = random.nextBytes(Long.BYTES);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        assertEquals(nextLong, buffer.getLong());
    }
    {
        long[] nextLong = { 0x0123456789abcdefL, 0xfedcba9876543210L, 0x1122334455667788L, 0x8877665544332211L };
        int octects = nextLong.length * Byte.SIZE;
        ByteBuffer buffer1 = ByteBuffer.allocate(octects);
        ByteBuffer buffer2 = ByteBuffer.allocate(octects);
        AtomicInteger x = new AtomicInteger();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> nextLong[x.getAndIncrement()]);
        for (int i = 0; i < nextLong.length; i++) {
            buffer1.putLong(nextLong[i]);
            buffer2.put(random.nextBytes(8));
        }
        assertEquals(new BigInteger(1, buffer1.array()).toString(16), new BigInteger(1, buffer2.array()).toString(16));
    }
    {
        SplittableRandom entropy = new SplittableRandom(1);
        long[] nextLong = { entropy.nextLong(), entropy.nextLong(), entropy.nextLong(), entropy.nextLong() };
        int octects = nextLong.length * Byte.SIZE;
        ByteBuffer buffer1 = ByteBuffer.allocate(octects);
        ByteBuffer buffer2 = ByteBuffer.allocate(octects);
        AtomicInteger x = new AtomicInteger();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> nextLong[x.getAndIncrement()]);
        for (int i = 0; i < nextLong.length; i++) {
            buffer1.putLong(nextLong[i]);
            buffer2.put(random.nextBytes(8));
        }
        assertEquals(Arrays.toString(buffer1.array()), Arrays.toString(buffer2.array()));
    }
}

@Override
public byte[] nextBytes(int length) {
    int shift = 0;
    long random = 0;
    final byte[] bytes = new byte[length];
    for (int i = 0; i < length; i++) {
        if (shift < Byte.SIZE) {
            shift = Long.SIZE;
            random = randomFunction.getAsLong();
        }
        shift -= Byte.SIZE;
        bytes[i] = (byte) (random >>> shift);
    }
    return bytes;
}

@Test
public void testLogRandomNextBytes() {
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random()).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.LongRandom(() -> number);
        assertEquals(Arrays.toString(bytes), Arrays.toString(random.nextBytes(Long.BYTES)));
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random()).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.LongRandom(() -> buffer1.getLong());
        for (int j = 0; j < ints; j++) {
            byte[] octects = new byte[Long.BYTES];
            buffer2.get(octects);
            assertEquals(Arrays.toString(octects), Arrays.toString(random.nextBytes(Long.BYTES)));
        }
    }
}

@Test
public void testLogRandomNextBytes() {
    SplittableRandom seeder = new SplittableRandom(1);
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> number);
        assertEquals(Arrays.toString(bytes), Arrays.toString(random.nextBytes(Long.BYTES)));
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> buffer1.getLong());
        for (int j = 0; j < ints; j++) {
            byte[] octects = new byte[Long.BYTES];
            buffer2.get(octects);
            assertEquals(Arrays.toString(octects), Arrays.toString(random.nextBytes(Long.BYTES)));
        }
    }
}

@Test
public void testByteRandomNextBytes() {
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random()).nextBytes(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.ByteRandom((x) -> bytes);
        assertEquals(Arrays.toString(bytes), Arrays.toString(random.nextBytes(Long.BYTES)));
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random()).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.ByteRandom((x) -> {
            byte[] octects = new byte[x];
            buffer1.get(octects);
            return octects;
        });
        for (int j = 0; j < ints; j++) {
            byte[] octects = new byte[Long.BYTES];
            buffer2.get(octects);
            assertEquals(Arrays.toString(octects), Arrays.toString(random.nextBytes(Long.BYTES)));
        }
    }
}

@Test
public void testByteRandomNextBytes() {
    SplittableRandom seeder = new SplittableRandom(1);
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.SafeRandom((x) -> bytes);
        assertEquals(Arrays.toString(bytes), Arrays.toString(random.nextBytes(Long.BYTES)));
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.SafeRandom((x) -> {
            byte[] octects = new byte[x];
            buffer1.get(octects);
            return octects;
        });
        for (int j = 0; j < ints; j++) {
            byte[] octects = new byte[Long.BYTES];
            buffer2.get(octects);
            assertEquals(Arrays.toString(octects), Arrays.toString(random.nextBytes(Long.BYTES)));
        }
    }
}

@Override
public byte[] nextBytes(int length) {
    return this.randomFunction.apply(length);
}

@Test
public void testLongRandomWithFactory() {
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = UuidUtil.setVersion(new UUID(list.get(0), list.get(1)), 4);
        RandomBasedFactory factory = new RandomBasedFactory(() -> list.remove(0));
        UUID uuid = factory.create();
        assertEquals(uuid1, uuid);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(ThreadLocalRandom.current().nextLong());
        list.add(ThreadLocalRandom.current().nextLong());
        UUID uuid1 = UuidUtil.setVersion(new UUID(list.get(0), list.get(1)), 4);
        RandomBasedFactory factory = new RandomBasedFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        assertEquals(uuid1, uuid2);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = new UUID(0x0000000000004defL, 0xbedcba9876543210L);
        PrefixCombFactory factory = new PrefixCombFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        String substring1 = uuid1.toString().substring(14);
        String substring2 = uuid2.toString().substring(14);
        assertEquals(substring1, substring2);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = new UUID(0x0123456789ab4defL, 0xb210000000000000L);
        SuffixCombFactory factory = new SuffixCombFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        String substring1 = uuid1.toString().substring(0, 24);
        String substring2 = uuid2.toString().substring(0, 24);
        assertEquals(substring1, substring2);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = new UUID(0x0000456789ab4defL, 0xbedcba9876543210L);
        ShortPrefixCombFactory factory = new ShortPrefixCombFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        String substring1 = uuid1.toString().substring(14);
        String substring2 = uuid2.toString().substring(14);
        assertEquals(substring1, substring2);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = new UUID(0x0123456789ab4defL, 0xba98000076543210L);
        ShortSuffixCombFactory factory = new ShortSuffixCombFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        String substring1 = uuid1.toString().substring(0, 24) + uuid1.toString().substring(28, 36);
        String substring2 = uuid2.toString().substring(0, 24) + uuid2.toString().substring(28, 36);
        assertEquals(substring1, substring2);
    }
}

@Test
public void testLongRandomWithFactory() {
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = UuidUtil.setVersion(new UUID(list.get(0), list.get(1)), 4);
        RandomBasedFactory factory = new RandomBasedFactory(() -> list.remove(0));
        UUID uuid = factory.create();
        assertEquals(uuid1, uuid);
    }
    {
        SplittableRandom random = new SplittableRandom(1);
        List<Long> list = new ArrayList<>();
        list.add(random.nextLong());
        list.add(random.nextLong());
        UUID uuid1 = UuidUtil.setVersion(new UUID(list.get(0), list.get(1)), 4);
        RandomBasedFactory factory = new RandomBasedFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        assertEquals(uuid1, uuid2);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = new UUID(0x0000000000004defL, 0xbedcba9876543210L);
        PrefixCombFactory factory = new PrefixCombFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        String substring1 = uuid1.toString().substring(14);
        String substring2 = uuid2.toString().substring(14);
        assertEquals(substring1, substring2);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = new UUID(0x0123456789ab4defL, 0xb210000000000000L);
        SuffixCombFactory factory = new SuffixCombFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        String substring1 = uuid1.toString().substring(0, 24);
        String substring2 = uuid2.toString().substring(0, 24);
        assertEquals(substring1, substring2);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = new UUID(0x0000456789ab4defL, 0xbedcba9876543210L);
        ShortPrefixCombFactory factory = new ShortPrefixCombFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        String substring1 = uuid1.toString().substring(14);
        String substring2 = uuid2.toString().substring(14);
        assertEquals(substring1, substring2);
    }
    {
        List<Long> list = new ArrayList<>();
        list.add(0x0123456789abcdefL);
        list.add(0xfedcba9876543210L);
        UUID uuid1 = new UUID(0x0123456789ab4defL, 0xba98000076543210L);
        ShortSuffixCombFactory factory = new ShortSuffixCombFactory(() -> list.remove(0));
        UUID uuid2 = factory.create();
        String substring1 = uuid1.toString().substring(0, 24) + uuid1.toString().substring(28, 36);
        String substring2 = uuid2.toString().substring(0, 24) + uuid2.toString().substring(28, 36);
        assertEquals(substring1, substring2);
    }
}

@Test
public void testLogRandomNextLong() {
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random()).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.LongRandom(() -> number);
        assertEquals(number, random.nextLong());
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random()).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.LongRandom(() -> buffer1.getLong());
        for (int j = 0; j < ints; j++) {
            assertEquals(buffer2.getLong(), random.nextLong());
        }
    }
}

@Test
public void testLogRandomNextLong() {
    SplittableRandom seeder = new SplittableRandom(1);
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> number);
        assertEquals(number, random.nextLong());
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random(seeder.nextLong())).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> buffer1.getLong());
        for (int j = 0; j < ints; j++) {
            assertEquals(buffer2.getLong(), random.nextLong());
        }
    }
}

@Override
public long nextLong() {
    return randomFunction.getAsLong();
}


@Test
public void testGetPrefixCombCheckTimestamp() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long random = ThreadLocalRandom.current().nextLong(1L << 48);
        Clock clock = Clock.fixed(Instant.ofEpochMilli(random), Clock.systemUTC().getZone());
        Instant instant1 = clock.instant();
        long timestamp1 = UuidTime.toUnixTimestamp(instant1);
        UUID uuid = PrefixCombFactory.builder().withClock(clock).build().create();
        Instant instant2 = CombUtil.getPrefixInstant(uuid);
        long timestamp2 = CombUtil.getPrefix(uuid) * 10_000;
        assertEquals(instant1, instant2);
        assertEquals(timestamp1, timestamp2);
    }
}

@Test
public void testGetPrefixCombCheckTimestamp() {
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long time = random.nextLong(1L << 48);
        Clock clock = Clock.fixed(Instant.ofEpochMilli(time), Clock.systemUTC().getZone());
        Instant instant1 = clock.instant();
        long timestamp1 = UuidTime.toUnixTimestamp(instant1);
        UUID uuid = PrefixCombFactory.builder().withClock(clock).build().create();
        Instant instant2 = CombUtil.getPrefixInstant(uuid);
        long timestamp2 = CombUtil.getPrefix(uuid) * 10_000;
        assertEquals(instant1, instant2);
        assertEquals(timestamp1, timestamp2);
    }
}

@Test
public void testGetPrefixCombWithRandomFunction() {
    SplittableRandom random = new SplittableRandom(1);
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> random.nextLong();
    PrefixCombFactory factory = new PrefixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
        assertNotNull("UUID is null", list[i]);
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}


@Test
public void testGetShortPrefixCombWithRandomFunction() {
    SplittableRandom random = new SplittableRandom(1);
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> random.nextLong();
    ShortPrefixCombFactory factory = new ShortPrefixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}


@Test
public void testGetShortSuffixCombWithRandomFunction() {
    SplittableRandom random = new SplittableRandom(1);
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> random.nextLong();
    ShortSuffixCombFactory factory = new ShortSuffixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}


@Test
public void testGetSuffixCombCheckTimestamp() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long random = ThreadLocalRandom.current().nextLong(1L << 48);
        Clock clock = Clock.fixed(Instant.ofEpochMilli(random), Clock.systemUTC().getZone());
        Instant instant1 = clock.instant();
        long timestamp1 = UuidTime.toUnixTimestamp(instant1);
        UUID uuid = SuffixCombFactory.builder().withClock(clock).build().create();
        Instant instant2 = CombUtil.getSuffixInstant(uuid);
        long timestamp2 = CombUtil.getSuffix(uuid) * 10_000;
        assertEquals(instant1, instant2);
        assertEquals(timestamp1, timestamp2);
    }
}

@Test
public void testGetSuffixCombCheckTimestamp() {
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long time = random.nextLong(1L << 48);
        Clock clock = Clock.fixed(Instant.ofEpochMilli(time), Clock.systemUTC().getZone());
        Instant instant1 = clock.instant();
        long timestamp1 = UuidTime.toUnixTimestamp(instant1);
        UUID uuid = SuffixCombFactory.builder().withClock(clock).build().create();
        Instant instant2 = CombUtil.getSuffixInstant(uuid);
        long timestamp2 = CombUtil.getSuffix(uuid) * 10_000;
        assertEquals(instant1, instant2);
        assertEquals(timestamp1, timestamp2);
    }
}

@Test
public void testGetSuffixCombWithRandomFunction() {
    SplittableRandom random = new SplittableRandom(1);
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> random.nextLong();
    SuffixCombFactory factory = new SuffixCombFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkOrdering(list);
    checkVersion(list, 4);
    checkUniqueness(list);
}


@Test
public void testGetRandomBasedWithRandomFunction() {
    SplittableRandom random = new SplittableRandom(1);
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    LongSupplier randomFunction = () -> random.nextLong();
    RandomBasedFactory factory = new RandomBasedFactory(randomFunction);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    checkNotNull(list);
    checkUniqueness(list);
    checkVersion(list, UuidVersion.VERSION_RANDOM_BASED.getValue());
}


@Test
public void testGetSuffixWithClock() {
    final int bits = 48;
    final long bound = (long) Math.pow(2, bits);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long millis = Math.abs(ThreadLocalRandom.current().nextLong(bound));
        Clock clock = Clock.fixed(Instant.ofEpochMilli(millis), ZoneOffset.UTC);
        AbstRandomBasedFactory factory = new SuffixCombFactory(clock);
        UUID comb = factory.create();
        long suffix = CombUtil.getSuffix(comb);
        assertEquals("Wrong suffix", millis, suffix);
    }
}

@Test
public void testGetSuffixWithClock() {
    final int bits = 48;
    final long bound = (long) Math.pow(2, bits);
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long millis = Math.abs(random.nextLong(bound));
        Clock clock = Clock.fixed(Instant.ofEpochMilli(millis), ZoneOffset.UTC);
        AbstRandomBasedFactory factory = new SuffixCombFactory(clock);
        UUID comb = factory.create();
        long suffix = CombUtil.getSuffix(comb);
        assertEquals("Wrong suffix", millis, suffix);
    }
}

@Test
public void testGetPrefixWithClock() {
    final int bits = 48;
    final long bound = (long) Math.pow(2, bits);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long millis = Math.abs(ThreadLocalRandom.current().nextLong(bound));
        Clock clock = Clock.fixed(Instant.ofEpochMilli(millis), ZoneOffset.UTC);
        AbstRandomBasedFactory factory = new PrefixCombFactory(clock);
        UUID comb = factory.create();
        long prefix = CombUtil.getPrefix(comb);
        assertEquals("Wrong prefix", millis, prefix);
    }
}

@Test
public void testGetPrefixWithClock() {
    final int bits = 48;
    final long bound = (long) Math.pow(2, bits);
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final long millis = Math.abs(random.nextLong(bound));
        Clock clock = Clock.fixed(Instant.ofEpochMilli(millis), ZoneOffset.UTC);
        AbstRandomBasedFactory factory = new PrefixCombFactory(clock);
        UUID comb = factory.create();
        long prefix = CombUtil.getPrefix(comb);
        assertEquals("Wrong prefix", millis, prefix);
    }
}


@Test
public void testByteRandomNextLong() {
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random()).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.SafeRandom((x) -> bytes);
        assertEquals(number, random.nextLong());
    }
    for (int i = 0; i < 10; i++) {
        int longs = 10;
        int size = Long.BYTES * longs;
        byte[] bytes = new byte[size];
        (new Random()).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.SafeRandom((x) -> {
            byte[] octects = new byte[x];
            buffer1.get(octects);
            return octects;
        });
        for (int j = 0; j < longs; j++) {
            assertEquals(buffer2.getLong(), random.nextLong());
        }
    }
}

@Test
public void testLongRandom() {
    {
        long nextLong = 0x1122334455667788L;
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> nextLong);
        byte[] bytes = random.nextBytes(Long.BYTES);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        assertEquals(nextLong, buffer.getLong());
    }
    {
        long nextLong = ThreadLocalRandom.current().nextLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> nextLong);
        byte[] bytes = random.nextBytes(Long.BYTES);
        ByteBuffer buffer = ByteBuffer.wrap(bytes);
        assertEquals(nextLong, buffer.getLong());
    }
    {
        long[] nextLong = { 0x0123456789abcdefL, 0xfedcba9876543210L, 0x1122334455667788L, 0x8877665544332211L };
        int octects = nextLong.length * Byte.SIZE;
        ByteBuffer buffer1 = ByteBuffer.allocate(octects);
        ByteBuffer buffer2 = ByteBuffer.allocate(octects);
        AtomicInteger x = new AtomicInteger();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> nextLong[x.getAndIncrement()]);
        for (int i = 0; i < nextLong.length; i++) {
            buffer1.putLong(nextLong[i]);
            buffer2.put(random.nextBytes(8));
        }
        assertEquals(new BigInteger(1, buffer1.array()).toString(16), new BigInteger(1, buffer2.array()).toString(16));
    }
    {
        long[] nextLong = { ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong(), ThreadLocalRandom.current().nextLong() };
        int octects = nextLong.length * Byte.SIZE;
        ByteBuffer buffer1 = ByteBuffer.allocate(octects);
        ByteBuffer buffer2 = ByteBuffer.allocate(octects);
        AtomicInteger x = new AtomicInteger();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> nextLong[x.getAndIncrement()]);
        for (int i = 0; i < nextLong.length; i++) {
            buffer1.putLong(nextLong[i]);
            buffer2.put(random.nextBytes(8));
        }
        assertEquals(Arrays.toString(buffer1.array()), Arrays.toString(buffer2.array()));
    }
}

@Test
public void testLogRandomNextBytes() {
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random()).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> number);
        assertEquals(Arrays.toString(bytes), Arrays.toString(random.nextBytes(Long.BYTES)));
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random()).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> buffer1.getLong());
        for (int j = 0; j < ints; j++) {
            byte[] octects = new byte[Long.BYTES];
            buffer2.get(octects);
            assertEquals(Arrays.toString(octects), Arrays.toString(random.nextBytes(Long.BYTES)));
        }
    }
}

@Test
public void testByteRandomNextBytes() {
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random()).nextBytes(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.SafeRandom((x) -> bytes);
        assertEquals(Arrays.toString(bytes), Arrays.toString(random.nextBytes(Long.BYTES)));
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random()).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.SafeRandom((x) -> {
            byte[] octects = new byte[x];
            buffer1.get(octects);
            return octects;
        });
        for (int j = 0; j < ints; j++) {
            byte[] octects = new byte[Long.BYTES];
            buffer2.get(octects);
            assertEquals(Arrays.toString(octects), Arrays.toString(random.nextBytes(Long.BYTES)));
        }
    }
}

@Test
public void testLogRandomNextLong() {
    for (int i = 0; i < 10; i++) {
        byte[] bytes = new byte[Long.BYTES];
        (new Random()).nextBytes(bytes);
        long number = ByteBuffer.wrap(bytes).getLong();
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> number);
        assertEquals(number, random.nextLong());
    }
    for (int i = 0; i < 10; i++) {
        int ints = 10;
        int size = Long.BYTES * ints;
        byte[] bytes = new byte[size];
        (new Random()).nextBytes(bytes);
        ByteBuffer buffer1 = ByteBuffer.wrap(bytes);
        ByteBuffer buffer2 = ByteBuffer.wrap(bytes);
        AbstRandomBasedFactory.IRandom random = new AbstRandomBasedFactory.FastRandom(() -> buffer1.getLong());
        for (int j = 0; j < ints; j++) {
            assertEquals(buffer2.getLong(), random.nextLong());
        }
    }
}


@Test
public void testGetShortPrefixCombCheckTimeWithDifferentInterval() {
    Random random = new SecureRandom();
    Clock clock = Clock.systemUTC();
    int interval = 1000;
    ShortPrefixCombFactory factory = new ShortPrefixCombFactory(random, clock, interval);
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    long startTime = (System.currentTimeMillis() / interval) & 0x000000000000ffffL;
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    long endTime = (System.currentTimeMillis() / interval) & 0x000000000000ffffL;
    checkNotNull(list);
    checkOrdering(list);
    checkUniqueness(list);
    long previous = 0;
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long creationTime = list[i].getMostSignificantBits() >>> 48;
        assertTrue("Comb Guid creation time before start time", startTime <= creationTime);
        assertTrue("Comb Guid creation time after end time", creationTime <= endTime);
        assertTrue("Comb Guid sequence is not sorted " + previous + " " + creationTime, previous <= creationTime);
        previous = creationTime;
    }
}

@Test
public void testGetShortPrefixCombCheckTimeWithDifferentInterval() {
    int interval = 1000;
    ShortPrefixCombFactory factory = ShortPrefixCombFactory.builder().withInterval(interval).build();
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    long startTime = (System.currentTimeMillis() / interval) & 0x000000000000ffffL;
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    long endTime = (System.currentTimeMillis() / interval) & 0x000000000000ffffL;
    checkNotNull(list);
    checkOrdering(list);
    checkUniqueness(list);
    long previous = 0;
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long creationTime = list[i].getMostSignificantBits() >>> 48;
        assertTrue("Comb Guid creation time before start time", startTime <= creationTime);
        assertTrue("Comb Guid creation time after end time", creationTime <= endTime);
        assertTrue("Comb Guid sequence is not sorted " + previous + " " + creationTime, previous <= creationTime);
        previous = creationTime;
    }
}

public Builder withInterval(int interval) {
    this.interval = interval;
    return this;
}


@Test
public void testGetShortSuffixCombCheckTimeWithDifferentInterval() {
    Random random = new SecureRandom();
    Clock clock = Clock.systemUTC();
    int interval = 1000;
    ShortSuffixCombFactory factory = new ShortSuffixCombFactory(random, clock, interval);
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    long startTime = (System.currentTimeMillis() / interval) & 0x000000000000ffffL;
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    long endTime = (System.currentTimeMillis() / interval) & 0x000000000000ffffL;
    checkNotNull(list);
    checkOrdering(list);
    checkUniqueness(list);
    long previous = 0;
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long creationTime = extractSuffix(list[i]);
        assertTrue("Comb Guid creation time before start time", startTime <= creationTime);
        assertTrue("Comb Guid creation time after end time", creationTime <= endTime);
        assertTrue("Comb Guid sequence is not sorted " + previous + " " + creationTime, previous <= creationTime);
        previous = creationTime;
    }
}

@Test
public void testGetShortSuffixCombCheckTimeWithDifferentInterval() {
    int interval = 1000;
    ShortSuffixCombFactory factory = ShortSuffixCombFactory.builder().withInterval(interval).build();
    UUID[] list = new UUID[DEFAULT_LOOP_MAX];
    long startTime = (System.currentTimeMillis() / interval) & 0x000000000000ffffL;
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        list[i] = factory.create();
    }
    long endTime = (System.currentTimeMillis() / interval) & 0x000000000000ffffL;
    checkNotNull(list);
    checkOrdering(list);
    checkUniqueness(list);
    long previous = 0;
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        long creationTime = extractSuffix(list[i]);
        assertTrue("Comb Guid creation time before start time", startTime <= creationTime);
        assertTrue("Comb Guid creation time after end time", creationTime <= endTime);
        assertTrue("Comb Guid sequence is not sorted " + previous + " " + creationTime, previous <= creationTime);
        previous = creationTime;
    }
}


@Test
public void testGetTimeBasedWithOptionalArguments() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final Instant instant = Instant.ofEpochMilli(ThreadLocalRandom.current().nextLong() >>> 24);
        final int clockseq = ThreadLocalRandom.current().nextInt() & 0x000003ff;
        final long nodeid = ThreadLocalRandom.current().nextLong() & 0x0000ffffffffffffL | 0x0000010000000000L;
        UUID uuid = UuidCreator.getTimeBased(instant, clockseq, nodeid);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The node identifier is incorrect", nodeid, UuidUtil.getNodeIdentifier(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
        uuid = UuidCreator.getTimeBasedWithMac(instant, clockseq);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
        uuid = UuidCreator.getTimeBasedWithHash(instant, clockseq);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
    }
}

@Test
public void testGetTimeBasedWithOptionalArguments() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final Instant instant = Instant.ofEpochMilli(ThreadLocalRandom.current().nextLong() >>> 24);
        final int clockseq = ThreadLocalRandom.current().nextInt() & 0x000003ff;
        final long nodeid = ThreadLocalRandom.current().nextLong() & 0x0000ffffffffffffL | 0x0000010000000000L;
        UUID uuid = UuidCreator.getTimeBased(instant, clockseq, nodeid);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The node identifier is incorrect", nodeid, UuidUtil.getNodeIdentifier(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
    }
}

public static UUID getTimeBasedWithHash(Instant instant, Integer clockseq) {
    return TimeBasedWithHashHolder.INSTANCE.create(instant, clockseq, null);
}


@Test
public void testGetTimeOrderedWithOptionalArguments() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final Instant instant = Instant.ofEpochMilli(ThreadLocalRandom.current().nextLong() >>> 24);
        final int clockseq = ThreadLocalRandom.current().nextInt() & 0x000003ff;
        final long nodeid = ThreadLocalRandom.current().nextLong() & 0x0000ffffffffffffL | 0x0000010000000000L;
        UUID uuid = UuidCreator.getTimeOrdered(instant, clockseq, nodeid);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The node identifier is incorrect", nodeid, UuidUtil.getNodeIdentifier(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
        uuid = UuidCreator.getTimeOrderedWithMac(instant, clockseq);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
        uuid = UuidCreator.getTimeOrderedWithHash(instant, clockseq);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
    }
}

@Test
public void testGetTimeOrderedWithOptionalArguments() {
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final Instant instant = Instant.ofEpochMilli(ThreadLocalRandom.current().nextLong() >>> 24);
        final int clockseq = ThreadLocalRandom.current().nextInt() & 0x000003ff;
        final long nodeid = ThreadLocalRandom.current().nextLong() & 0x0000ffffffffffffL | 0x0000010000000000L;
        UUID uuid = UuidCreator.getTimeOrdered(instant, clockseq, nodeid);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The node identifier is incorrect", nodeid, UuidUtil.getNodeIdentifier(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
    }
}

public static UUID getTimeOrderedWithHash(Instant instant, Integer clockseq) {
    return TimeOrderedWithHashHolder.INSTANCE.create(instant, clockseq, null);
}


@Test
public void testGetTimeBasedWithOptionalArguments() {
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final Instant instant = Instant.ofEpochMilli(random.nextLong() >>> 24);
        final int clockseq = random.nextInt() & 0x000003ff;
        final long nodeid = random.nextLong() & 0x0000ffffffffffffL | 0x0000010000000000L;
        UUID uuid = UuidCreator.getTimeBased(instant, clockseq, nodeid);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The node identifier is incorrect", nodeid, UuidUtil.getNodeIdentifier(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
    }
}

@Test
public void testGetTimeBasedWithOptionalArguments() {
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final Instant instant = Instant.ofEpochMilli(random.nextLong() >>> 24);
        final int clockseq = random.nextInt() & 0x000003ff;
        final long nodeid = random.nextLong() & 0x0000ffffffffffffL | 0x0000010000000000L;
        UUID uuid = UuidCreator.getTimeBased(instant, clockseq, nodeid);
        assertTrue(UuidUtil.isStandard(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The node identifier is incorrect", nodeid, UuidUtil.getNodeIdentifier(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
    }
}

public static boolean isStandard(UUID uuid) {
    return isVariant(uuid, UuidVariant.VARIANT_STANDARD);
}


@Test
public void testGetTimeOrderedWithOptionalArguments() {
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final Instant instant = Instant.ofEpochMilli(random.nextLong() >>> 24);
        final int clockseq = random.nextInt() & 0x000003ff;
        final long nodeid = random.nextLong() & 0x0000ffffffffffffL | 0x0000010000000000L;
        UUID uuid = UuidCreator.getTimeOrdered(instant, clockseq, nodeid);
        assertTrue(UuidUtil.isRfc4122(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The node identifier is incorrect", nodeid, UuidUtil.getNodeIdentifier(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
    }
}

@Test
public void testGetTimeOrderedWithOptionalArguments() {
    SplittableRandom random = new SplittableRandom(1);
    for (int i = 0; i < DEFAULT_LOOP_MAX; i++) {
        final Instant instant = Instant.ofEpochMilli(random.nextLong() >>> 24);
        final int clockseq = random.nextInt() & 0x000003ff;
        final long nodeid = random.nextLong() & 0x0000ffffffffffffL | 0x0000010000000000L;
        UUID uuid = UuidCreator.getTimeOrdered(instant, clockseq, nodeid);
        assertTrue(UuidUtil.isStandard(uuid));
        assertEquals("The timestamp is incorrect.", instant, UuidUtil.getInstant(uuid));
        assertEquals("The node identifier is incorrect", nodeid, UuidUtil.getNodeIdentifier(uuid));
        assertEquals("The clock sequence is incorrect", clockseq, UuidUtil.getClockSequence(uuid));
    }
}


@Test
public void testIsRfc4122() {
    UUID uuid1 = UuidNamespace.NAMESPACE_DNS.getValue();
    UUID uuid2 = UuidCreator.getNil();
    assertTrue(isRfc4122(uuid1));
    assertFalse(isRfc4122(uuid2));
}

@Test
public void testIsRfc4122() {
    UUID uuid1 = UuidNamespace.NAMESPACE_DNS.getValue();
    UUID uuid2 = UuidCreator.getNil();
    assertTrue(isStandard(uuid1));
    assertFalse(isStandard(uuid2));
}

public static boolean isRfc4122(UUID uuid) {
    return isVariant(uuid, UuidVariant.VARIANT_RFC_4122);
}