@Test
public void test_LongestWord() {
    V2_AsciiTable at;
    int[] cols;
    at = new V2_AsciiTable();
    at.addRule();
    at.addRow("first row (col1)", "with some information (col2)");
    at.addRule();
    at.addRow("second row (col1)", "with some information (col2)");
    at.addRule();
    cols = WidthUtilities.longestWord(at);
    assertEquals(2, cols.length);
    assertEquals(8, cols[0]);
    assertEquals(13, cols[1]);
    System.out.println(ArrayUtils.toString(cols));
    at = new V2_AsciiTable();
    at.addRow(new LoremIpsum().getWords());
    cols = WidthUtilities.longestWord(at);
    assertEquals(1, cols.length);
    assertEquals(12, cols[0]);
    System.out.println(ArrayUtils.toString(cols));
}

public static int[] longestWord(V2_AsciiTable table) {
    if (table == null) {
        return null;
    }
    if (table.getTable().size() == 0) {
        return new int[0];
    }
    int[] ret = new int[table.getColumnCount()];
    for (V2_Row row : table.getTable()) {
        if (row instanceof ContentRow) {
            ContentRow crow = (ContentRow) row;
            for (int i = 0; i < crow.getColumns().length; i++) {
                if (crow.getColumns()[i] != null) {
                    String[] ar = StringUtils.split(crow.getColumns()[i].toString());
                    for (int k = 0; k < ar.length; k++) {
                        int count = ar[k].length() + crow.getPadding()[i] + crow.getPadding()[i];
                        if (count > ret[i]) {
                            ret[i] = count;
                        }
                    }
                }
            }
        }
    }
    return ret;
}


@Test
public void isOnlyEmojis_for_random_string_returns_false() {
    String str = "😀a";
    boolean isEmoji = EmojiManager.isOnlyEmojis(str);
    assertFalse(isEmoji);
}

public static boolean isOnlyEmojis(String string) {
    return string != null && EmojiParser.removeAllEmojis(string).isEmpty();
}

@Test
public void isOnlyEmojis_for_an_emoji_returns_true() {
    String str = "😀";
    boolean isEmoji = EmojiManager.isOnlyEmojis(str);
    assertTrue(isEmoji);
}

@Test
public void isOnlyEmojis_for_emojis_returns_true() {
    String str = "😀😀😀";
    boolean isEmoji = EmojiManager.isOnlyEmojis(str);
    assertTrue(isEmoji);
}


@Test
public void getAll_doesnt_return_duplicates() {
    Collection<Emoji> emojis = EmojiManager.getAll();
    Set<String> unicodes = new HashSet<String>();
    for (Emoji emoji : emojis) {
        assertFalse("Duplicate: " + emoji.getDescription(), unicodes.contains(emoji.getUnicode()));
        unicodes.add(emoji.getUnicode());
    }
    assertEquals(unicodes.size(), emojis.size());
}

public static Collection<Emoji> getAll() {
    return EMOJIS_BY_ALIAS.values();
}

public static Collection<Emoji> getAll() {
    return ALL_EMOJIS;
}


@Test
public void getAliasCanditates_with_one_alias_an_another_colon_after() {
    String str = "test :candidate: test:";
    List<AliasCandidate> candidates = EmojiParser.getAliasCandidates(str);
    assertEquals(1, candidates.size());
    assertEquals("candidate", candidates.get(0).alias);
    assertNull(candidates.get(0).fitzpatrick);
}

protected static List<AliasCandidate> getAliasCandidates(String input) {
    List<AliasCandidate> candidates = new ArrayList<AliasCandidate>();
    String regex = "(?<=:)\\+?(\\w|\\|)+(?=:)";
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(input);
    matcher = matcher.useTransparentBounds(true);
    while (matcher.find()) {
        String match = matcher.group();
        if (!match.contains("|")) {
            candidates.add(new AliasCandidate(match, match, null));
        } else {
            String[] splitted = match.split("\\|");
            if (splitted.length == 2 || splitted.length > 2) {
                candidates.add(new AliasCandidate(match, splitted[0], splitted[1]));
            } else {
                candidates.add(new AliasCandidate(match, match, null));
            }
        }
    }
    return candidates;
}

@Test
public void parseToHtmlHexadecimal_PARSE_with_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66\uD83C\uDFFF";
    String result = EmojiParser.parseToHtmlHexadecimal(str, FitzpatrickAction.PARSE);
    assertEquals("👦", result);
}

public static String parseToHtmlHexadecimal(String input, FitzpatrickAction fitzpatrickAction) {
    String result = prepareParsing(input, fitzpatrickAction);
    for (Emoji emoji : EmojiManager.getAll()) {
        if (fitzpatrickAction != FitzpatrickAction.REMOVE) {
            if (emoji.supportsFitzpatrick()) {
                for (Fitzpatrick fitzpatrick : Fitzpatrick.values()) {
                    String search = fitzpatrickAction == FitzpatrickAction.PARSE ? emoji.getUnicode(fitzpatrick) : emoji.getUnicode();
                    result = result.replace(search, emoji.getHtmlHexidecimal());
                }
            }
        }
        result = result.replace(emoji.getUnicode(), emoji.getHtmlHexidecimal());
    }
    return result;
}

@Test
public void parseToHtmlHexadecimal_IGNORE_with_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66\uD83C\uDFFF";
    String result = EmojiParser.parseToHtmlHexadecimal(str, FitzpatrickAction.IGNORE);
    assertEquals("👦\uD83C\uDFFF", result);
}

@Test
public void parseToHtmlDecimal_REMOVE_with_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66\uD83C\uDFFF";
    String result = EmojiParser.parseToHtmlDecimal(str, FitzpatrickAction.REMOVE);
    assertEquals("👦", result);
}

public static String parseToHtmlDecimal(String input, FitzpatrickAction fitzpatrickAction) {
    String result = prepareParsing(input, fitzpatrickAction);
    for (Emoji emoji : EmojiManager.getAll()) {
        if (fitzpatrickAction != FitzpatrickAction.REMOVE) {
            if (emoji.supportsFitzpatrick()) {
                for (Fitzpatrick fitzpatrick : Fitzpatrick.values()) {
                    String search = fitzpatrickAction == FitzpatrickAction.PARSE ? emoji.getUnicode(fitzpatrick) : emoji.getUnicode();
                    result = result.replace(search, emoji.getHtmlDecimal());
                }
            }
        }
        result = result.replace(emoji.getUnicode(), emoji.getHtmlDecimal());
    }
    return result;
}

@Test
public void getAliasCanditates_with_a_fitzpatrick_modifier() {
    String str = "test :candidate|type_3: test";
    List<AliasCandidate> candidates = EmojiParser.getAliasCandidates(str);
    assertEquals(1, candidates.size());
    assertEquals("candidate", candidates.get(0).alias);
    assertEquals(Fitzpatrick.TYPE_3, candidates.get(0).fitzpatrick);
}

@Test
public void getAliasCanditates_with_one_alias_an_another_colon_before_after() {
    String str = "test ::candidate: test";
    List<AliasCandidate> candidates = EmojiParser.getAliasCandidates(str);
    assertEquals(1, candidates.size());
    assertEquals("candidate", candidates.get(0).alias);
    assertNull(candidates.get(0).fitzpatrick);
}

@Test
public void parseToHtmlDecimal_PARSE_with_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66\uD83C\uDFFF";
    String result = EmojiParser.parseToHtmlDecimal(str, FitzpatrickAction.PARSE);
    assertEquals("👦", result);
}

@Test
public void parseToAliases_IGNORE_without_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66";
    String result = EmojiParser.parseToAliases(str, FitzpatrickAction.IGNORE);
    assertEquals(":boy:", result);
}

public static String parseToAliases(String input, FitzpatrickAction fitzpatrickAction) {
    String result = prepareParsing(input, fitzpatrickAction);
    for (Emoji emoji : EmojiManager.getAll()) {
        if (fitzpatrickAction != FitzpatrickAction.REMOVE) {
            if (emoji.supportsFitzpatrick()) {
                for (Fitzpatrick fitzpatrick : Fitzpatrick.values()) {
                    String replacement = ":" + emoji.getAliases().get(0) + ":";
                    if (fitzpatrickAction == FitzpatrickAction.PARSE) {
                        replacement = ":" + emoji.getAliases().get(0) + "|" + fitzpatrick.toString().toLowerCase() + ":";
                    }
                    result = result.replace(emoji.getUnicode(fitzpatrick), replacement);
                }
            }
        }
        result = result.replace(emoji.getUnicode(), ":" + emoji.getAliases().get(0) + ":");
    }
    return result;
}

@Test
public void parseToAliases_REMOVE_with_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66\uD83C\uDFFF";
    String result = EmojiParser.parseToAliases(str, FitzpatrickAction.REMOVE);
    assertEquals(":boy:", result);
}

@Test
public void getAliasCanditates_with_one_alias() {
    String str = "test :candidate: test";
    List<AliasCandidate> candidates = EmojiParser.getAliasCandidates(str);
    assertEquals(1, candidates.size());
    assertEquals("candidate", candidates.get(0).alias);
    assertNull(candidates.get(0).fitzpatrick);
}

@Test
public void parseToHtmlHexadecimal_REMOVE_with_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66\uD83C\uDFFF";
    String result = EmojiParser.parseToHtmlHexadecimal(str, FitzpatrickAction.REMOVE);
    assertEquals("👦", result);
}

@Test
public void parseToAliases_IGNORE_with_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66\uD83C\uDFFF";
    String result = EmojiParser.parseToAliases(str, FitzpatrickAction.IGNORE);
    assertEquals(":boy:", result);
}

@Test
public void parseToHtmlDecimal_IGNORE_with_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66\uD83C\uDFFF";
    String result = EmojiParser.parseToHtmlDecimal(str, FitzpatrickAction.IGNORE);
    assertEquals("👦\uD83C\uDFFF", result);
}

@Test
public void parseToUnicode_with_a_fitzpatrick_modifier() {
    String str = ":boy|type_6:";
    String result = EmojiParser.parseToUnicode(str);
    assertEquals("\uD83D\uDC66\uD83C\uDFFF", result);
}

public static String parseToUnicode(String input) {
    List<String> aliases = getAliasesCandidates(input);
    String result = input;
    for (String alias : aliases) {
        Emoji emoji = EmojiManager.getForAlias(alias);
        if (emoji != null) {
            result = result.replace(":" + alias + ":", emoji.getUnicode());
        }
    }
    for (Emoji emoji : EmojiManager.getAll()) {
        result = result.replace(emoji.getHtmlHexidecimal(), emoji.getUnicode());
        result = result.replace(emoji.getHtml(), emoji.getUnicode());
    }
    return result;
}

public static String parseToUnicode(String input) {
    List<AliasCandidate> candidates = getAliasCandidates(input);
    String result = input;
    for (AliasCandidate candidate : candidates) {
        Emoji emoji = EmojiManager.getForAlias(candidate.alias);
        if (emoji != null) {
            if (emoji.supportsFitzpatrick() || (!emoji.supportsFitzpatrick() && candidate.fitzpatrick == null)) {
                String replacement = emoji.getUnicode();
                if (candidate.fitzpatrick != null) {
                    replacement += candidate.fitzpatrick.unicode;
                }
                result = result.replace(":" + candidate.fullString + ":", replacement);
            }
        }
    }
    for (Emoji emoji : EmojiManager.getAll()) {
        result = result.replace(emoji.getHtmlHexidecimal(), emoji.getUnicode());
        result = result.replace(emoji.getHtml(), emoji.getUnicode());
    }
    return result;
}

@Test
public void getAliasCanditates_with_two_aliases_sharing_a_colon() {
    String str = "test :candi:candidate: test";
    List<AliasCandidate> candidates = EmojiParser.getAliasCandidates(str);
    assertEquals(2, candidates.size());
    assertEquals("candi", candidates.get(0).alias);
    assertNull(candidates.get(0).fitzpatrick);
    assertEquals("candidate", candidates.get(1).alias);
    assertNull(candidates.get(1).fitzpatrick);
}

@Test
public void parseToAliases_REMOVE_without_a_fitzpatrick_modifier() {
    String str = "\uD83D\uDC66";
    String result = EmojiParser.parseToAliases(str, FitzpatrickAction.REMOVE);
    assertEquals(":boy:", result);
}

@Test
public void getAliasCanditates_with_one_alias_an_another_colon_right_after() {
    String str = "test :candidate::test";
    List<AliasCandidate> candidates = EmojiParser.getAliasCandidates(str);
    assertEquals(1, candidates.size());
    assertEquals("candidate", candidates.get(0).alias);
    assertNull(candidates.get(0).fitzpatrick);
}

@Test
public void getAliasCanditates_with_two_aliases() {
    String str = "test :candi: :candidate: test";
    List<AliasCandidate> candidates = EmojiParser.getAliasCandidates(str);
    assertEquals(2, candidates.size());
    assertEquals("candi", candidates.get(0).alias);
    assertNull(candidates.get(0).fitzpatrick);
    assertEquals("candidate", candidates.get(1).alias);
    assertNull(candidates.get(1).fitzpatrick);
}


@Test
public void removeAllEmojis_removes_all_the_emojis_from_the_string() {
    String input = "An 😀awesome 😃string 😄with a \uD83D\uDC66\uD83C\uDFFFfew 😉emojis!";
    String expected = "An awesome string with a few emojis!";
    assertEquals(expected, EmojiParser.removeAllEmojis(input));
}

public static String removeAllEmojis(String str) {
    for (Fitzpatrick fitzpatrick : Fitzpatrick.values()) {
        str = str.replaceAll(fitzpatrick.unicode, "");
    }
    for (Emoji emoji : EmojiManager.getAll()) {
        str = str.replaceAll(emoji.getUnicode(), "");
    }
    return str;
}

@Test
public void removeEmojis_only_removes_the_emojis_in_the_iterable_from_the_string() {
    String input = "An 😀awesome 😃string 😄with a \uD83D\uDC66\uD83C\uDFFFfew 😉emojis!";
    String expected = "An awesome string with a few emojis!";
    assertEquals(expected, EmojiParser.removeAllEmojis(input));
}


@Test
public void removeAllEmojis_test() {
    String input = "An 😀awesome 😃string 😄with a \uD83D\uDC66\uD83C\uDFFFfew 😉emojis!";
    String expected = "An awesome string with a few emojis!";
    assertEquals(expected, EmojiParser.removeAllEmojis(input));
}


@Test
public void buildEmojiFromJSON_computes_the_html() throws UnsupportedEncodingException {
    JSONObject json = new JSONObject("{" + "\"emoji\": \"😄\"," + "\"description\": \"smiling face with open mouth and smiling eyes\"," + "\"aliases\": [\"smile\"]," + "\"tags\": [\"happy\", \"joy\", \"pleased\"]" + "}");
    Emoji emoji = EmojiLoader.buildEmojiFromJSON(json);
    assertEquals("😄", emoji.getUnicode());
    assertEquals("😄", emoji.getHtml());
}

protected static Emoji buildEmojiFromJSON(JSONObject json) throws UnsupportedEncodingException {
    if (!json.has("emoji")) {
        return null;
    }
    byte[] bytes = json.getString("emoji").getBytes("UTF-8");
    String description = null;
    if (json.has("description")) {
        description = json.getString("description");
    }
    List<String> aliases = jsonArrayToStringList(json.getJSONArray("aliases"));
    List<String> tags = jsonArrayToStringList(json.getJSONArray("tags"));
    return new Emoji(description, aliases, tags, bytes);
}

protected static Emoji buildEmojiFromJSON(JSONObject json) throws UnsupportedEncodingException {
    if (!json.has("emoji")) {
        return null;
    }
    byte[] bytes = json.getString("emoji").getBytes("UTF-8");
    String description = null;
    if (json.has("description")) {
        description = json.getString("description");
    }
    List<String> aliases = jsonArrayToStringList(json.getJSONArray("aliases"));
    List<String> tags = jsonArrayToStringList(json.getJSONArray("tags"));
    String html = getHtmlFromUnicode(bytes);
    return new Emoji(description, aliases, tags, html, bytes);
}


@Test
public void buildEmojiFromJSON_with_support_for_fitzpatrick_false() throws UnsupportedEncodingException {
    JSONObject json = new JSONObject("{\n" + "    \"emoji\": \"\uD83D\uDE15\",\n" + "    \"description\": \"confused face\",\n" + "    \"supports_fitzpatrick\": false,\n" + "    \"aliases\": [\n" + "      \"confused\"\n" + "    ],\n" + "    \"tags\": [\n" + "    ]\n" + "  }");
    Emoji emoji = EmojiLoader.buildEmojiFromJSON(json);
    assertFalse(emoji.supportsFitzpatrick());
}

protected static Emoji buildEmojiFromJSON(JSONObject json) throws UnsupportedEncodingException {
    if (!json.has("emoji")) {
        return null;
    }
    byte[] bytes = json.getString("emoji").getBytes("UTF-8");
    String description = null;
    if (json.has("description")) {
        description = json.getString("description");
    }
    List<String> aliases = jsonArrayToStringList(json.getJSONArray("aliases"));
    List<String> tags = jsonArrayToStringList(json.getJSONArray("tags"));
    int html = getHtmlCodeFromBytes(bytes);
    return new Emoji(description, aliases, tags, html, bytes);
}

protected static Emoji buildEmojiFromJSON(JSONObject json) throws UnsupportedEncodingException {
    if (!json.has("emoji")) {
        return null;
    }
    byte[] bytes = json.getString("emoji").getBytes("UTF-8");
    String description = null;
    if (json.has("description")) {
        description = json.getString("description");
    }
    boolean supportsFitzpatrick = false;
    if (json.has("supports_fitzpatrick")) {
        supportsFitzpatrick = json.getBoolean("supports_fitzpatrick");
    }
    List<String> aliases = jsonArrayToStringList(json.getJSONArray("aliases"));
    List<String> tags = jsonArrayToStringList(json.getJSONArray("tags"));
    int html = getHtmlCodeFromBytes(bytes);
    return new Emoji(description, supportsFitzpatrick, aliases, tags, html, bytes);
}

@Test
public void buildEmojiFromJSON_without_support_for_fitzpatrick() throws UnsupportedEncodingException {
    JSONObject json = new JSONObject("{\n" + "    \"emoji\": \"\uD83D\uDE15\",\n" + "    \"description\": \"confused face\",\n" + "    \"aliases\": [\n" + "      \"confused\"\n" + "    ],\n" + "    \"tags\": [\n" + "    ]\n" + "  }");
    Emoji emoji = EmojiLoader.buildEmojiFromJSON(json);
    assertFalse(emoji.supportsFitzpatrick());
}

@Test
public void buildEmojiFromJSON_with_support_for_fitzpatrick_true() throws UnsupportedEncodingException {
    JSONObject json = new JSONObject("{\n" + "    \"emoji\": \"\uD83D\uDC66\",\n" + "    \"description\": \"boy\",\n" + "    \"supports_fitzpatrick\": true,\n" + "    \"aliases\": [\n" + "      \"boy\"\n" + "    ],\n" + "    \"tags\": [\n" + "      \"child\"\n" + "    ]\n" + "  }");
    Emoji emoji = EmojiLoader.buildEmojiFromJSON(json);
    assertTrue(emoji.supportsFitzpatrick());
}


@Test
public void buildEmojiFromJSON_with_support_for_fitzpatrick_false() throws UnsupportedEncodingException {
    JSONObject json = new JSONObject("{\n" + "    \"emoji\": \"\uD83D\uDE15\",\n" + "    \"description\": \"confused face\",\n" + "    \"supports_fitzpatrick\": false,\n" + "    \"aliases\": [\n" + "      \"confused\"\n" + "    ],\n" + "    \"tags\": [\n" + "    ]\n" + "  }");
    Emoji emoji = EmojiLoader.buildEmojiFromJSON(json);
    assertNotNull(emoji);
    assertFalse(emoji.supportsFitzpatrick());
}

@Test
public void buildEmojiFromJSON_without_support_for_fitzpatrick() throws UnsupportedEncodingException {
    JSONObject json = new JSONObject("{\n" + "    \"emoji\": \"\uD83D\uDE15\",\n" + "    \"description\": \"confused face\",\n" + "    \"aliases\": [\n" + "      \"confused\"\n" + "    ],\n" + "    \"tags\": [\n" + "    ]\n" + "  }");
    Emoji emoji = EmojiLoader.buildEmojiFromJSON(json);
    assertNotNull(emoji);
    assertFalse(emoji.supportsFitzpatrick());
}

@Test
public void buildEmojiFromJSON_with_support_for_fitzpatrick_true() throws UnsupportedEncodingException {
    JSONObject json = new JSONObject("{\n" + "    \"emoji\": \"\uD83D\uDC66\",\n" + "    \"description\": \"boy\",\n" + "    \"supports_fitzpatrick\": true,\n" + "    \"aliases\": [\n" + "      \"boy\"\n" + "    ],\n" + "    \"tags\": [\n" + "      \"child\"\n" + "    ]\n" + "  }");
    Emoji emoji = EmojiLoader.buildEmojiFromJSON(json);
    assertNotNull(emoji);
    assertTrue(emoji.supportsFitzpatrick());
}


@Test
public void parseToHtmlDecimal_replaces_the_emojis_by_their_html_decimal_representation() {
    String str = "An 😀awesome 😃string with a few 😉emojis!";
    String result = EmojiParser.parseToHtmlDecimal(str);
    assertEquals("An 😀awesome 😃string with a few 😉emojis!", result);
}

public static String parseToHtmlDecimal(String input) {
    String result = input;
    for (Emoji emoji : EmojiManager.getAll()) {
        result = result.replace(emoji.getUnicode(), emoji.getHtmlDecimal());
    }
    return result;
}


@Test
public void getAliasAt_with_one_alias() {
    String str = "test :boy: test";
    AliasCandidate candidate = EmojiParser.getAliasAt(str, 5);
    assertTrue(candidate.emoji.getAliases().contains("boy"));
    assertNull(candidate.fitzpatrick);
}

protected static AliasCandidate getAliasAt(String input, int start) {
    if (input.charAt(start) != ':')
        return null;
    int aliasEnd = input.indexOf(':', start + 2);
    if (aliasEnd == -1)
        return null;
    int fitzpatrickStart = input.indexOf('|', start + 2);
    if (fitzpatrickStart != -1 && fitzpatrickStart < aliasEnd) {
        Emoji emoji = EmojiManager.getForAlias(input.substring(start, fitzpatrickStart));
        if (emoji == null)
            return null;
        if (!emoji.supportsFitzpatrick())
            return null;
        Fitzpatrick fitzpatrick = Fitzpatrick.fitzpatrickFromType(input.substring(fitzpatrickStart + 1, aliasEnd));
        return new AliasCandidate(emoji, fitzpatrick, start, aliasEnd);
    }
    Emoji emoji = EmojiManager.getForAlias(input.substring(start, aliasEnd));
    if (emoji == null)
        return null;
    return new AliasCandidate(emoji, null, start, aliasEnd);
}

@Test
public void getAliasAt_with_one_alias_an_another_colon_before_after() {
    String str = "test ::boy: test";
    AliasCandidate candidate = EmojiParser.getAliasAt(str, 5);
    assertNull(candidate);
    candidate = EmojiParser.getAliasAt(str, 6);
    assertTrue(candidate.emoji.getAliases().contains("boy"));
    assertNull(candidate.fitzpatrick);
}

@Test
public void getAliasAt_with_one_alias_an_another_colon_right_after() {
    String str = "test :boy::test";
    AliasCandidate candidate = EmojiParser.getAliasAt(str, 5);
    assertTrue(candidate.emoji.getAliases().contains("boy"));
    assertNull(candidate.fitzpatrick);
}

@Test
public void getAliasAt_with_one_alias_an_another_colon_after() {
    String str = "test :boy: test:";
    AliasCandidate candidate = EmojiParser.getAliasAt(str, 5);
    assertTrue(candidate.emoji.getAliases().contains("boy"));
    assertNull(candidate.fitzpatrick);
}

@Test
public void getAliasAt_with_a_fitzpatrick_modifier() {
    String str = "test :boy|type_3: test";
    AliasCandidate candidate = EmojiParser.getAliasAt(str, 5);
    assertTrue(candidate.emoji.getAliases().contains("boy"));
    assertEquals(Fitzpatrick.TYPE_3, candidate.fitzpatrick);
}


@Test
public void testAddingTriggersAfterSettle() {
    HtmxResponse response = new HtmxResponse().addTrigger("event", null, HxTriggerLifecycle.SETTLE);
    assertThat(response.getTriggersAfterSettle()).containsKey("event");
}

Map<String, String> getTriggersAfterSettle() {
    if (this.triggers.isEmpty()) {
        return Collections.emptyMap();
    }
    return new HashMap<>(this.triggersAfterSettle);
}

Map<String, String> getTriggersAfterSettle() {
    if (this.triggersAfterSettle.isEmpty()) {
        return Collections.emptyMap();
    }
    return new HashMap<>(this.triggersAfterSettle);
}

@Test
public void testAddingTriggersAfterSwap() {
    HtmxResponse response = new HtmxResponse().addTrigger("event", null, HxTriggerLifecycle.SWAP);
    assertThat(response.getTriggersAfterSwap()).containsKey("event");
}

Map<String, String> getTriggersAfterSwap() {
    if (this.triggers.isEmpty()) {
        return Collections.emptyMap();
    }
    return new HashMap<>(this.triggersAfterSwap);
}

Map<String, String> getTriggersAfterSwap() {
    if (this.triggersAfterSwap.isEmpty()) {
        return Collections.emptyMap();
    }
    return new HashMap<>(this.triggersAfterSwap);
}


@Test
public void andShouldOverrideProperties() {
    HtmxResponse response1 = new HtmxResponse().retarget("selector1").reswap(HxSwapType.INNER_HTML).browserRedirect("url1").browserRefresh(false).pushHistory("url1");
    HtmxResponse response2 = new HtmxResponse().retarget("selector2").reswap(HxSwapType.OUTER_HTML).browserRedirect("url2").browserRefresh(true).pushHistory("url2");
    response1.and(response2);
    assertThat(response1).satisfies(response -> {
        assertThat(response.getHeaderRetarget()).isEqualTo("selector2");
        assertThat(response.getHeaderReswap()).isEqualTo(HxSwapType.OUTER_HTML.getValue());
        assertThat(response.getHeaderRedirect()).isEqualTo("url2");
        assertThat(response.getHeaderRefresh()).isEqualTo(true);
        assertThat(response.getHeaderPushHistory()).isEqualTo("url2");
    });
}

public HtmxResponse and(HtmxResponse otherResponse) {
    otherResponse.templates.forEach(otherTemplate -> {
        if (this.templates.stream().anyMatch(mav -> same(otherTemplate, mav))) {
            LOG.info("Duplicate template '{}' found while merging HtmxResponse", otherTemplate);
        } else {
            templates.add(otherTemplate);
        }
    });
    mergeMapAndLog(HxTriggerLifecycle.RECEIVE, this.triggers, otherResponse.triggers);
    mergeMapAndLog(HxTriggerLifecycle.SETTLE, this.triggersAfterSettle, otherResponse.triggersAfterSettle);
    mergeMapAndLog(HxTriggerLifecycle.SWAP, this.triggersAfterSwap, otherResponse.triggersAfterSwap);
    if (otherResponse.getHeaderPushHistory() != null) {
        this.headerPushHistory = otherResponse.getHeaderPushHistory();
    }
    if (otherResponse.getHeaderRedirect() != null) {
        this.headerRedirect = otherResponse.getHeaderRedirect();
    }
    if (otherResponse.getHeaderRefresh()) {
        this.headerRefresh = true;
    }
    if (otherResponse.getHeaderRedirect() != null) {
        this.headerRedirect = otherResponse.getHeaderRedirect();
    }
    if (otherResponse.getHeaderReswap() != null) {
        this.headerReswap = otherResponse.getHeaderReswap();
    }
    return this;
}

public HtmxResponse and(HtmxResponse otherResponse) {
    otherResponse.templates.forEach(otherTemplate -> {
        if (this.templates.stream().anyMatch(mav -> same(otherTemplate, mav))) {
            LOG.info("Duplicate template '{}' found while merging HtmxResponse", otherTemplate);
        } else {
            templates.add(otherTemplate);
        }
    });
    mergeMapAndLog(HxTriggerLifecycle.RECEIVE, this.triggers, otherResponse.triggers);
    mergeMapAndLog(HxTriggerLifecycle.SETTLE, this.triggersAfterSettle, otherResponse.triggersAfterSettle);
    mergeMapAndLog(HxTriggerLifecycle.SWAP, this.triggersAfterSwap, otherResponse.triggersAfterSwap);
    if (otherResponse.getHeaderPushHistory() != null) {
        this.headerPushHistory = otherResponse.getHeaderPushHistory();
    }
    if (otherResponse.getHeaderRedirect() != null) {
        this.headerRedirect = otherResponse.getHeaderRedirect();
    }
    if (otherResponse.getHeaderRefresh()) {
        this.headerRefresh = true;
    }
    if (otherResponse.getHeaderRetarget() != null) {
        this.headerRetarget = otherResponse.getHeaderRetarget();
    }
    if (otherResponse.getHeaderReswap() != null) {
        this.headerReswap = otherResponse.getHeaderReswap();
    }
    return this;
}


@Test
public void testHxTriggerWithoutDetails() throws Exception {
    mockMvc.perform(get("/hvhi/hx-trigger-without-details")).andExpect(status().isOk()).andExpect(header().string("HX-Trigger", "event1,event2"));
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    if (handler instanceof HandlerMethod) {
        Method method = ((HandlerMethod) handler).getMethod();
        setHxTrigger(response, method);
        setHxRefresh(response, method);
        setVary(request, response);
    }
    return true;
}

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    if (handler instanceof HandlerMethod) {
        Method method = ((HandlerMethod) handler).getMethod();
        setHxTrigger(response, method);
        setHxRefresh(response, method);
    }
    return true;
}

@Test
public void testHxReplaceUrl() throws Exception {
    mockMvc.perform(get("/hvhi/hx-replace-url")).andExpect(status().isOk()).andExpect(header().string("HX-Replace-Url", "/path"));
}

@Test
public void testHxTriggerAfterSwapWithoutDetails() throws Exception {
    mockMvc.perform(get("/hvhi/hx-trigger-after-swap-without-details")).andExpect(status().isOk()).andExpect(header().string("HX-Trigger-After-Swap", "event1,event2"));
}

@Test
public void testHxTriggerWithDetails() throws Exception {
    mockMvc.perform(get("/hvhi/hx-trigger-with-details")).andExpect(status().isOk()).andExpect(header().string("HX-Trigger", "{\"event1\":null,\"event2\":{\"var\":\"value\"}}"));
}

@Test
public void testHxTriggerAfterSettleWithoutDetails() throws Exception {
    mockMvc.perform(get("/hvhi/hx-trigger-after-settle-without-details")).andExpect(status().isOk()).andExpect(header().string("HX-Trigger-After-Settle", "event1,event2"));
}

@Test
public void testHxReswap() throws Exception {
    mockMvc.perform(get("/hvhi/hx-reswap")).andExpect(status().isOk()).andExpect(header().string("HX-Reswap", "outerHTML transition:true focus-scroll:true swap:100ms settle:300ms scroll:#target:bottom show:#target:top"));
}

@Test
public void testHxLocationWithoutContextData() throws Exception {
    mockMvc.perform(get("/hvhi/hx-location-without-context-data")).andExpect(status().isOk()).andExpect(header().string("HX-Location", "/path"));
}

@Test
public void testHxTriggerAfterSettleWithDetails() throws Exception {
    mockMvc.perform(get("/hvhi/hx-trigger-after-settle-with-details")).andExpect(status().isOk()).andExpect(header().string("HX-Trigger-After-Settle", "{\"event1\":null,\"event2\":{\"var\":\"value\"}}"));
}

@Test
public void testHxLocationWithContextData() throws Exception {
    mockMvc.perform(get("/hvhi/hx-location-with-context-data")).andExpect(status().isOk()).andExpect(header().string("HX-Location", "{\"path\":\"/path\",\"source\":\"source\",\"event\":\"event\",\"handler\":\"handler\",\"target\":\"target\",\"swap\":\"swap\",\"values\":{\"value1\":\"v1\",\"value2\":\"v2\"},\"headers\":{\"header1\":\"v1\",\"header2\":\"v2\"}}"));
}

@Test
public void testHxTriggerAfterSwapWithDetails() throws Exception {
    mockMvc.perform(get("/hvhi/hx-trigger-after-swap-with-details")).andExpect(status().isOk()).andExpect(header().string("HX-Trigger-After-Swap", "{\"event1\":null,\"event2\":{\"var\":\"value\"}}"));
}

@Test
public void testHxReselect() throws Exception {
    mockMvc.perform(get("/hvhi/hx-reselect")).andExpect(status().isOk()).andExpect(header().string("HX-Reselect", "#target"));
}


@Test
void testScrollBottomWithTarget() {
    var reswap = HtmxReswap.innerHtml().scroll(HtmxReswap.Position.BOTTOM).scrollTarget("#target");
    assertThat(reswap).hasToString("innerHTML scroll:#target:bottom");
}

public HtmxReswap scroll(Position position) {
    this.scroll = position;
    return this;
}

@Test
void testSwap() {
    var reswap = HtmxReswap.innerHtml().swap(Duration.ofMillis(200));
    assertThat(reswap).hasToString("innerHTML swap:200ms");
}

public HtmxReswap swap(Duration duration) {
    this.swap = duration;
    return this;
}

@Test
void testShowTopWithTarget() {
    var reswap = HtmxReswap.innerHtml().show(HtmxReswap.Position.TOP).showTarget("#target");
    assertThat(reswap).hasToString("innerHTML show:#target:top");
}

public HtmxReswap showTarget(String cssSelector) {
    this.showTarget = cssSelector;
    return this;
}

@Test
void testScrollTopWithTarget() {
    var reswap = HtmxReswap.innerHtml().scroll(HtmxReswap.Position.TOP).scrollTarget("#target");
    assertThat(reswap).hasToString("innerHTML scroll:#target:top");
}

public HtmxReswap scrollTarget(String cssSelector) {
    this.scrollTarget = cssSelector;
    return this;
}

@Test
void testFocusScrollTrue() {
    var reswap = HtmxReswap.innerHtml().focusScroll(true);
    assertThat(reswap).hasToString("innerHTML focus-scroll:true");
}

public HtmxReswap focusScroll(boolean enabled) {
    this.focusScroll = enabled;
    return this;
}

@Test
void testShowTop() {
    var reswap = HtmxReswap.innerHtml().show(HtmxReswap.Position.TOP);
    assertThat(reswap).hasToString("innerHTML show:top");
}

public HtmxReswap show(Position position) {
    this.show = position;
    return this;
}

@Test
void testShowBottomWithTarget() {
    var reswap = HtmxReswap.innerHtml().show(HtmxReswap.Position.BOTTOM).showTarget("#target");
    assertThat(reswap).hasToString("innerHTML show:#target:bottom");
}

@Test
void testScrollBottom() {
    var reswap = HtmxReswap.innerHtml().scroll(HtmxReswap.Position.BOTTOM);
    assertThat(reswap).hasToString("innerHTML scroll:bottom");
}

@Test
void testFocusScrollFalse() {
    var reswap = HtmxReswap.innerHtml().focusScroll(false);
    assertThat(reswap).hasToString("innerHTML focus-scroll:false");
}

@Test
void testScrollTop() {
    var reswap = HtmxReswap.innerHtml().scroll(HtmxReswap.Position.TOP);
    assertThat(reswap).hasToString("innerHTML scroll:top");
}

@Test
void testSwapOptions() {
    assertThat(HtmxReswap.afterBegin()).hasToString("afterbegin");
    assertThat(HtmxReswap.afterEnd()).hasToString("afterend");
    assertThat(HtmxReswap.beforeBegin()).hasToString("beforebegin");
    assertThat(HtmxReswap.beforeEnd()).hasToString("beforeend");
    assertThat(HtmxReswap.delete()).hasToString("delete");
    assertThat(HtmxReswap.none()).hasToString("none");
    assertThat(HtmxReswap.outerHtml()).hasToString("outerHTML");
}

public static HtmxReswap none() {
    return new HtmxReswap(HxSwapType.NONE);
}

@Test
void testSettle() {
    var reswap = HtmxReswap.innerHtml().settle(Duration.ofMillis(200));
    assertThat(reswap).hasToString("innerHTML settle:200ms");
}

public HtmxReswap settle(Duration duration) {
    this.settle = duration;
    return this;
}

@Test
void testTransition() {
    var reswap = HtmxReswap.innerHtml().transition();
    assertThat(reswap).hasToString("innerHTML transition:true");
}

public HtmxReswap transition() {
    this.transition = true;
    return this;
}

@Test
void testShowBottom() {
    var reswap = HtmxReswap.innerHtml().show(HtmxReswap.Position.BOTTOM);
    assertThat(reswap).hasToString("innerHTML show:bottom");
}


@Test
public void testAddingTriggersAfterSettle() {
    HtmxResponse response = new HtmxResponse().addTrigger("event", null, HxTriggerLifecycle.SETTLE);
    assertThat(response.getTriggersAfterSettle()).containsKey("event");
}

@Test
public void testAddingTriggersAfterSwap() {
    HtmxResponse response = new HtmxResponse().addTrigger("event", null, HxTriggerLifecycle.SWAP);
    assertThat(response.getTriggersAfterSwap()).containsKey("event");
}


@Test
public void deveRetornarCategoriaCorretaParaCodigoValido() {
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_2_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("02"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_3_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("04"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_4_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("06"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_5_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("07"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_6_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("08"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_7_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("10"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_8_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("11"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_9_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("12"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_10_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("13"));
    assertEquals(MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_ACIMA_10_EIXOS, MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("14"));
}

public static MDFTipoCategoriaCombinacaoVeicular valueOfCodigo(final String codigo) {
    for (MDFTipoCategoriaCombinacaoVeicular tipoEmitente : MDFTipoCategoriaCombinacaoVeicular.values()) {
        if (tipoEmitente.getCodigo().equalsIgnoreCase(codigo)) {
            return tipoEmitente;
        }
    }
    return null;
}

@Test
public void deveRetornarCodigoCorreto() {
    assertEquals("02", MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_2_EIXOS.getCodigo());
    assertEquals("13", MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_10_EIXOS.getCodigo());
    assertEquals("14", MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_ACIMA_10_EIXOS.getCodigo());
}

public String getCodigo() {
    return this.codigo;
}

@Test
public void deveRetornarNullParaCodigoInvalido() {
    assertNull(MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("99"));
    assertNull(MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo("INVALIDO"));
    assertNull(MDFTipoCategoriaCombinacaoVeicular.valueOfCodigo(null));
}

@Test
public void deveRetornarDescricaoCorretaNoToString() {
    assertEquals("02 - Veículo Comercial 2 eixos", MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_2_EIXOS.toString());
    assertEquals("13 - Veículo Comercial 10 eixos", MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_10_EIXOS.toString());
    assertEquals("14 - Veículo Comercial Acima de 10 eixos", MDFTipoCategoriaCombinacaoVeicular.VEICULO_COMERCIAL_ACIMA_10_EIXOS.toString());
}

@Override
public String toString() {
    return codigo + " - " + descricao;
}


@Test
public void testValueOfCodigoInvalido() {
    MDFTipoIndicadorAntecipacaoPagamento tipo = MDFTipoIndicadorAntecipacaoPagamento.valueOfCodigo("2");
    assertNull(tipo);
}

public static MDFTipoIndicadorAntecipacaoPagamento valueOfCodigo(final String codigo) {
    for (final MDFTipoIndicadorAntecipacaoPagamento operacao : MDFTipoIndicadorAntecipacaoPagamento.values()) {
        if (operacao.getCodigo().equals(codigo)) {
            return operacao;
        }
    }
    return null;
}

@Test
public void testValueOfCodigoNao() {
    MDFTipoIndicadorAntecipacaoPagamento tipo = MDFTipoIndicadorAntecipacaoPagamento.valueOfCodigo("0");
    assertEquals(MDFTipoIndicadorAntecipacaoPagamento.NAO, tipo);
    Assert.assertNotNull(tipo);
    assertEquals("0", tipo.getCodigo());
    assertEquals("0 - Não", tipo.toString());
}

@Test
public void testValueOfCodigoSim() {
    MDFTipoIndicadorAntecipacaoPagamento tipo = MDFTipoIndicadorAntecipacaoPagamento.valueOfCodigo("1");
    assertEquals(MDFTipoIndicadorAntecipacaoPagamento.SIM, tipo);
    Assert.assertNotNull(tipo);
    assertEquals("1", tipo.getCodigo());
    assertEquals("1 - Sim", tipo.toString());
}


@Test
public void testValueOfCodigo() {
    assertEquals(MDFTipoPermissaoAntecipacao.NAO_PERMITE_ANTECIPAR, MDFTipoPermissaoAntecipacao.valueOfCodigo("0"));
    assertEquals(MDFTipoPermissaoAntecipacao.PERMITE_ANTECIPAR, MDFTipoPermissaoAntecipacao.valueOfCodigo("1"));
    assertEquals(MDFTipoPermissaoAntecipacao.PERMITE_ANTECIPAR_COM_CONFIRMACAO, MDFTipoPermissaoAntecipacao.valueOfCodigo("2"));
    assertNull(MDFTipoPermissaoAntecipacao.valueOfCodigo("3"));
    assertNull(MDFTipoPermissaoAntecipacao.valueOfCodigo(null));
}

public static MDFTipoPermissaoAntecipacao valueOfCodigo(final String codigo) {
    for (MDFTipoPermissaoAntecipacao tipoEmitente : MDFTipoPermissaoAntecipacao.values()) {
        if (tipoEmitente.getCodigo().equalsIgnoreCase(codigo)) {
            return tipoEmitente;
        }
    }
    return null;
}

@Test
public void testGetCodigo() {
    assertEquals("0", MDFTipoPermissaoAntecipacao.NAO_PERMITE_ANTECIPAR.getCodigo());
    assertEquals("1", MDFTipoPermissaoAntecipacao.PERMITE_ANTECIPAR.getCodigo());
    assertEquals("2", MDFTipoPermissaoAntecipacao.PERMITE_ANTECIPAR_COM_CONFIRMACAO.getCodigo());
}

@Test
public void testToString() {
    assertEquals("0 - Não permite antecipar", MDFTipoPermissaoAntecipacao.NAO_PERMITE_ANTECIPAR.toString());
    assertEquals("1 - Permite antecipar as parcelas", MDFTipoPermissaoAntecipacao.PERMITE_ANTECIPAR.toString());
    assertEquals("2 - Permite antecipar as parcelas mediante confirmação", MDFTipoPermissaoAntecipacao.PERMITE_ANTECIPAR_COM_CONFIRMACAO.toString());
}


@Test
public void testGetCodigo() {
    assertEquals("01", MDFTipoValePedagio.TAG.getCodigo());
    assertEquals("02", MDFTipoValePedagio.CUPOM.getCodigo());
    assertEquals("03", MDFTipoValePedagio.CARTAO.getCodigo());
}

@Test
public void testToString() {
    assertEquals("01 - TAG", MDFTipoValePedagio.TAG.toString());
    assertEquals("02 - Cupom", MDFTipoValePedagio.CUPOM.toString());
    assertEquals("03 - Cart\u00E3o", MDFTipoValePedagio.CARTAO.toString());
}

@Test
public void testValueOfCodigo() {
    assertEquals(MDFTipoValePedagio.TAG, MDFTipoValePedagio.valueOfCodigo("01"));
    assertEquals(MDFTipoValePedagio.CUPOM, MDFTipoValePedagio.valueOfCodigo("02"));
    assertEquals(MDFTipoValePedagio.CARTAO, MDFTipoValePedagio.valueOfCodigo("03"));
    assertNull(MDFTipoValePedagio.valueOfCodigo("99"));
    assertNull(MDFTipoValePedagio.valueOfCodigo(null));
}

public static MDFTipoValePedagio valueOfCodigo(final String codigo) {
    for (MDFTipoValePedagio tipoEmitente : MDFTipoValePedagio.values()) {
        if (tipoEmitente.getCodigo().equalsIgnoreCase(codigo)) {
            return tipoEmitente;
        }
    }
    return null;
}


@Test
public void devePermitirInfContratoNuloEManterReferencia() {
    Assert.assertNull(antt.getInfContrato());
    MDFInfoModalRodoviarioInfContrato contrato = new MDFInfoModalRodoviarioInfContrato();
    antt.setInfContrato(contrato);
    Assert.assertSame(contrato, antt.getInfContrato());
    antt.setInfContrato(null);
    Assert.assertNull(antt.getInfContrato());
}

public MDFInfoModalRodoviarioInfContrato getInfContrato() {
    return infContrato;
}


@Test(expected = NumberFormatException.class)
public void deveRejeitarValorContratoGlobalComMaisDe13DigitosTotais() {
    BigDecimal invalido = new BigDecimal("100000000000.00");
    contrato.setValorContratoGlobal(invalido);
}

public void setValorContratoGlobal(BigDecimal valorContratoGlobal) {
    DFBigDecimalValidador.tamanho13Com2CasasDecimais(valorContratoGlobal, "Valor do Contrato Global");
    this.valorContratoGlobal = valorContratoGlobal;
}

@Test
public void deveAceitarValorContratoGlobalValidoIntermediario() {
    BigDecimal valor = new BigDecimal("1234567890.23");
    contrato.setValorContratoGlobal(valor);
    assertEquals(0, contrato.getValorContratoGlobal().compareTo(valor));
}

public BigDecimal getValorContratoGlobal() {
    return valorContratoGlobal;
}

@Test
public void deveAceitarValorContratoGlobalValidoNoLimite() {
    BigDecimal validoLimite = new BigDecimal("9999999999.99");
    contrato.setValorContratoGlobal(validoLimite);
    assertEquals("Valor retornado deve ser igual ao atribuído (compareTo == 0)", 0, contrato.getValorContratoGlobal().compareTo(validoLimite));
}

@Test(expected = NumberFormatException.class)
public void deveRejeitarValorContratoGlobalComMaisDe2CasasDecimais() {
    BigDecimal invalido = new BigDecimal("1.234");
    contrato.setValorContratoGlobal(invalido);
}


@Test
public void deveAceitarVParcelaValidoIntermediario() {
    BigDecimal valor = new BigDecimal("1234567890.23");
    prazo.setVParcela(valor);
    assertEquals(0, prazo.getVParcela().compareTo(valor));
}

public void setVParcela(BigDecimal vParcela) {
    this.vParcela = vParcela;
}

public void setVParcela(BigDecimal vParcela) {
    DFBigDecimalValidador.tamanho13Com2CasasDecimais(vParcela, "Valor da Parcela");
    this.vParcela = vParcela;
}

@Test
public void deveAceitarVParcelaValidoNoLimite() {
    BigDecimal limite = new BigDecimal("9999999999.99");
    prazo.setVParcela(limite);
    assertEquals(0, prazo.getVParcela().compareTo(limite));
}

@Test(expected = NumberFormatException.class)
public void deveRejeitarVParcelaComMaisDe13Digitos() {
    prazo.setVParcela(new BigDecimal("100000000000.00"));
}

@Test(expected = NumberFormatException.class)
public void deveRejeitarVParcelaComMaisDe2CasasDecimais() {
    prazo.setVParcela(new BigDecimal("10.999"));
}


@Test
public void deveSetarEnumsDeFormaSimples() {
    pag.setIndPag(MDFTipoInfPag.A_PRAZO);
    pag.setIndicadorAtencipacaoAdiantamento(MDFTipoIndicadorAntecipacaoPagamento.SIM);
    Assert.assertEquals(MDFTipoInfPag.A_PRAZO, pag.getIndPag());
    Assert.assertEquals(MDFTipoIndicadorAntecipacaoPagamento.SIM, pag.getIndicadorAtencipacaoAdiantamento());
}

public MDFTipoIndicadorAntecipacaoPagamento getIndicadorAtencipacaoAdiantamento() {
    return indicadorAtencipacaoAdiantamento;
}

@Test(expected = NumberFormatException.class)
public void deveRejeitarVAdiantComMaisDe2Casas() {
    pag.setVAdiant(new BigDecimal("10.999"));
}

public void setVAdiant(BigDecimal vAdiant) {
    DFBigDecimalValidador.tamanho13Com2CasasDecimais(vAdiant, "Valor do Adiantamento");
    this.vAdiant = vAdiant;
}

@Test(expected = NumberFormatException.class)
public void deveRejeitarVAdiantComMaisDe13Digitos() {
    pag.setVAdiant(new BigDecimal("10000000000.00"));
}

@Test(expected = NumberFormatException.class)
public void deveAceitarVAdiantValidoNoLimite() {
    pag.setVAdiant(new BigDecimal("99999999999.99"));
}


@Test
public void deveSetarEObterCategoriaCombinacaoVeicular() {
    MDFTipoCategoriaCombinacaoVeicular categoriaCombinacaoVeicular = MDFTipoCategoriaCombinacaoVeicular.values()[0];
    disp.setCategoriaCombinacaoVeicular(categoriaCombinacaoVeicular);
    Assert.assertEquals(categoriaCombinacaoVeicular, disp.getCategoriaCombinacaoVeicular());
    disp.setCategoriaCombinacaoVeicular(null);
    Assert.assertNull(disp.getCategoriaCombinacaoVeicular());
}

public void setCategoriaCombinacaoVeicular(MDFTipoCategoriaCombinacaoVeicular categoriaCombinacaoVeicular) {
    this.categoriaCombinacaoVeicular = categoriaCombinacaoVeicular;
}


@Test
public void deveRetornarEnumCorretoParaCodigoValido() {
    assertEquals(NFNotaInfoAgropecuarioTipoGuia.GTA, NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("1"));
    assertEquals(NFNotaInfoAgropecuarioTipoGuia.TTA, NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("2"));
    assertEquals(NFNotaInfoAgropecuarioTipoGuia.DTA, NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("3"));
    assertEquals(NFNotaInfoAgropecuarioTipoGuia.ATV, NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("4"));
    assertEquals(NFNotaInfoAgropecuarioTipoGuia.PTV, NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("5"));
    assertEquals(NFNotaInfoAgropecuarioTipoGuia.GTV, NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("6"));
    assertEquals(NFNotaInfoAgropecuarioTipoGuia.GUIA_FLORESTAL, NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("7"));
}

public static NFNotaInfoAgropecuarioTipoGuia valueOfCodigo(final String codigo) {
    for (final NFNotaInfoAgropecuarioTipoGuia tpGuia : NFNotaInfoAgropecuarioTipoGuia.values()) {
        if (tpGuia.getCodigo().equals(codigo)) {
            return tpGuia;
        }
    }
    return null;
}

@Test
public void deveRetornarNullParaCodigoInvalidoOuNulo() {
    assertNull(NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("0"));
    assertNull(NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo("8"));
    assertNull(NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo(null));
    assertNull(NFNotaInfoAgropecuarioTipoGuia.valueOfCodigo(""));
}

@Test
public void deveRetornarToStringFormatadoCorretamente() {
    assertEquals("1 - Guia de Trânsito Animal", NFNotaInfoAgropecuarioTipoGuia.GTA.toString());
    assertEquals("7 - Guia Florestal (DOF, SisFlora - PA e MT ou SIAM - MG)", NFNotaInfoAgropecuarioTipoGuia.GUIA_FLORESTAL.toString());
}


@Test
public void naoAceitaCPFInvalido() {
    NFNotaInfoAgropecuarioDefensivo d = new NFNotaInfoAgropecuarioDefensivo();
    try {
        d.setNumeroReceituario("ABCDEFGHIJABCDEFGHIJABCDEFGHIJ");
        d.setCPFRespTec("39053344706A");
        fail("Deveria lançar exceção para CPF inválido");
    } catch (RuntimeException expected) {
    }
}

public void setCPFRespTec(String CPFRespTec) {
    DFStringValidador.cpf(CPFRespTec, "CPF do Responsável Técnico");
    this.cpfRespTec = CPFRespTec;
}

@Test
public void naoAceitaNumeroGuiaComMaisDe9Digitos() {
    NFNotaInfoAgropecuarioGuiaTransito g = new NFNotaInfoAgropecuarioGuiaTransito();
    try {
        g.setNumeroGuia(1234567890);
        fail("Deveria lançar exceção para número > 9");
    } catch (RuntimeException expected) {
    }
}

public void setNumeroGuia(Integer numeroGuia) {
    DFIntegerValidador.tamanho9(numeroGuia, "Número da Guia");
    this.numeroGuia = numeroGuia;
}

@Test
public void naoAceitaReceituarioComMaisDe30Caracteres() {
    NFNotaInfoAgropecuarioDefensivo d = new NFNotaInfoAgropecuarioDefensivo();
    try {
        d.setNumeroReceituario("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
        fail("Deveria lançar exceção para receituário > 30");
    } catch (RuntimeException expected) {
    }
}

public void setNumeroReceituario(String numeroReceituario) {
    DFStringValidador.tamanho30(numeroReceituario, "Número da receita ou receituário de aplicação do defensivo");
    this.numeroReceituario = numeroReceituario;
}

@Test
public void naoAceitaSerieGuiaComMaisDe9Caracteres() {
    NFNotaInfoAgropecuarioGuiaTransito g = new NFNotaInfoAgropecuarioGuiaTransito();
    try {
        g.setSerieGuia("ABCDEFGHIJ");
        fail("Deveria lançar exceção para série > 9");
    } catch (RuntimeException expected) {
    }
}

public void setSerieGuia(String serieGuia) {
    DFStringValidador.tamanho9(serieGuia, "Série da Guia");
    this.serieGuia = serieGuia;
}


@Test
public void deveTransformarLocalDateEmString() throws Exception {
    Assert.assertEquals("2015-10", new DFYearMonthTransformer().write(YearMonth.of(2015, 10)));
}

@Override
public String write(final YearMonth data) {
    return DFYearMonthTransformer.DATETIME_FORMATTER_YYYYMM.format(data);
}

@Test
public void deveTransformarDataStringEmLocalDate() throws Exception {
    Assert.assertEquals("2006-02", new DFYearMonthTransformer().read("2006-02-01-02:00").toString());
    Assert.assertEquals("2006-02", new DFYearMonthTransformer().read("2006-02-01").toString());
    Assert.assertEquals("2006-02", new DFYearMonthTransformer().read("01/02/2006").toString());
    Assert.assertEquals("2006-02", new DFYearMonthTransformer().read("01/02/2006 12:00:01").toString());
    Assert.assertEquals("2006-02", new DFYearMonthTransformer().read("2006-02-01T00:00:00-03:00").toString());
}

@Override
public YearMonth read(final String data) {
    try {
        return YearMonth.parse(data, DFYearMonthTransformer.DATETIME_FORMATTER_YYYYMMDD);
    } catch (final Exception e) {
        try {
            return YearMonth.from(DFYearMonthTransformer.DATATIME_FORMATTER_YYYYMMDDXXX.parse(data));
        } catch (final Exception e2) {
            try {
                return YearMonth.from(DFYearMonthTransformer.DATETIME_FORMATTER_DDMMYYYY.parse(data));
            } catch (final Exception e3) {
                try {
                    return YearMonth.from(DFYearMonthTransformer.DATETIME_FORMATTER_DDMMYYYHHMMSS.parse(data));
                } catch (final Exception e4) {
                    try {
                        return YearMonth.from(DFYearMonthTransformer.DATATIME_FORMATTER_YYYYMMDDHHMMSSXXXX.parse(data));
                    } catch (final Exception e5) {
                        return YearMonth.parse(data, DFYearMonthTransformer.DATETIME_FORMATTER_YYYYMM);
                    }
                }
            }
        }
    }
}


@Test
public void deveIdentificarEmitentePessoaFisica() {
    MDFChaveParser parser = new MDFChaveParser("35191012346589017069580010000000012345678901");
    assertTrue(parser.isEmitentePessoaFisica());
    assertFalse(parser.isEmitentePessoaJuridica());
}

public boolean isEmitentePessoaFisica() {
    return DFUtils.isCpfValido(this.chave.substring(9, 20));
}

@Test
public void deveRetornarCpfEmitenteQuandoValido() {
    MDFChaveParser parser = new MDFChaveParser("35191012346589017069580010000000012345678901");
    assertEquals("46589017069", parser.getCpfEmitente());
}

public String getCpfEmitente() {
    return isEmitentePessoaFisica() ? this.chave.substring(9, 20) : null;
}

@Test
public void deveIdentificarEmitentePessoaJuridica() {
    MDFChaveParser parser = new MDFChaveParser("35191012345678000195580010000000012345678901");
    assertTrue(parser.isEmitentePessoaJuridica());
    assertFalse(parser.isEmitentePessoaFisica());
}

public boolean isEmitentePessoaJuridica() {
    return DFUtils.isCnpjValido(this.chave.substring(6, 20));
}


@Test
public void deveIdentificarSerieReservadaParaPessoaFisica() {
    assertTrue(parserPessoaFisica.isSerieReservadaPessoaFisica());
    assertFalse(parserPessoaJuridica.isSerieReservadaPessoaFisica());
}

public boolean isSerieReservadaPessoaFisica() {
    if (DFUtils.isNumerico(this.getSerie())) {
        final int serie = Integer.parseInt(this.getSerie());
        return serie >= 920 && serie <= 969;
    }
    return false;
}


@Test
public void deveIdentificarSerieReservadaParaPessoaFisica() {
    NotaFiscalChaveParser notaFiscalChaveParser = new NotaFiscalChaveParser("42151100038883975022649694579133596141399591");
    assertTrue(notaFiscalChaveParser.isSerieReservadaPessoaFisica());
}


@Test
public void pragmaGetGeneratedKeys() throws SQLException {
    SQLiteConnection connection = (SQLiteConnection) DriverManager.getConnection("jdbc:sqlite::memory:?jdbc.get_generated_keys=false");
    assertThat(connection.getConnectionConfig().isGetGeneratedKeys()).isFalse();
}

public boolean isGetGeneratedKeys() {
    return getGeneratedKeys;
}


@Test
void worksNormallyWhenNoExceptionIsThrown() throws Exception {
    final Text source = new TextOf("hello, cactoos!");
    new Assertion<>("Must work normally when no exception is thrown", new IoCheckedBytes(new BytesOf(source)).asBytes(), new IsEqual<>(new BytesOf(source).asBytes())).affirm();
}

@Override
public byte[] asBytes() throws IOException {
    return this.scalar.value();
}

@Test
void worksWithFallback() throws IOException {
    final byte[] empty = {};
    new Assertion<>("Must work with fallback", new IoCheckedBytes(() -> {
        throw new IllegalArgumentException("OK");
    }, new Fallback.From<>(IllegalArgumentException.class, ex -> empty)).asBytes(), new IsEqual<>(empty)).affirm();
}


@Test
void previousIndexTest() {
    final ListIterator<Integer> joined = new JoinedListIterator<>(new ListOf<>(1).listIterator(), new ListOf<>(2).listIterator(), new ListOf<>(3).listIterator());
    joined.next();
    new Assertion<>("Must return index of the previous element", joined.previousIndex(), new IsEqual<>(0)).affirm();
    joined.next();
    new Assertion<>("Must return index of the previous element", joined.previousIndex(), new IsEqual<>(1)).affirm();
}

@Override
public int previousIndex() {
    int previousidx = -1;
    if (this.hasPrevious()) {
        previousidx = this.cursor.get() - 1;
    }
    return previousidx;
}

@Override
public int previousIndex() {
    int previousidx = -1;
    if (this.hasPrevious()) {
        previousidx = this.cursor.get();
    }
    return previousidx;
}


@Test
void returnsTrueOnTrue() throws Exception {
    new Assertion<>("Must return true on true statement", new ThrowsOnFalse(() -> true, "test").value(), new IsEqual<>(true)).affirm();
}

@Override
public Boolean value() throws Exception {
    if (!this.scalar.value()) {
        throw this.exception.value();
    }
    return true;
}


@Test
public void testRemoveAll() {
    Dictionary dictionary = new DoubleArrayDictionaryTrie();
    try {
        dictionary.removeAll(Arrays.asList("APDPlat", "杨尚川"));
        fail();
    } catch (Exception e) {
        assertEquals("not yet support menthod!", e.getMessage());
    }
}

@Override
public void removeAll(List<String> items) {
    throw new RuntimeException("not yet support menthod!");
}


@Test
public void testTrigram() {
    try {
        Map<String, Integer> map = new HashMap<>();
        List<String> lines = Files.readAllLines(Paths.get("src/test/resources/trigram.txt"));
        lines.forEach(line -> {
            String[] attrs = line.split("\\s+");
            if (attrs != null && attrs.length == 2) {
                map.put(attrs[0], Integer.parseInt(attrs[1]));
            }
        });
        DoubleArrayGenericTrie doubleArrayGenericTrie = new DoubleArrayGenericTrie(WordConfTools.getInt("trigram.double.array.trie.size", 9800000));
        doubleArrayGenericTrie.putAll(map);
        map.keySet().forEach(key -> assertEquals(map.get(key).intValue(), doubleArrayGenericTrie.get(key)));
    } catch (Exception e) {
        e.printStackTrace();
        fail();
    }
}

public void putAll(Map<String, Integer> map) {
    if (check != null) {
        throw new RuntimeException("addAll method can just be used once after clear method!");
    }
    List<String> items = map.keySet().stream().sorted().collect(Collectors.toList());
    if (LOGGER.isDebugEnabled()) {
        if (items.size() < 10) {
            items.forEach(item -> LOGGER.debug(item + "=" + map.get(item)));
        }
    }
    init(items, map);
}

@Test
public void testBigram() {
    try {
        Map<String, Integer> map = new HashMap<>();
        List<String> lines = Files.readAllLines(Paths.get("src/test/resources/bigram.txt"));
        lines.forEach(line -> {
            String[] attrs = line.split("\\s+");
            if (attrs != null && attrs.length == 2) {
                map.put(attrs[0], Integer.parseInt(attrs[1]));
            }
        });
        DoubleArrayGenericTrie doubleArrayGenericTrie = new DoubleArrayGenericTrie(WordConfTools.getInt("bigram.double.array.trie.size", 5300000));
        doubleArrayGenericTrie.putAll(map);
        map.keySet().forEach(key -> assertEquals(map.get(key).intValue(), doubleArrayGenericTrie.get(key)));
    } catch (Exception e) {
        e.printStackTrace();
        fail();
    }
}


@Test
public void testTrigram() {
    try {
        Map<String, Integer> map = new HashMap<>();
        List<String> lines = Files.readAllLines(Paths.get("src/test/resources/trigram.txt"));
        lines.forEach(line -> {
            String[] attrs = line.split("\\s+");
            if (attrs != null && attrs.length == 2) {
                map.put(attrs[0], Integer.parseInt(attrs[1]));
            }
        });
        DoubleArrayGenericTrie doubleArrayGenericTrie = new DoubleArrayGenericTrie(WordConfTools.getInt("trigram.double.array.trie.size", 10100000));
        doubleArrayGenericTrie.putAll(map);
        map.keySet().forEach(key -> assertEquals(map.get(key).intValue(), doubleArrayGenericTrie.get(key)));
    } catch (Exception e) {
        e.printStackTrace();
        fail();
    }
}

@Test
public void testBigram() {
    try {
        Map<String, Integer> map = new HashMap<>();
        List<String> lines = Files.readAllLines(Paths.get("src/test/resources/bigram.txt"));
        lines.forEach(line -> {
            String[] attrs = line.split("\\s+");
            if (attrs != null && attrs.length == 2) {
                map.put(attrs[0], Integer.parseInt(attrs[1]));
            }
        });
        DoubleArrayGenericTrie doubleArrayGenericTrie = new DoubleArrayGenericTrie(WordConfTools.getInt("bigram.double.array.trie.size", 5500000));
        doubleArrayGenericTrie.putAll(map);
        map.keySet().forEach(key -> assertEquals(map.get(key).intValue(), doubleArrayGenericTrie.get(key)));
    } catch (Exception e) {
        e.printStackTrace();
        fail();
    }
}


@Test
public void testCrop() {
    assertTrue(SOURCE.isCropSupported());
    LuminanceSource cropped = SOURCE.crop(1, 1, 1, 1);
    assertEquals(1, cropped.getHeight());
    assertEquals(1, cropped.getWidth());
    assertArrayEquals(new byte[] { 0x7F }, cropped.getRow(0, null));
}

@Override
public LuminanceSource crop(int left, int top, int width, int height) {
    return new GrayscaleLuminanceSource(luminances, dataWidth, dataHeight, this.left + left, this.top + top, width, height);
}

@Test
public void testRotateCropped() {
    assertTrue(SOURCE.isCropSupported());
    assertTrue(SOURCE.isRotateSupported());
    LuminanceSource cropped = SOURCE.crop(1, 1, 2, 2);
    assertArrayEquals(new byte[] { 0x7F, 0x3F }, cropped.getRow(0, null));
    assertArrayEquals(new byte[] { 0x7F, 0x3F }, cropped.getRow(1, null));
    LuminanceSource rot90 = cropped.rotateCounterClockwise();
    assertArrayEquals(new byte[] { 0x3F, 0x3F }, rot90.getRow(0, null));
    assertArrayEquals(new byte[] { 0x7F, 0x7F }, rot90.getRow(1, null));
    LuminanceSource rot180 = rot90.rotateCounterClockwise();
    assertArrayEquals(new byte[] { 0x3F, 0x7F }, rot180.getRow(0, null));
    assertArrayEquals(new byte[] { 0x3F, 0x7F }, rot180.getRow(1, null));
    LuminanceSource rot270 = rot180.rotateCounterClockwise();
    assertArrayEquals(new byte[] { 0x7F, 0x7F }, rot270.getRow(0, null));
    assertArrayEquals(new byte[] { 0x3F, 0x3F }, rot270.getRow(1, null));
    LuminanceSource rot360 = rot270.rotateCounterClockwise();
    assertArrayEquals(new byte[] { 0x7F, 0x3F }, rot360.getRow(0, null));
    assertArrayEquals(new byte[] { 0x7F, 0x3F }, rot360.getRow(1, null));
    assertArrayEquals(cropped.getMatrix(), rot360.getMatrix());
}

@Test
public void testMatrix() {
    assertArrayEquals(new byte[] { 0x00, 0x7F, (byte) 0xFF, 0x3F, 0x7F, 0x3F, 0x3F, 0x7F, 0x3F }, SOURCE.getMatrix());
    LuminanceSource croppedFullWidth = SOURCE.crop(0, 1, 3, 2);
    assertArrayEquals(new byte[] { 0x3F, 0x7F, 0x3F, 0x3F, 0x7F, 0x3F }, croppedFullWidth.getMatrix());
    LuminanceSource croppedCorner = SOURCE.crop(1, 1, 2, 2);
    assertArrayEquals(new byte[] { 0x7F, 0x3F, 0x7F, 0x3F }, croppedCorner.getMatrix());
}

@Override
public byte[] getMatrix() {
    int width = getWidth();
    int height = getHeight();
    if (width == dataWidth && height == dataHeight) {
        return luminances;
    }
    int area = width * height;
    byte[] matrix = new byte[area];
    int inputOffset = top * dataWidth + left;
    if (width == dataWidth) {
        System.arraycopy(luminances, inputOffset, matrix, 0, area);
        return matrix;
    }
    for (int y = 0; y < height; y++) {
        int outputOffset = y * width;
        System.arraycopy(luminances, inputOffset, matrix, outputOffset, width);
        inputOffset += dataWidth;
    }
    return matrix;
}

@Test
public void testRotate() {
    assertTrue(SOURCE.isRotateSupported());
    assertArrayEquals(new byte[] { 0x00, 0x7F, (byte) 0xFF }, SOURCE.getRow(0, null));
    assertArrayEquals(new byte[] { 0x3F, 0x7F, 0x3F }, SOURCE.getRow(1, null));
    assertArrayEquals(new byte[] { 0x3F, 0x7F, 0x3F }, SOURCE.getRow(2, null));
    LuminanceSource rot90 = SOURCE.rotateCounterClockwise();
    assertArrayEquals(new byte[] { (byte) 0xFF, 0x3F, 0x3F }, rot90.getRow(0, null));
    assertArrayEquals(new byte[] { 0x7F, 0x7F, 0x7F }, rot90.getRow(1, null));
    assertArrayEquals(new byte[] { 0x00, 0x3F, 0x3F }, rot90.getRow(2, null));
    LuminanceSource rot180 = rot90.rotateCounterClockwise();
    assertArrayEquals(new byte[] { 0x3F, 0x7F, 0x3F }, rot180.getRow(0, null));
    assertArrayEquals(new byte[] { 0x3F, 0x7F, 0x3F }, rot180.getRow(1, null));
    assertArrayEquals(new byte[] { (byte) 0xFF, 0x7F, 0x00 }, rot180.getRow(2, null));
    LuminanceSource rot270 = rot180.rotateCounterClockwise();
    assertArrayEquals(new byte[] { 0x3F, 0x3F, 0x00 }, rot270.getRow(0, null));
    assertArrayEquals(new byte[] { 0x7F, 0x7F, 0x7F }, rot270.getRow(1, null));
    assertArrayEquals(new byte[] { 0x3F, 0x3F, (byte) 0xFF }, rot270.getRow(2, null));
    LuminanceSource rot360 = rot270.rotateCounterClockwise();
    assertArrayEquals(new byte[] { 0x00, 0x7F, (byte) 0xFF }, rot360.getRow(0, null));
    assertArrayEquals(new byte[] { 0x3F, 0x7F, 0x3F }, rot360.getRow(1, null));
    assertArrayEquals(new byte[] { 0x3F, 0x7F, 0x3F }, rot360.getRow(2, null));
    assertArrayEquals(SOURCE.getMatrix(), rot360.getMatrix());
}

@Override
public boolean isRotateSupported() {
    return true;
}

@Test
public void testGetRow() {
    assertArrayEquals(new byte[] { 0x3F, 0x7F, 0x3F }, SOURCE.getRow(2, new byte[3]));
}

@Override
public byte[] getRow(int y, byte[] row) {
    if (y < 0 || y >= getHeight()) {
        throw new IllegalArgumentException("Requested row is outside the image: " + y);
    }
    int width = getWidth();
    if (row == null || row.length < width) {
        row = new byte[width];
    }
    int offset = (y + top) * dataWidth + left;
    System.arraycopy(luminances, offset, row, 0, width);
    return row;
}