/* libxmq - Copyright (C) 2023-2025 Fredrik Öhrström (spdx: MIT)

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

#include"xmq.h"

// If we are concatening a single source file dist/xmq.dc then add the define BUILDING_DIST_XMQ
#define BUILDING_DIST_XMQ

// PART HEADERS //////////////////////////////////////////////////

// PART H ALWAYS ////////////////////////////////////////

#ifdef BUILDING_DIST_XMQ
#define STATIC static
#else
#define STATIC
#endif

#include<stdbool.h>
#include<stdlib.h>
#include<stdarg.h>
#include<setjmp.h>

struct XMQLineConfig
{
    bool human_readable_;
};
typedef struct XMQLineConfig XMQLineConfig;

struct MemBuffer;
typedef struct MemBuffer MemBuffer;

extern bool xmq_trace_enabled_;
extern bool xmq_debug_enabled_;
extern bool xmq_verbose_enabled_;
extern const char *xmq_log_filter_;
extern struct XMQLineConfig xmq_log_line_config_;

STATIC void error__(const char* fmt, ...);
STATIC void warning__(const char* fmt, ...);
STATIC void verbose__(const char* fmt, ...);
STATIC void debug__(const char* fmt, ...);
STATIC void trace__(const char* fmt, ...);
STATIC void debug_mb__(const char* module, MemBuffer *mb);
STATIC void trace_mb__(const char* fmt, MemBuffer *mb);
STATIC void check_malloc(void *a);

STATIC char *buf_vsnprintf(const char *format, va_list ap);

#define error(...) error__(__VA_ARGS__)
#define warning(...) warning__(__VA_ARGS__)
#define verbose(...) if (xmq_verbose_enabled_) { verbose__(__VA_ARGS__); }
#define debug(...) if (xmq_debug_enabled_) {debug__(__VA_ARGS__); }
#define debug_mb(module, mb) if (xmq_debug_enabled_) {debug_mb__(module, mb); }
#define trace(...) if (xmq_trace_enabled_) {trace__(__VA_ARGS__); }
#define trace_mb(module, mb) if (xmq_trace_enabled_) {trace_mb__(module, mb); }

#define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)
#define PRINT_WARNING(...) fprintf(stderr, __VA_ARGS__)

#ifdef PLATFORM_WINAPI
char *strndup(const char *s, size_t l);
#endif

// A common free function ptr to be used when freeing collections.
typedef void(*FreeFuncPtr)(void*);

STATIC char *humanReadableTwoDecimals(size_t s);

#define ALWAYS_MODULE

// PART H COLORS ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ
#include "xmq.h"
#endif

struct XMQOutputSettings;
typedef struct XMQOutputSettings XMQOutputSettings;

/**
    XMQColor:

    Map token type into color index.
*/
typedef enum XMQColor {
    COLOR_none,
    COLOR_whitespace,
    COLOR_unicode_whitespace,
    COLOR_indentation_whitespace,
    COLOR_equals,
    COLOR_brace_left,
    COLOR_brace_right,
    COLOR_apar_left,
    COLOR_apar_right,
    COLOR_cpar_left,
    COLOR_cpar_right,
    COLOR_quote,
    COLOR_entity,
    COLOR_comment,
    COLOR_comment_continuation,
    COLOR_ns_colon,
    COLOR_element_ns,
    COLOR_element_name,
    COLOR_element_key,
    COLOR_element_value_text,
    COLOR_element_value_quote,
    COLOR_element_value_entity,
    COLOR_element_value_compound_quote,
    COLOR_element_value_compound_entity,
    COLOR_attr_ns,
    COLOR_attr_key,
    COLOR_attr_value_text,
    COLOR_attr_value_quote,
    COLOR_attr_value_entity,
    COLOR_attr_value_compound_quote,
    COLOR_attr_value_compound_entity,
    COLOR_ns_declaration,
    COLOR_ns_override_xsl,
} XMQColor;

/**
   XMQColorName:

   The actual number of colors are fewer than the number of tokens
   since we reuse colors for several tokens, no need to have different
   colors for left and right compound parentheses.
*/
typedef enum XMQColorName {
    XMQ_COLOR_C, // Comment
    XMQ_COLOR_Q, // Quote
    XMQ_COLOR_E, // Entity
    XMQ_COLOR_NS, // Name Space (both for element and attribute)
    XMQ_COLOR_EN, // Element Name
    XMQ_COLOR_EK, // Element Key
    XMQ_COLOR_EKV, // Element Key Value
    XMQ_COLOR_AK, // Attribute Key
    XMQ_COLOR_AKV, // Attribute Key Value
    XMQ_COLOR_CP, // Compound Parentheses
    XMQ_COLOR_NSD, // Name Space Declaration xmlns
    XMQ_COLOR_UW, // Unicode whitespace
    XMQ_COLOR_XLS, // Override XLS element names with this color.
} XMQColorName;

#define XMQ_COLOR_NAMES \
    X(C) \
    X(Q) \
    X(E) \
    X(NS) \
    X(EN) \
    X(EK) \
    X(EKV) \
    X(AK) \
    X(AKV) \
    X(CP) \
    X(NSD) \
    X(UW) \
    X(XLS) \

#define NUM_XMQ_COLOR_NAMES 13

const char* colorName(int i);

typedef struct XMQColorDef {
    int r, g, b;
    bool bold;
    bool underline;
} XMQColorDef;

/**
    XMQThemeStrings:
    @pre: string to inserted before the token
    @post: string to inserted after the token

    A color string object is stored for each type of token.
    It can store the ANSI color prefix, the html span etc.
    If post is NULL then when the token ends, the pre of the containing color will be reprinted.
    This is used for ansi codes where there is no stack memory (pop impossible) to the previous colors.
    I.e. pre = "\033[0;1;32m" which means reset;bold;green but post = NULL.
    For html/tex coloring we use the stack memory (pop possible) of tags.
    I.e. pre = "<span class="red">" post = "</span>"
    I.e. pre = "{\color{red}" post = "}"
*/
struct XMQThemeStrings
{
    const char *pre;
    const char *post;
};
typedef struct XMQThemeStrings XMQThemeStrings;

/**
    XMQTheme

    The theme struct is used to prefix/postfix ANSI/HTML/TEX strings for
    XMQ tokens to colorize the printed xmq output.
*/
struct XMQTheme
{
    const char *name;
    const char *indentation_space;
    const char *explicit_space;
    const char *explicit_nl;
    const char *explicit_tab;
    const char *explicit_cr;

    XMQThemeStrings document; // <html></html>  \documentclass{...}... etc
    XMQThemeStrings header; // <head>..</head>
    XMQThemeStrings style;  // Stylesheet content inside header (html) or color(tex) definitions.
    XMQThemeStrings body; // <body></body> \begin{document}\end{document}
    XMQThemeStrings content; // Wrapper around rendered code. Like <pre></pre>, \textt{...}

    XMQThemeStrings whitespace; // The normal whitespaces: space=32. Normally not colored.
    XMQThemeStrings unicode_whitespace; // The remaining unicode whitespaces, like: nbsp=160 color as red underline.
    XMQThemeStrings indentation_whitespace; // The xmq generated indentation spaces. Normally not colored.
    XMQThemeStrings equals; // The key = value equal sign.
    XMQThemeStrings brace_left; // Left brace starting a list of childs.
    XMQThemeStrings brace_right; // Right brace ending a list of childs.
    XMQThemeStrings apar_left; // Left parentheses surrounding attributes. foo(x=1)
    XMQThemeStrings apar_right; // Right parentheses surrounding attributes.
    XMQThemeStrings cpar_left; // Left parentheses surrounding a compound value. foo = (&#10;' x '&#10;)
    XMQThemeStrings cpar_right; // Right parentheses surrounding a compound value.
    XMQThemeStrings quote; // A quote 'x y z' can be single or multiline.
    XMQThemeStrings entity; // A entity &#10;
    XMQThemeStrings comment; // A comment // foo or /* foo */
    XMQThemeStrings comment_continuation; // A comment containing newlines /* Hello */* there */
    XMQThemeStrings ns_colon; // The color of the colon separating a namespace from a name.
    XMQThemeStrings element_ns; // The namespace part of an element tag, i.e. the text before colon in foo:alfa.
    XMQThemeStrings element_name; // When an element tag has multiple children or attributes it is rendered using this color.
    XMQThemeStrings element_key; // When an element tag is suitable to be presented as a key value, this color is used.
    XMQThemeStrings element_value_text; // When an element is presented as a key and the value is presented as text, use this color.
    XMQThemeStrings element_value_quote; // When the value is a single quote, use this color.
    XMQThemeStrings element_value_entity; // When the value is a single entity, use this color.
    XMQThemeStrings element_value_compound_quote; // When the value is compounded and this is a quote in the compound.
    XMQThemeStrings element_value_compound_entity; // When the value is compounded and this is an entity in the compound.
    XMQThemeStrings attr_ns; // The namespace part of an attribute name, i.e. the text before colon in bar:speed.
    XMQThemeStrings attr_key; // The color of the attribute name, i.e. the key.
    XMQThemeStrings attr_value_text; // When the attribute value is text, use this color.
    XMQThemeStrings attr_value_quote; // When the attribute value is a quote, use this color.
    XMQThemeStrings attr_value_entity; // When the attribute value is an entity, use this color.
    XMQThemeStrings attr_value_compound_quote; // When the attribute value is a compound and this is a quote in the compound.
    XMQThemeStrings attr_value_compound_entity; // When the attribute value is a compound and this is an entity in the compound.
    XMQThemeStrings ns_declaration; // The xmlns part of an attribute namespace declaration.
    XMQThemeStrings ns_override_xsl; // Override key/name colors for elements with xsl namespace.

    // RGB Sources + bold + underline from which we can configure the strings.
    XMQColorDef colors_darkbg[NUM_XMQ_COLOR_NAMES];
    XMQColorDef colors_lightbg[NUM_XMQ_COLOR_NAMES];
};
typedef struct XMQTheme XMQTheme;

void getThemeStrings(XMQOutputSettings *os, XMQColor c, const char **pre, const char **post);

bool string_to_color_def(const char *s, XMQColorDef *def);

// Expect buffer to store 128 bytes.
bool generate_ansi_color(char *buf, size_t buf_size, XMQColorDef *def);
bool generate_html_color(char *buf, size_t buf_size, XMQColorDef *def, const char *name);
bool generate_tex_color(char *buf, size_t buf_size, XMQColorDef *def, const char *name);


#define COLORS_MODULE

// PART H CORE ////////////////////////////////////////

#include<stdbool.h>
#include<stdint.h>

#define CORE_MODULE

bool coreParseI8(const char *s, int8_t *out);
bool coreParseI16(const char *s, int16_t *out);
bool coreParseI32(const char *s, int32_t *out);
bool coreParseI64(const char *s, int64_t *out);
//bool coreParseI128(const char *s, __int128 *out);

// PART H DEFAULT_THEMES ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ
#include "xmq.h"
#endif

struct XMQTheme;
typedef struct XMQTheme XMQTheme;

void installDefaultThemeColors(XMQTheme *theme);
const char *ansiWin(int i);

#define DEFAULT_THEMES_MODULE

// PART H ENTITIES ////////////////////////////////////////

/**
    toHtmlEntity:
    @uc: Unicode code point.
*/
const char *toHtmlEntity(int uc);

#define ENTITIES_MODULE

// PART H UTF8 ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ
#include"xmq.h"
#include"colors.h"
#include"xmq_internals.h"
#endif

struct XMQPrintState;
typedef struct XMQPrintState XMQPrintState;

enum XMQColor;
typedef enum XMQColor XMQColor;

size_t print_utf8_char(XMQPrintState *ps, const char *start, const char *stop);
size_t print_utf8_internal(XMQPrintState *ps, const char *start, const char *stop);
size_t print_utf8(XMQPrintState *ps, XMQColor c, size_t num_pairs, ...);

#define UTF8_MODULE

// PART H HASHMAP ////////////////////////////////////////

struct HashMap;
typedef struct HashMap HashMap;

struct HashMapIterator;
typedef struct HashMapIterator HashMapIterator;

HashMap *hashmap_create(size_t max_size);
// Returns NULL if no key is found.
void *hashmap_get(HashMap* map, const char* key);
// Putting a non-NULL value.
void hashmap_put(HashMap* map, const char* key, void *val);
// How many key-vals are there?
size_t hashmap_size(HashMap *map);
// Free the hashmap itself.
void hashmap_free(HashMap* map);
// Free the hasmap and its contents.
void hashmap_free_and_values(HashMap *map, FreeFuncPtr freefunc);

HashMapIterator *hashmap_iterate(HashMap *map);
bool hashmap_next_key_value(HashMapIterator *i, const char **key, void **val);
void hashmap_free_iterator(HashMapIterator *i);

#define HASHMAP_MODULE

// PART H IXML ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include"xmq.h"

#endif

struct YaepGrammar;
typedef struct YaepGrammar YaepGrammar;
struct YaepParseRun;
typedef struct YaepParseRun YaepParseRun;
struct yaep_tree_node;

struct IXMLRule;
typedef struct IXMLRule IXMLRule;
struct IXMLTerminal;
typedef struct IXMLTerminal IXMLTerminal;
struct IXMLNonTerminal;
typedef struct IXMLNonTerminal IXMLNonTerminal;

bool ixml_build_yaep_grammar(YaepParseRun *pr,
                             YaepGrammar *g,
                             XMQParseState *state,
                             const char *grammar_start,
                             const char *grammar_stop,
                             const char *content_start, // Needed to minimize charset rule sizes.
                             const char *content_stop);

IXMLTerminal *new_ixml_terminal();
IXMLNonTerminal *new_ixml_nonterminal();
void free_ixml_rule(IXMLRule *r);
void free_ixml_terminal(IXMLTerminal *t);
void free_ixml_nonterminal(IXMLNonTerminal *nt);
const char *ixml_to_yaep_read_terminal(YaepParseRun *pr,
                                       YaepGrammar *g,
                                       int *code);
const char *ixml_to_yaep_read_rule(YaepParseRun *pr,
                                   YaepGrammar *g,
                                   const char ***rhs,
                                   const char **abs_node,
                                   int *cost,
                                   int **transl,
                                   char *mark,
                                   char **marks);

void scan_content_fixup_charsets(XMQParseState *state, const char *start, const char *stop);

void ixml_print_grammar(XMQParseState *state);

#define IXML_MODULE

// PART H MEMBUFFER ////////////////////////////////////////

#include<stdlib.h>
#include<stdarg.h>

/**
    MemBuffer:
    @max_: Current size of malloced buffer.
    @used_: Number of bytes actually used of buffer.
    @buffer_: Start of buffer data.
*/
struct MemBuffer;
typedef struct MemBuffer
{
    size_t max_; // Current size of malloc for buffer.
    size_t used_; // How much is actually used.
    char *buffer_; // The malloced data.
} MemBuffer;

// Output buffer functions ////////////////////////////////////////////////////////

MemBuffer *new_membuffer();
char *free_membuffer_but_return_trimmed_content(MemBuffer *mb);
void free_membuffer_and_free_content(MemBuffer *mb);
size_t pick_buffer_new_size(size_t max, size_t used, size_t add);
size_t membuffer_used(MemBuffer *mb);
void membuffer_reuse(MemBuffer *mb, char *start, size_t len);
void membuffer_append_region(MemBuffer *mb, const char *start, const char *stop);
void membuffer_append(MemBuffer *mb, const char *start);
void membuffer_append_char(MemBuffer *mb, char c);
void membuffer_append_int(MemBuffer *mb, int i);
void membuffer_append_entity(MemBuffer *mb, char c);
void membuffer_append_null(MemBuffer *mb);
void membuffer_append_nl(MemBuffer *mb);
void membuffer_drop_last_null(MemBuffer *mb);
void membuffer_append_pointer(MemBuffer *mb, void *ptr);
void membuffer_logf(MemBuffer *mb, const char* fmt, ...);
void membuffer_printf(MemBuffer *mb, const char* fmt, ...);
char membuffer_back(MemBuffer *mb);
void membuffer_prefix_lines(MemBuffer *mb, const char *prefix);

#define MEMBUFFER_MODULE

// PART H STACK ////////////////////////////////////////

#include<stdlib.h>

typedef struct StackElement StackElement;
struct StackElement
{
    void *data;
    StackElement *below; // When this element is popped, below becomes top element.
};

struct Stack
{
    StackElement *bottom;
    StackElement *top;
    size_t size;
};
typedef struct Stack Stack;

Stack *stack_create();
void stack_free(Stack *stack);
void stack_push(Stack *s, void *);
// Pop the top element.
void *stack_pop(Stack *s);
// Pull the bottom element.
void *stack_rock(Stack *s);

#define STACK_MODULE

// PART H TEXT ////////////////////////////////////////

#include<stdbool.h>
#include<stdlib.h>

/**
    UTF8Char: storage for 1 to 4 utf8 bytes

    An utf8 char is at most 4 bytes since the max unicode nr is capped at U+10FFFF:

    Add an extra byte that we can set to zero if we need.
*/
#define MAX_NUM_UTF8_BYTES (4+1)
typedef struct
{
    char bytes[MAX_NUM_UTF8_BYTES];
} UTF8Char;

size_t count_whitespace(const char *i, const char *stop);
bool decode_utf8(const char *start, const char *stop, int *out_char, size_t *out_len);
size_t encode_utf8(int uc, UTF8Char *utf8);
const char *has_ending_nl_space(const char *start, const char *stop, size_t *only_newlines);
const char *has_leading_space_nl(const char *start, const char *stop, size_t *only_newlines);
bool has_leading_ending_different_quotes(const char *start, const char *stop);
bool has_newlines(const char *start, const char *stop);
bool has_must_escape_chars(const char *start, const char *stop);
bool has_all_quotes(const char *start, const char *stop);
bool has_all_whitespace(const char *start, const char *stop, bool *all_space, bool *only_newlines);
void increment(char c, size_t num_bytes, const char **i, size_t *line, size_t *col);
bool is_lowercase_hex(char c);
bool is_xmq_token_whitespace(char c);
bool is_xml_whitespace(char c);
bool is_all_xml_whitespace(const char *s);
bool is_xmq_element_name(const char *start, const char *stop, const char **colon);
bool is_xmq_element_start(char c);
bool is_xmq_text_name(char c);
bool is_hex(char c);
unsigned char hex_value(char c);
bool is_unicode_whitespace(const char *start, const char *stop);
size_t num_utf8_bytes(char c);
size_t peek_utf8_char(const char *start, const char *stop, UTF8Char *uc);
void str_b_u_len(const char *start, const char *stop, size_t *b_len, size_t *u_len);
char to_hex(int c);
bool utf8_char_to_codepoint_string(UTF8Char *uc, char *buf);
char *xmq_quote_as_c(const char *start, const char *stop, bool add_quotes);
char *xmq_unquote_as_c(const char *start, const char *stop, bool remove_quotes);
char *potentially_add_leading_ending_space(const char *start, const char *stop);
bool find_line_col(const char *start, const char *stop, size_t at, int *line, int *col);
const char *find_eol_or_stop(const char *start, const char *stop);

// Fetch a pointer to a NULL ended array of char pointers to category parts for the given name.
// For input "Lu" this function returns the array { "Lu", 0 }
// For input "L" this function returns the array  { "Lu","Ll","Lt","Lm","Lo",0 }
const char **unicode_lookup_category_parts(const char *name);

// Return an array of unicode code points for a given category name.
// Ie. Ll=Letters lowercase, Lu=Letters uppercase etc.
bool unicode_get_category_part(const char *part, int **out_cat, size_t *out_cat_len);

bool category_has_code(int code, int *cat, size_t cat_len);

#define TEXT_MODULE

// PART H VECTOR ////////////////////////////////////////

struct Vector
{
    void **elements;
    size_t size;
    size_t elements_size;
};
typedef struct Vector Vector;

Vector *vector_create();
void vector_free(Vector *v);
void vector_free_and_values(Vector *v, FreeFuncPtr freefunc);
void vector_push_back(Vector *v, void *);
void *vector_pop_back(Vector *v);
void *vector_element_at(Vector *v, size_t i);

#define VECTOR_MODULE

// PART H XML ////////////////////////////////////////

#include<stdbool.h>
#include<libxml/tree.h>

int decode_entity_ref(const char *name);
void free_xml(xmlNode * node);
bool has_attributes(xmlNodePtr node);
bool is_attribute_node(const xmlNode *node);
bool is_comment_node(const xmlNode *node);
bool is_content_node(const xmlNode *node);
bool is_doctype_node(const xmlNode *node);
bool is_element_node(const xmlNode *node);
bool is_entity_node(const xmlNode *node);
bool is_key_value_node(xmlNodePtr node);
bool is_leaf_node(xmlNode *node);
bool is_pi_node(const xmlNode *node);
bool is_single_empty_text_node(xmlNodePtr node);
bool is_text_node(const xmlNode *node);
void xml_add_root_child(xmlDoc *doc, xmlNode *node);
const char *xml_attr_key(xmlAttr *attr);
char *xml_collapse_text(xmlNode *node);
const char *xml_element_content(xmlNode *node);
const char *xml_element_name(xmlNode *node);
const char *xml_element_ns_prefix(const xmlNode *node);
xmlAttr *xml_first_attribute(xmlNode *node);
xmlNode *xml_first_child(xmlNode *node);
xmlNs *xml_first_namespace_def(xmlNode *node);
xmlAttr *xml_get_attribute(xmlNode *node, const char *name);
bool xml_has_non_empty_namespace_defs(xmlNode *node);
xmlNode *xml_last_child(xmlNode *node);
const char *xml_namespace_href(xmlNs *ns);
xmlAttr *xml_next_attribute(xmlAttr *attr);
xmlNs *xml_next_namespace_def(xmlNs *ns);
xmlNode *xml_next_sibling(xmlNode *node);
bool xml_non_empty_namespace(xmlNs *ns);
xmlNode *xml_prev_sibling(xmlNode *node);

#define XML_MODULE

// PART H XMQ_PARSER ////////////////////////////////////////

struct XMQParseState;
typedef struct XMQParseState XMQParseState;

void parse_xmq(XMQParseState *state);

void eat_xml_whitespace(XMQParseState *state, const char **start, const char **stop);
bool is_xmq_text_value(const char *start, const char *stop);
bool unsafe_value_start(char c, char cc);
bool is_safe_value_char(const char *i, const char *stop);

#define XMQ_PARSER_MODULE

// PART H XMQ_PRINTER ////////////////////////////////////////

struct XMQPrintState;
typedef struct XMQPrintState XMQPrintState;

#ifdef __cplusplus
enum Level : short;
#else
enum Level;
#endif
typedef enum Level Level;

int count_necessary_quotes(const char *start, const char *stop, bool *add_nls, bool *add_compound, bool prefer_double_quotes, bool *use_double_quotes);
size_t count_necessary_slashes(const char *start, const char *stop);

void print_nodes(XMQPrintState *ps, xmlNode *from, xmlNode *to, size_t align);
void print_content_node(XMQPrintState *ps, xmlNode *node);
void print_entity_node(XMQPrintState *ps, xmlNode *node);
void print_color_post(XMQPrintState *ps, XMQColor c);
void print_color_pre(XMQPrintState *ps, XMQColor c);
void print_comment_line(XMQPrintState *ps, const char *start, const char *stop, bool compact);
void print_comment_lines(XMQPrintState *ps, const char *start, const char *stop, bool compact);
void print_comment_node(XMQPrintState *ps, xmlNode *node);
size_t print_element_name_and_attributes(XMQPrintState *ps, xmlNode *node);
void print_leaf_node(XMQPrintState *ps,
                     xmlNode *node);
void print_key_node(XMQPrintState *ps,
                    xmlNode *node,
                    size_t align);
void print_element_with_children(XMQPrintState *ps,
                                 xmlNode *node,
                                 size_t align);
void print_doctype(XMQPrintState *ps, xmlNode *node);
void print_pi_node(XMQPrintState *ps, xmlNode *node);
void print_node(XMQPrintState *ps, xmlNode *node, size_t align);

void print_white_spaces(XMQPrintState *ps, int num);
void print_all_whitespace(XMQPrintState *ps, const char *start, const char *stop, Level level);
void print_explicit_spaces(XMQPrintState *ps, XMQColor c, int num);
void print_quoted_spaces(XMQPrintState *ps, XMQColor color, int num);
void print_quotes(XMQPrintState *ps, int num, XMQColor color, bool use_double_quotes);
void print_double_quote(XMQPrintState *ps, XMQColor color);
void print_nl(XMQPrintState *ps, const char *prefix, const char *postfix);
void print_nl_and_indent(XMQPrintState *ps, const char *prefix, const char *postfix);
size_t print_char_entity(XMQPrintState *ps, XMQColor color, const char *start, const char *stop);
void print_slashes(XMQPrintState *ps, const char *pre, const char *post, size_t n);


bool need_separation_before_attribute_key(XMQPrintState *ps);
bool need_separation_before_entity(XMQPrintState *ps);
bool need_separation_before_element_name(XMQPrintState *ps);
bool need_separation_before_quote(XMQPrintState *ps);
bool need_separation_before_comment(XMQPrintState *ps);
void check_space_before_attribute(XMQPrintState *ps);
void check_space_before_entity_node(XMQPrintState *ps);
void check_space_before_quote(XMQPrintState *ps, Level level);
void check_space_before_key(XMQPrintState *ps);
void check_space_before_opening_brace(XMQPrintState *ps);
void check_space_before_closing_brace(XMQPrintState *ps);
void check_space_before_comment(XMQPrintState *ps);

void print_attribute(XMQPrintState *ps, xmlAttr *a, size_t align);
void print_namespace_declaration(XMQPrintState *ps, xmlNs *ns, size_t align);
void print_attributes(XMQPrintState *ps, xmlNodePtr node);

void print_quote_lines_and_color_uwhitespace(XMQPrintState *ps,
                                             XMQColor color,
                                             const char *start,
                                             const char *stop);
void print_safe_leaf_quote(XMQPrintState *ps,
                           XMQColor c,
                           const char *start,
                           const char *stop);
const char *find_next_line_end(XMQPrintState *ps, const char *start, const char *stop);
const char *find_next_char_that_needs_escape(XMQPrintState *ps, const char *start, const char *stop);
void print_value_internal_text(XMQPrintState *ps, const char *start, const char *stop, Level level);
void print_value_internal(XMQPrintState *ps, xmlNode *node, Level level);
bool quote_needs_compounded(XMQPrintState *ps, const char *start, const char *stop);
void print_value(XMQPrintState *ps, xmlNode *node, Level level);

#define XMQ_PRINTER_MODULE

// PART H YAEP_ALLOCATE ////////////////////////////////////////

#include<stddef.h>

/**
 * Type for functions behaving like standard malloc().
 *
 * @sa #Yaep_calloc()
 * @sa #Yaep_realloc()
 * @sa #Yaep_free()
 */
typedef void *(*Yaep_malloc) (size_t);

/**
 * Type for functions behaving like standard calloc().
 *
 * @sa #Yaep_malloc()
 * @sa #Yaep_realloc()
 * @sa #Yaep_free()
 */
typedef void *(*Yaep_calloc) (size_t, size_t);

/**
 * Type for functions behaving like standard realloc().
 *
 * @sa #Yaep_malloc()
 * @sa #Yaep_calloc()
 * @sa #Yaep_free()
 */
typedef void *(*Yaep_realloc) (void *, size_t);

/**
 * Type for functions behaving like standard free().
 *
 * @sa #Yaep_malloc()
 * @sa #Yaep_calloc()
 * @sa #Yaep_realloc()
 */
typedef void (*Yaep_free) (void *ptr);

/**
 * Callback type for allocation errors.
 *
 * It is not necessary for callbacks of this type
 * to return to the caller.
 *
 * @param userptr Pointer provided earlier by the caller.
 *
 * @sa #yaep_alloc_geterrfunc()
 * @sa #yaep_alloc_seterr()
 */
typedef void (*Yaep_alloc_error) (void *userptr);

/**
 * YAEP allocator type.
 *
 * @sa #yaep_alloc_new()
 */
typedef struct YaepAllocator YaepAllocator;

/**
 * Default error handling function.
 *
 * This function writes an error message to @ stderr
 * and exits the program.
 *
 * @param ignored Ignored parameter.
 *
 * @sa #yaep_alloc_seterr()
 */
void yaep_alloc_defaulterrfunc (void *ignored);

/**
 * Creates a new allocator.
 *
 * The new allocator uses #yaep_alloc_defaulterrfunc()
 * and a null user pointer.
 *
 * @param mallocf Pointer to function which behaves like @c malloc().
 * 	If this is a null pointer, @c malloc() is used instead.
 * @param callocf Pointer to function which behaves like @c calloc().
 * 	If this is a null pointer, a function which behaves like @c calloc()
 * 	and is compatible with the provided @c mallocf is used instead.
 * @param reallocf Pointer to function which behaves analogously to
 * 	@c realloc() with respect to @c mallocf and @c callocf.
 * 	If this is a null pointer, and @c malloc() and @c calloc() are used
 * 	for @c mallocf and @c callocf, respectively,
 * 	then @c realloc() is used instead.
 * 	Everything else is an error.
 * @param freef Pointer to function which can free memory returned by
 * 	@c mallocf, @c callocf, and @c reallocf.
 * 	If this is a null pointer, and @c malloc(), @c calloc(), and
 * 	@c realloc() are used for @c mallocf, @c callocf, and @c reallocf,
 * 	respectively, then @c free() is used instead.
 * 	Everything else is an error.
 *
 * @return On success, a pointer to the new allocator is returned.\n
 * 	On error, a null pointer is returned.
 *
 * @sa #yaep_alloc_del()
 * @sa #yaep_alloc_seterr()
 * @sa #yaep_alloc_defaulterrfunc()
 */
YaepAllocator *yaep_alloc_new (Yaep_malloc mallocf, Yaep_calloc callocf,
			       Yaep_realloc reallocf, Yaep_free freef);

/**
 * Destroys an allocator.
 *
 * @param allocator Pointer to allocator.
 *
 * @sa #yaep_alloc_new()
 */
void yaep_alloc_del (YaepAllocator * allocator);

/**
 * Allocates memory.
 *
 * @param allocator Pointer to allocator.
 * @param size Number of bytes to allocate.
 *
 * @return On success, a pointer to the allocated memory is returned.
 * 	If @c size was zero, this may be a null pointer.\n
 * 	On error, the allocator's error function is called.
 * 	If that function returns, a null pointer is returned.
 *
 * @sa #yaep_free()
 * @sa #yaep_realloc()
 * @sa #yaep_calloc()
 * @sa #yaep_alloc_seterr()
 */
void *yaep_malloc (YaepAllocator * allocator, size_t size);

/**
 * Allocates zero-initialised memory.
 *
 * @param allocator Pointer to allocator.
 * @param nmemb Number of elements to allocate.
 * @param size Element size in bytes.
 *
 * @return On success, a pointer to <code>nmemb * size</code> bytes
 * 	of newly allocated, zero-initialised  memory is returned.
 * 	If <code>nmemb * size</code> was zero, this may be a null pointer.\n
 * 	On error, the allocator's error function is called.
 * 	If that function returns, a null pointer is returned.
 *
 * @sa #yaep_free()
 * @sa #yaep_realloc()
 * @sa #yaep_malloc()
 * @sa #yaep_alloc_seterr()
 */
void *yaep_calloc (YaepAllocator * allocator, size_t nmemb, size_t size);

/**
 * Resizes memory.
 *
 * @param allocator Pointer to allocator previously used to
 * 	allocate @c ptr.
 * @param ptr Pointer to memory previously returned by
 * 	#yaep_malloc(), #yaep_calloc(), or #yaep_realloc().
 * 	If this is a null pointer, this function behaves like #yaep_malloc().
 * @param size New memory size in bytes.
 *
 * @return On success, a pointer to @c size bytes of allocated memory
 * 	is returned, the contents of which is equal to the contents of
 * 	@c ptr immediately before the call, up to the smaller size of
 * 	both blocks.\n
 * 	On error, the allocator's error function is called.
 * 	If that function returns, a null pointer is returned.
 *
 * @sa #yaep_free()
 * @sa #yaep_malloc()
 * @sa #yaep_calloc()
 * @sa #yaep_alloc_seterr()
 */
void *yaep_realloc (YaepAllocator * allocator, void *ptr, size_t size);

/**
 * Frees previously allocated memory.
 *
 * @param allocator Pointer to allocator previously used to
 * 	allocate @c ptr.
 * @param ptr Pointer to memory to be freed.
 * 	If this is a null pointer, no operation is performed.
 *
 * @sa #yaep_malloc()
 * @sa #yaep_calloc()
 * @sa #yaep_realloc()
 */
void yaep_free (YaepAllocator * allocator, void *ptr);

/**
 * Obtains the current error function of an allocator.
 *
 * @param allocator Pointer to allocator.
 *
 * @return On success, a pointer to the error function of the
 * 	specified allocator is returned.
 * 	If no error function has ever been set for this allocator,
 * 	this is #yaep_alloc_defaulterrfunc().\n
 * 	On error, a null pointer is returned.
 *
 * @sa #yaep_alloc_getuserptr()
 * @sa #yaep_alloc_seterr()
 * @sa #yaep_alloc_defaulterrfunc()
 */
Yaep_alloc_error yaep_alloc_geterrfunc (YaepAllocator * allocator);

/**
 * Obtains the current user-provided pointer.
 *
 * @param allocator Pointer to allocator.
 *
 * @return On success, the user-provided pointer of the
 * 	specified allocator is returned.
 * 	If no pointer has ever been set for this allocator,
 * 	a pointer to the allocator itself is returned.\n
 * 	On error, a null pointer is returned.
 *
 * @sa #yaep_alloc_seterr()
 */
void *yaep_alloc_getuserptr (YaepAllocator * allocator);

/**
 * Sets the error function and user-provided pointer of an allocator.
 *
 * The error function is called by the allocator with the user-provided
 * pointer as argument whenever an allocation error occurs.
 *
 * @param allocator Pointer to allocator.
 * @param errfunc Pointer to error function.
 * 	If this is a null pointer, #yaep_alloc_defaulterrfunc() will be used.
 * @param userptr User-provided pointer.
 * 	The allocator will never attempt to dereference this pointer.
 *
 * @sa #yaep_alloc_geterrfunc()
 * @sa #yaep_alloc_getuserptr()
 */
void yaep_alloc_seterr (YaepAllocator * allocator, Yaep_alloc_error errfunc,
			void *userptr);

#define YAEP_ALLOCATE_MODULE

// PART H YAEP_HASHTAB ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include <stdbool.h>
#include <stdlib.h>

#include"yaep_allocate.h"

#endif

/* The hash table element is represented by the following type. */

typedef const void *hash_table_entry_t;

typedef unsigned int ( *hash_table_hash_function )(hash_table_entry_t el_ptr);
typedef bool (*hash_table_eq_function) (hash_table_entry_t el1_ptr, hash_table_entry_t el2_ptr);


/* Hash tables are of the following type.  The structure
   (implementation) of this type is not needed for using the hash
   tables.  All work with hash table should be executed only through
   functions mentioned below. */

typedef struct
{
  /* Current size (in entries) of the hash table */
  size_t size;
  /* Current number of elements including also deleted elements */
  size_t number_of_elements;
  /* Current number of deleted elements in the table */
  size_t number_of_deleted_elements;
  /* The following member is used for debugging. Its value is number
     of all calls of `find_hash_table_entry' for the hash table. */
  int searches;
  /* The following member is used for debugging.  Its value is number
     of collisions fixed for time of work with the hash table. */
  int collisions;
  /* Pointer to function for evaluation of hash value (any unsigned value).
     This function has one parameter of type hash_table_entry_t. */
  hash_table_hash_function hash_function;
  /* Pointer to function for test on equality of hash table elements (two
     parameter of type hash_table_entry_t. */
  hash_table_eq_function eq_function;
  /* Table itself */
  hash_table_entry_t *entries;
  /* Allocator */
  YaepAllocator * alloc;
} *hash_table_t;


/* The following variable is used for debugging. Its value is number
   of all calls of `find_hash_table_entry' for all hash tables. */

//extern int all_searches;

/* The following variable is used for debugging. Its value is number
   of collisions fixed for time of work with all hash tables. */

//extern int all_collisions;

/* The prototypes of the package functions. */

extern hash_table_t create_hash_table(YaepAllocator * allocator,
                                      size_t size,
                                      hash_table_hash_function hash_function,
                                      hash_table_eq_function eq_function);

extern void empty_hash_table (hash_table_t htab);

extern void delete_hash_table (hash_table_t htab);

extern hash_table_entry_t *find_hash_table_entry(hash_table_t htab,
                                                 hash_table_entry_t element,
                                                 bool reserve);

extern void remove_element_from_hash_table_entry (hash_table_t htab,
                                                  hash_table_entry_t element);

extern size_t hash_table_size (hash_table_t htab);

extern size_t hash_table_elements_number (hash_table_t htab);

/* The following function returns number of searches during all work
   with given hash table. */
static inline int
get_searches (hash_table_t htab)
{
  return htab->searches;
}

/* The following function returns number of occurred collisions during
   all work with given hash table. */
static inline int
get_collisions (hash_table_t htab)
{
  return htab->collisions;
}

/* The following function returns number of searches during all work
   with all hash tables. */
int get_all_searches();
/* The following function returns number of occurred collisions
   during all work with all hash tables. */
int get_all_collisions();
int hash_table_collision_percentage (hash_table_t htab);
int all_hash_table_collision_percentage (void);

size_t hash_table_memusage(hash_table_t htab);

#define YAEP_HASHTAB_MODULE

// PART H YAEP_OBJSTACK ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>

#include "yaep_allocate.h"

#endif

/* This auxiliary structure is used to evaluation of maximum
   alignment for objects. */

struct _os_auxiliary_struct
{
  char _os_character;
  double _os_double;
};

/* This macro is auxiliary.  Its value is maximum alignment for objects. */

#ifdef VMS
/* It is necessarry for VMS C compiler. */
#define _OS_ALIGNMENT  4
#else
#define _OS_ALIGNMENT offsetof (struct _os_auxiliary_struct, _os_double)
#if 0
#define _OS_ALIGNMENT\
  ((char *) &((struct _os_auxiliary_struct *) 64)->_os_double - (char *) 64)
#else
#endif
#endif

/* This macro is auxiliary.  Its value is aligned address nearest to `a'. */

#define _OS_ALIGNED_ADDRESS(a)\
  ((void *) ((size_t) ((char *) (a) + (_OS_ALIGNMENT - 1))\
             & (~(size_t) (_OS_ALIGNMENT - 1))))

/* This macro value is default size of memory segments which will be
   allocated for OS when the stack is created (with zero initial
   segment size).  This is also minimal size of all segments.
   Original value of the macros is equal to 512.  This macro can be
   redefined in C compiler command line or with the aid of directive
   `#undef' before any using the package macros.  */

#ifndef OS_DEFAULT_SEGMENT_LENGTH
#define OS_DEFAULT_SEGMENT_LENGTH 512
#endif

/* This internal structure describes segment of an object stack. */

struct _os_segment
{
    struct _os_segment *os_previous_segment;
    size_t os_segment_size; // Fredrik added this, does this change the OS_ALIGNMENT calculation? TODO.
    char os_segment_content[_OS_ALIGNMENT];
};

/* This type describes a descriptor of stack of objects.  All work
   with stack of objects is executed by following macros through the
   descriptors.  Structure (implementation) of this type is not needed
   for using stack of objects.  But it should remember that work with
   the stack through several descriptors is not safe. */

typedef struct
{
  /* The real length of the first memory segment. */
  size_t initial_segment_length;
  struct _os_segment *os_current_segment;

  /* Pointer to memory currently used for storing the top object. */
  char *os_top_object_start;

  /* Pointer to first byte after the last byte of the top object. */
  char *os_top_object_stop;

  /* Pointer to first byte after the memory allocated for storing
     the OS segment and the top object. */
  char *os_segment_stop;

  /* Pointer to allocator. */
  YaepAllocator *os_alloc;
} os_t;


/* This macro creates OS which contains the single zero length object.
   If initial length of OS segment is equal to zero the allocated
   memory segments length is equal to `OS_DEFAULT_SEGMENT_LENGTH'.
   But in any case the segment length is always not less than maximum
   alignment.  OS must be created before any using other macros of the
   package for work with given OS.  The macro has no side effects. */

#define OS_CREATE( os, allocator, initial_segment_length ) \
  do { \
    os_t * _temp_os = &( os ); \
    _temp_os->os_alloc = allocator; \
    _OS_create_function( _temp_os, initial_segment_length ); \
  } while( 0 )

/* This macro is used for freeing memory allocated for OS.  Any work
   (except for creation) with given OS is not possible after
   evaluation of this macros.  The macro has no side effects. */

#define OS_DELETE(os) _OS_delete_function (& (os))

/* This macro is used for freeing memory allocated for OS except for
   the first segment.  The macro has no side effects. */

#define OS_EMPTY(os) _OS_empty_function (& (os))

/* This macro makes that length of variable length object on the top
   of OS will be equal to zero.  The macro has no side effects. */

#define OS_TOP_NULLIFY(os)\
  do\
  {\
    os_t *_temp_os = &(os);\
    assert (_temp_os->os_top_object_start != NULL);\
    _temp_os->os_top_object_stop = _temp_os->os_top_object_start;\
  }\
  while (0)

/* The macro creates new variable length object with initial zero
   length on the top of OS .  The work (analogous to one with variable
   length object) with object which was on the top of OS are finished,
   i.e. the object will never more change address.  The macro has not
   side effects. */

#define OS_TOP_FINISH(os)\
  do\
  {\
    os_t *_temp_os = &(os);\
    assert (_temp_os->os_top_object_start != NULL);\
    _temp_os->os_top_object_start\
        = (char *) _OS_ALIGNED_ADDRESS (_temp_os->os_top_object_stop);    \
    _temp_os->os_top_object_stop = _temp_os->os_top_object_start;\
  }\
  while (0)

/* This macro returns current length of variable length object on the
   top of OS.  The macro has side effects! */

#ifndef NDEBUG
#define OS_TOP_LENGTH(os)\
  ((os).os_top_object_start != NULL\
   ? (os).os_top_object_stop - (os).os_top_object_start\
   : (abort (), 0))
#else
#define OS_TOP_LENGTH(os)  ((os).os_top_object_stop - (os).os_top_object_start)
#endif

/* This macro returns pointer to the first byte of variable length
   object on the top of OS.  The macro has side effects!  Remember also
   that the top object may change own place after any addition. */

#ifndef NDEBUG
#define OS_TOP_BEGIN(os)\
  ((os).os_top_object_start != NULL ? (void *) (os).os_top_object_start\
                                    : (abort (), (void *) 0))
#else
#define OS_TOP_BEGIN(os) ((void *) (os).os_top_object_start)
#endif

/* This macro returns pointer (of type `void *') to the last byte of
   variable length object on the top OS.  The macro has side effects!
   Remember also that the top object may change own place after any
   addition. */

#ifndef NDEBUG
#define OS_TOP_END(os)\
  ((os).os_top_object_start != NULL ? (void *) ((os).os_top_object_stop - 1)\
                                    : (abort (), (void *) 0))
#else
#define OS_TOP_END(os) ((void *) ((os).os_top_object_stop - 1))
#endif

/* This macro returns pointer (of type `void *') to the next byte of
   the last byte of variable length object on the top OS.  The macro
   has side effects!  Remember also that the top object may change own
   place after any addition. */

#ifndef NDEBUG
#define OS_TOP_BOUND(os)\
  ((os).os_top_object_start != NULL ? (void *) (os).os_top_object_stop\
                                    : (abort (), (void *) 0))
#else
#define OS_TOP_BOUND(os) ((void *) (os).os_top_object_stop)
#endif

/* This macro removes N bytes from the end of variable length object
   on the top of OS.  The top variable length object is nullified if
   its length is less than N.  The macro has no side effects. */

#define OS_TOP_SHORTEN(os, n)\
  do\
  {\
    os_t *_temp_os = &(os);\
    size_t _temp_n = (n);\
    assert (_temp_os->os_top_object_start != NULL);\
    if ((size_t) OS_TOP_LENGTH (*_temp_os) < _temp_n)\
      _temp_os->os_top_object_stop = _temp_os->os_top_object_start;\
    else\
      _temp_os->os_top_object_stop -= _temp_n;\
  }\
  while (0)

/* This macro increases length of variable length object on the top of
   OS on given number of bytes.  The values of bytes added to the end
   of variable length object on the top of OS will be not defined.
   The macro has no side effects. */

#define OS_TOP_EXPAND(os, length)\
  do\
  {\
    os_t *_temp_os = &(os);\
    size_t _temp_length = (length);\
    assert (_temp_os->os_top_object_start != NULL);\
    if (_temp_os->os_top_object_stop + _temp_length > _temp_os->os_segment_stop)\
      _OS_expand_memory (_temp_os, _temp_length);\
    _temp_os->os_top_object_stop += _temp_length;\
  }\
  while (0)

/* This macro adds byte to the end of variable length object on the
   top of OS.  The macro has no side effects. */

#define OS_TOP_ADD_BYTE(os, b)\
  do\
  {\
    os_t *_temp_os = &(os);\
    assert (_temp_os->os_top_object_start != NULL);\
    if (_temp_os->os_top_object_stop >= _temp_os->os_segment_stop)\
      _OS_expand_memory (_temp_os, 1);\
    *_temp_os->os_top_object_stop++ = (b);\
  }\
  while (0)

/* This macro adds memory bytes to the end of variable length object
   on the top of OS.  The macro has no side effects. */

#define OS_TOP_ADD_MEMORY(os, str, length)\
  do\
  {\
    os_t *_temp_os = &(os);\
    size_t _temp_length = (length);\
    assert (_temp_os->os_top_object_start != NULL);\
    if (_temp_os->os_top_object_stop + _temp_length > _temp_os->os_segment_stop)\
      _OS_expand_memory (_temp_os, _temp_length);\
    memcpy( _temp_os->os_top_object_stop, ( str ), _temp_length ); \
    _temp_os->os_top_object_stop += _temp_length;\
  }\
  while (0)

/* This macro adds C string (with end marker '\0') to the end of
   variable length object on the top of OS.  Before the addition the
   macro delete last character of the object.  The last character is
   suggested to be C string end marker '\0'.  The macro has no side
   effects. */

#define OS_TOP_ADD_STRING(os, str) _OS_add_string_function(&(os), (str))

/* The following functions are to be used only by the package macros.
   Remember that they are internal functions - all work with OS is
   executed through the macros. */

extern void _OS_create_function (os_t *os, size_t initial_segment_length);
extern void _OS_delete_function (os_t *os);
extern void _OS_empty_function (os_t *os);
extern void _OS_add_string_function (os_t *os, const char *str);
extern void _OS_expand_memory (os_t *os, size_t additional_length);

/* Return total amount of memory used by this objstack. */
size_t objstack_memusage(os_t *os);

#define YAEP_OBJSTACK_MODULE

// PART H YAEP_VLOBJECT ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "yaep_allocate.h"

#endif

/* Default initial size of memory is allocated for VLO when the object
   is created (with zero initial size).  This macro can be redefined
   in C compiler command line or with the aid of directive `#undef'
   before any using the package macros. */

#ifndef VLO_DEFAULT_LENGTH
#define VLO_DEFAULT_LENGTH 512
#endif

/* This type describes a descriptor of variable length object.  All
   work with variable length object is executed by following macros
   through the descriptors.  Structure (implementation) of this type
   is not needed for using variable length object.  But it should
   remember that work with the object through several descriptors is
   not safe. */

typedef struct
{
  /* Pointer to memory currently used for storing the VLO. */
  char *vlo_start;
  /* Pointer to first byte after the last VLO byte. */
  char *vlo_stop;
  /* Pointer to first byte after the memory currently allocated for storing
     the VLO. */
  char *vlo_segment_stop;
  /* Pointer to allocator. */
  YaepAllocator *vlo_alloc;
} vlo_t;


/* This macro is used for creation of VLO with initial zero length.
   If initial length of memory needed for the VLO is equal to 0 the
   initial allocated memory length is equal to VLO_DEFAULT_LENGTH.
   VLO must be created before any using other macros of the package
   for work with given VLO.  The macro has not side effects. */

#define VLO_CREATE(vlo, allocator, initial_length)\
  do\
  {\
    vlo_t *_temp_vlo = &(vlo);\
    size_t temp_initial_length = (initial_length);\
    YaepAllocator * _temp_alloc = ( allocator ); \
    temp_initial_length = (temp_initial_length != 0 ? temp_initial_length\
                                                    : VLO_DEFAULT_LENGTH);\
    _temp_vlo->vlo_start = (char*)yaep_malloc( _temp_alloc, temp_initial_length ); \
    _temp_vlo->vlo_segment_stop = _temp_vlo->vlo_start + temp_initial_length;\
    _temp_vlo->vlo_stop = _temp_vlo->vlo_start;\
    _temp_vlo->vlo_alloc = _temp_alloc; \
  }\
  while (0)


/* This macro is used for freeing memory allocated for VLO.  Any work
   (except for creation) with given VLO is not possible after
   evaluation of this macro.  The macro has not side effects. */

#ifndef NDEBUG
#define VLO_DELETE(vlo)\
  do\
  {\
    vlo_t *_temp_vlo = &(vlo);\
    assert (_temp_vlo->vlo_start != NULL);\
    yaep_free( _temp_vlo->vlo_alloc,_temp_vlo->vlo_start );\
    _temp_vlo->vlo_start = _temp_vlo->vlo_segment_stop = NULL;\
  }\
  while (0)
#else
#define VLO_DELETE(vlo) \
  do { \
    vlo_t * _temp_vlo = &( vlo ); \
    yaep_free( _temp_vlo->vlo_alloc, _temp_vlo->vlo_start ); \
    _temp_vlo->vlo_start = _temp_vlo->vlo_segment_stop = NULL;\
  } while( 0 )
#endif /* #ifndef NDEBUG */

/* This macro makes that length of VLO will be equal to zero (but
   memory for VLO is not freed and not reallocated).  The macro has
   not side effects. */

#define VLO_NULLIFY(vlo)\
  do\
  {\
    vlo_t *_temp_vlo = &(vlo);\
    assert (_temp_vlo->vlo_start != NULL);\
    _temp_vlo->vlo_stop = _temp_vlo->vlo_start;\
  }\
  while (0)


/* The following macro makes that length of memory allocated for VLO
   becames equal to VLO length.  The macro has not side effects. */

#define VLO_TAILOR(vlo) _VLO_tailor_function(&(vlo))


/* This macro returns current length of VLO.  The macro has side effects! */

#ifndef NDEBUG
#define VLO_LENGTH(vlo) ((vlo).vlo_start != NULL\
                         ? (vlo).vlo_stop - (vlo).vlo_start\
                         : (abort (), 0))
#else
#define VLO_LENGTH(vlo) ((vlo).vlo_stop - (vlo).vlo_start)
#endif /* #ifndef NDEBUG */


/* This macro returns pointer (of type `void *') to the first byte of
   the VLO.  The macro has side effects!  Remember also that the VLO
   may change own place after any addition. */

#ifndef NDEBUG
#define VLO_BEGIN(vlo) ((vlo).vlo_start != NULL\
                        ? (void *) (vlo).vlo_start\
                        : (abort (), (void *) 0))
#else
#define VLO_BEGIN(vlo) ((void *) (vlo).vlo_start)
#endif /* #ifndef NDEBUG */

/* This macro returns pointer (of type `void *') to the last byte of
   VLO.  The macro has side effects!  Remember also that the VLO may
   change own place after any addition. */

#ifndef NDEBUG
#define VLO_END(vlo) ((vlo).vlo_start != NULL\
                      ? (void *) ((vlo).vlo_stop - 1)\
                      : (abort (), (void *) 0))
#else
#define VLO_END(vlo) ((void *) ((vlo).vlo_stop - 1))
#endif /* #ifndef NDEBUG */

/* This macro returns pointer (of type `void *') to the next byte of
   the last byte of VLO.  The macro has side effects!  Remember also
   that the VLO may change own place after any addition. */

#ifndef NDEBUG
#define VLO_BOUND(vlo) ((vlo).vlo_start != NULL\
                        ? (void *) (vlo).vlo_stop\
                        : (abort (), (void *) 0))
#else
#define VLO_BOUND(vlo) ((void *) (vlo).vlo_stop)
#endif /* #ifndef NDEBUG */

/* This macro removes N bytes from the end of VLO.  VLO is nullified
   if its length is less than N.  The macro has not side effects. */

#define VLO_SHORTEN(vlo, n)\
  do\
  {\
    vlo_t *_temp_vlo = &(vlo);\
    size_t _temp_n = (n);\
    assert (_temp_vlo->vlo_start != NULL);\
    if ((size_t) VLO_LENGTH (*_temp_vlo) < _temp_n)\
      _temp_vlo->vlo_stop = _temp_vlo->vlo_start;\
    else\
      _temp_vlo->vlo_stop -= _temp_n;\
  }\
  while (0)


/* This macro increases length of VLO.  The values of bytes added to
   the end of VLO will be not defined.  The macro has not side
   effects. */

#define VLO_EXPAND(vlo, length)\
  do\
  {\
    vlo_t *_temp_vlo = &(vlo);\
    size_t _temp_length = (length);\
    assert (_temp_vlo->vlo_start != NULL);\
    if (_temp_vlo->vlo_stop + _temp_length > _temp_vlo->vlo_segment_stop)\
      _VLO_expand_memory (_temp_vlo, _temp_length);\
    _temp_vlo->vlo_stop += _temp_length;\
  }\
  while (0)


/* This macro adds a byte to the end of VLO.  The macro has not side
   effects. */

#define VLO_ADD_BYTE(vlo, b)\
  do\
  {\
    vlo_t *_temp_vlo = &(vlo);\
    assert (_temp_vlo->vlo_start != NULL);\
    if (_temp_vlo->vlo_stop >= _temp_vlo->vlo_segment_stop)\
      _VLO_expand_memory (_temp_vlo, 1);\
    *_temp_vlo->vlo_stop++ = (b);\
  }\
  while (0)


/* This macro adds memory bytes to the end of VLO.  The macro has not
   side effects. */

#define VLO_ADD_MEMORY(vlo, str, length)\
  do\
  {\
    vlo_t *_temp_vlo = &(vlo);\
    size_t _temp_length = (length);\
    assert (_temp_vlo->vlo_start != NULL);\
    if (_temp_vlo->vlo_stop + _temp_length > _temp_vlo->vlo_segment_stop)\
      _VLO_expand_memory (_temp_vlo, _temp_length);\
    memcpy( _temp_vlo->vlo_stop, ( str ), _temp_length ); \
    _temp_vlo->vlo_stop += _temp_length;\
  }\
  while (0)


/* This macro adds C string (with end marker '\0') to the end of VLO.
   Before the addition the macro delete last character of the VLO.
   The last character is suggested to be C string end marker '\0'.
   The macro has not side effects. */

#define VLO_ADD_STRING(vlo, str) _VLO_add_string_function(&(vlo), (str))


/* The following functions are to be used only by the package macros.
   Remember that they are internal functions - all work with VLO is
   executed through the macros. */

extern void _VLO_tailor_function (vlo_t *vlo);
extern void _VLO_add_string_function (vlo_t *vlo, const char *str);
extern void _VLO_expand_memory (vlo_t *vlo, size_t additional_length);

size_t vlo_memusage(vlo_t *vlo);

#define YAEP_VLOBJECT_MODULE

// PART H YAEP ////////////////////////////////////////

#include<assert.h>
#include<limits.h>
#include<stdbool.h>
#include<stdlib.h>

#ifndef BUILDING_DIST_XMQ
#include "always.h"
#include "membuffer.h"
#endif

struct YaepGrammar;
typedef struct YaepGrammar YaepGrammar;

struct YaepParseRun;
typedef struct YaepParseRun YaepParseRun;

struct YaepAltNode;
typedef struct YaepAltNode YaepAltNode;

struct YaepErrorNode;
typedef struct YaepErrorNode YaepErrorNode;

struct YaepAbstractNode;
typedef struct YaepAbstractNode YaepAbstractNode;

struct YaepNilNode;
typedef struct YaepNilNode YaepNilNode;

struct YaepTerminalNode;
typedef struct YaepTerminalNode YaepTerminalNode;

struct YaepTreeNode;
typedef struct YaepTreeNode YaepTreeNode;

/* The following value is reserved to be designation of empty node for
   translation.  It should be positive number which is not intersected
   with symbol numbers. */
#define YAEP_NIL_TRANSLATION_NUMBER       INT_MAX

#define YAEP_NO_MEMORY                     1
#define YAEP_UNDEFINED_OR_BAD_GRAMMAR      2
#define YAEP_DESCRIPTION_SYNTAX_ERROR_CODE 3
#define YAEP_FIXED_NAME_USAGE              4
#define YAEP_REPEATED_TERM_DECL            5
#define YAEP_NEGATIVE_TERM_CODE            6
#define YAEP_REPEATED_TERM_CODE            7
#define YAEP_NO_RULES                      8
#define YAEP_TERM_IN_RULE_LHS              9
#define YAEP_INCORRECT_TRANSLATION         10
#define YAEP_NEGATIVE_COST                 11
#define YAEP_INCORRECT_SYMBOL_NUMBER       12
#define YAEP_REPEATED_SYMBOL_NUMBER        13
#define YAEP_UNACCESSIBLE_NONTERM          14
#define YAEP_NONTERM_DERIVATION            15
#define YAEP_LOOP_NONTERM                  16
#define YAEP_INVALID_TOKEN_CODE            17

struct YaepParseRun
{
    void *user_data; // Points to user supplied data, useful inside the callbacked read_token and syntax_error.
    const char *buffer_start; // Points to first token.
    const char *buffer_stop; // Points to address after last token.
    const char *buffer_i; // Points to the next token to read.
    // If read_token is NULL then use the built in read_token that uses buffer start,stop and i.
    int (*read_token)(YaepParseRun *pr, void **attr);
    // If syntax_error is NULL then use the built in stderr error message printer.
    void (*syntax_error)(YaepParseRun *pr,
                         int err_tok_num,
                         void *err_tok_attr,
                         int start_ignored_tok_num,
                         void *start_ignored_tok_attr,
                         int start_recovered_tok_num,
                         void *start_recovered_tok_attr);
    // If parse_alloc is NULL then use the built in allocator (malloc).
    void *(*parse_alloc)(int nmemb);
    // If parse_free is NULL then use the built in free (free).
    void (*parse_free)(void *mem);
    // The resulting DOM tree is stored here.
    YaepTreeNode *root;
    // Set to true if the parse was ambigious.
    bool ambiguous_p;
    // Set to true if the parse faild.
    bool failed_p;

    // We are parsing with this grammar.
    YaepGrammar *grammar;

    bool verbose;
    bool debug;
    bool trace;

    // This object continues with more data inside the implementation....
    // Do not allocate this object yourself, use yaepNewParseRun and yaepFreeParseRun instead.
};

/* The following describes the type of parse tree node. */
enum YaepTreeNodeType
{
    YAEP_NIL,
    YAEP_ERROR,
    YAEP_TERM,
    YAEP_ANODE,
    YAEP_ALT,
    /* _yaep_VISITED is not part of the interface and for internal use only */
    _yaep_VISITED = 0x80,
    /* _yaep_MAX is not part of the interface and is just here to ensure a
       logical OR of _yaep_VISITED with the other enum values does not produce
       an out-of-range enum */
    _yaep_MAX = 0xFF,
};
typedef enum YaepTreeNodeType YaepTreeNodeType;

/* The following node exists in one example. See comment to read_rule. */
struct YaepNilNode
{
    /* Whether this node has been used in the parse tree. */
    int used;
};

/* The following node exists in one example.  It is used as
   translation of pseudo terminal `error'. */
struct YaepErrorNode
{
    /* Whether this node has been used in the parse tree. */
    int used;
};

/* The following structure describes terminal node. */
struct YaepTerminalNode
{
    /* The terminal code. */
    int code;
    /* The IXML mark. */
    char mark;
    /* The terminal attributes. */
    void *attr;
};

/* The following structure describes abstract node. */
struct YaepAbstractNode
{
    /* The abstract node name. */
    const char *name;
    /* The ixml mark. */
    char mark;
    /* The following value is cost of the node plus costs of all
       children if the cost flag is set up.  Otherwise, the value is cost
       of the abstract node itself. */
    int cost;
    /* References for nodes for which the abstract node refers.  The end
       marker of the array is value NULL. */
    YaepTreeNode **children;
};

/* The following structure is not part of the interface and for internal use only */
struct _YaepAnodeName
{
    /* Allocated abstract node name. */
    char * name;
};
typedef struct _YaepAnodeName _YaepAnodeName;

/* The following structure describes alternatives in the parse tree.
   Which are presents only for ambiguous grammar. */
struct YaepAltNode
{
  /* The node (all node types but alternative) for this alternative. */
  YaepTreeNode *node;

  /* The next alternative. */
  YaepTreeNode *next;
};

/* The following structure describes generalized node of the parse tree. */
struct YaepTreeNode
{
  /* The type of node. */
  YaepTreeNodeType type;

  /* The node itself. */
  union
  {
    YaepNilNode nil;
    YaepErrorNode error;
    YaepTerminalNode terminal;
    YaepAbstractNode anode;
    YaepAltNode alt;
    _YaepAnodeName _anode_name; /* Internal use only */
  } val;
};

/* The following function creates an empty grammar. */
extern YaepGrammar *yaepNewGrammar();

/* The following function creates a parse run that uses the specified grammar.
   Each concurrent parse using the shared grammar needs a run to store the
   parse progress/state. */
extern YaepParseRun *yaepNewParseRun(YaepGrammar *g);

/* Set a pointer to a user structure that is available when callbacks are invoked,
   such as read_token when parsing. */
extern void yaepSetUserData(YaepGrammar *g, void *data);

/* Get the user data pointer from the grammar. */
extern void *yaepGetUserData(YaepGrammar *g);

/* The function returns the last occurred error code for given grammar. */
extern int yaep_error_code(YaepGrammar *g);

/* The function returns message are always contains error message
   corresponding to the last occurred error code. */
extern const char *yaep_error_message(YaepGrammar *g);

/* The following function reads terminals/rules into grammar G and
   checks it depending on STRICT_P.  It returns zero if it is all ok.
   Otherwise, the function returns error code occurred (its code will
   be in yaep_error_code and message in yaep_error_message).

   READ_TERMINAL is function for reading terminals.  This function is
   called before function read_rule.  The function should return the
   name and the code of the next terminal.  If all terminals have been
   read the function returns NULL.  The return code should be
   nonnegative.

   READ_RULE is function called to read the next rule.  This function
   is called after function read_terminal.  The function should return
   the name of LHS rule and array of names of symbols in RHS of the
   rule (the array end marker should be NULL).  If all rules have been
   read the function returns NULL.  All symbol with name which was not
   provided the previous function are considered to be nonterminal.
   The function also returns translation given by abstract node name
   and its fields which will be translation of symbols (with indexes
   given in array *TRANSL) in the RHS of the rule.  All indexes in
   TRANSL should be different (so the translation of a symbol can not
   be represented twice).  The end marker of the array should be a
   negative value.  There is a reserved value of the translation
   symbol number denoting empty node.  It is value defined by macro
   YAEP_NIL_TRANSLATION_NUMBER.  If *TRANSL is NULL or contains only
   the end marker, translations of the rule will be nil node.  If
   ABS_NODE is NULL, abstract node is not created.  In this case
   *TRANSL should be NULL or contain at most one element which means
   that the translation of the rule will be nil node or the
   translation of the symbol in RHS given by the single array element.
   The cost of the abstract node if given is passed through
   ANODE_COST. */
extern int yaep_read_grammar(YaepParseRun *ps,
                             YaepGrammar *g,
                             int strict_p,
                             const char *(*read_terminal) (YaepParseRun *pr,
                                                           YaepGrammar *g,
                                                           int *code),
                             const char *(*read_rule) (YaepParseRun *pr,
                                                       YaepGrammar *g,
                                                       const char ***rhs,
                                                       const char **abs_node,
                                                       int *anode_cost,
                                                       int **transl,
                                                       char *mark,
                                                       char **marks));

/* The following functions set up different parameters which affect
   parser work.  The functions return the previous parameter value.

   o lookahead_level means usage of static (if 1) or dynamic (2)
     lookahead to decrease size of sets.  Static lookaheads gives the
     best results with the point of space and speed, dynamic ones does
     slightly worse, and no usage of lookaheads does the worst.  The
     default value is 1.

   o one_parse_flag means building only one parse tree.  For
     unambiguous grammar the flag does not affect the result.  The
     default value is 1.

   o cost_flag means usage costs to build tree (trees if
     one_parse_flag is not set up) with minimal cost.  For unambiguous
     grammar the flag does not affect the result.  The default value
     is 0.

   o error_recovery_flag means making error recovery if syntax error
     occurred.  Otherwise, syntax error results in finishing parsing
     (although syntax_error is called once).  The default value is 1.

   o recovery_match means how much subsequent tokens should be
     successfully shifted to finish error recovery.  The default value is 3. */
extern int yaep_set_lookahead_level(YaepGrammar *grammar, int level);
extern bool yaep_set_one_parse_flag(YaepGrammar *grammar, bool flag);
extern bool yaep_set_cost_flag(YaepGrammar *grammar, bool flag);
extern bool yaep_set_error_recovery_flag(YaepGrammar *grammar, bool flag);
extern int yaep_set_recovery_match(YaepGrammar *grammar, int n_toks);

/* The following function parses input according read grammar.  The
   function returns the error code (which will be also in
   yaep_error_code).  If the code is zero, the function will also
   The *root will be NULL only if syntax error was occurred and error
   recovery was switched off).  The function sets up *AMBIGOUS_P if we
   found that the grammar is ambiguous (it works even we asked only one
   parse tree without alternatives).

   The function READ_TOKEN provides input tokens.  It returns code the
   next input token and its attribute.  If the function returns
   negative value we've read all tokens.

   Function SYNTAX_ERROR prints error message about syntax error
   which occurred on token with number ERR_TOK_NUM and attribute
   ERR_TOK_ATTR.  The following four parameters describes made error
   recovery which ignored tokens starting with token given by 3rd and
   4th parameters.  The first token which was not ignored is described
   by the last parameters.  If the number of ignored tokens is zero,
   the all parameters describes the same token.  If the error recovery
   is not made (see comments for function
   `yaep_set_error_recovery_flag'), the third and fifth parameters
   will be negative and forth and sixth parameters will be NULL.

   Function PARSE_ALLOC is used by YAEP to allocate memory
   for parse tree representation.  After calling yaep_fin we free
   all memory allocated by yaep parser.  At this point it is
   convenient to free all memory but parse tree.  Therefore we require
   the following function. If PARSE_ALLOC is a null pointer, then
   PARSE_FREE must also be a null pointer. In this case, YAEP will
   handle the memory management. Otherwise, the caller will be responsible to
   allocate and free memory for parse tree representation.  But the
   caller should not free the memory until yaep_fin is called.  The
   function may be called even during reading the grammar not only
   during the parsing.  Function PARSE_FREE is used by the parser to
   free memory allocated by PARSE_ALLOC. If PARSE_ALLOC is not NULL
   but PARSE_FREE is, the memory is not freed. In this case, the
   returned parse tree should also not be freed with yaep_free_tree(). */
extern int yaepParse(YaepParseRun *ps, YaepGrammar *g);

/* The following function frees memory allocated for the parse state. */
extern void yaepFreeParseRun(YaepParseRun *ps);

/* The following function frees memory allocated for the grammar. */
extern void yaepFreeGrammar(YaepParseRun *ps, YaepGrammar *g);

/* The following function frees memory allocated for the parse tree.
   It must not be called until after yaep_free_grammar() has been called.
   ROOT must be the root of the parse tree as returned by yaep_parse().
   If ROOT is a null pointer, no operation is performed.
   Otherwise, the argument passed for PARSE_FREE must be the same as passed for
   the parameter of the same name in yaep_parse() (but do not call this
   function with PARSE_FREE a null pointer if you called yaep_parse()
   with PARSE_ALLOC not a null pointer).
   Otherwise, if TERMCB is not a null pointer, it will be called
   exactly once for each term node in the parse tree.
   The TERMCB callback can be used by the caller
   to free the term attributes. The term node itself must not be freed. */
extern void yaepFreeTree(YaepTreeNode *root,
                         void (*parse_free)(void*),
                         void (*termcb)(YaepTerminalNode *termial));

#define YAEP_MODULE

// PART H YAEP_STRUCTS ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include <stdbool.h>
#include <setjmp.h>

#include "yaep.h"
#include "yaep_allocate.h"
#include "yaep_hashtab.h"
#include "yaep_objstack.h"
#include "yaep_vlobject.h"

#endif

/* Terminals are stored a in term set using bits in a bit array.
   The array consists of long ints, typedefed as terminal_bitset_el_t.
   A long int is 8 bytes, ie 64 bits. */
typedef long int terminal_bitset_t;

/* Calculate the number of required term set elements from the number of bits we want to store. */
#define CALC_NUM_ELEMENTS(num_bits) ((num_bits+CHAR_BIT*sizeof(terminal_bitset_t)-1)/(CHAR_BIT*sizeof(terminal_bitset_t)))
// This calculation translates for example into ((num_bits+63)/64)
// We round up to have enough long ints to store num_bits.

#ifndef YAEP_MAX_ERROR_MESSAGE_LENGTH
#define YAEP_MAX_ERROR_MESSAGE_LENGTH 200
#endif

/* As of Unicode 16 there are 155_063 allocated unicode code points.
   Lets pick 200_000 as the max, it shrinks to max-min code point anyway.*/
#define MAX_SYMB_CODE_TRANS_VECT_SIZE 200000

/* The initial length of array(in tokens) in which input tokens are placed.*/
#ifndef NUM_INITIAL_YAEP_INIT_TOKENS
#define NUM_INITIAL_YAEP_TOKENS 10000
#endif

/* The default number of tokens sucessfully matched to stop error recovery alternative(state). */
#define DEFAULT_RECOVERY_TOKEN_MATCHES 3

/* Define this if you want to reuse already calculated state sets
   when the matches lengths are identical. This considerably speeds up the parser. */
#define USE_SET_HASH_TABLE

/* This does not seem to be enabled by default? */
#define USE_CORE_SYMB_HASH_TABLE

/* Maximal goto sets saved for triple(set, terminal, lookahead). */
#define MAX_CACHED_GOTO_RESULTS 3

/* Prime number(79087987342985798987987) mod 32 used for hash calculations. */
static const unsigned jauquet_prime_mod32 = 2053222611;

/* Shift used for hash calculations. */
static const unsigned hash_shift = 611;

struct YaepSymbol;
typedef struct YaepSymbol YaepSymbol;

struct YaepSymbolStorage;
typedef struct YaepSymbolStorage YaepSymbolStorage;

struct YaepTerminalSet;
typedef struct YaepTerminalSet YaepTerminalSet;

struct YaepTerminalSetStorage;
typedef struct YaepTerminalSetStorage YaepTerminalSetStorage;

struct YaepRule;
typedef struct YaepRule YaepRule;

struct YaepRuleStorage;
typedef struct YaepRuleStorage YaepRuleStorage;

struct YaepVect;
typedef struct YaepVect YaepVect;

struct YaepCoreSymbToPredComps;
typedef struct YaepCoreSymbToPredComps YaepCoreSymbToPredComps;

struct YaepStateSetCore;
typedef struct YaepStateSetCore YaepStateSetCore;

struct YaepStateSet;
typedef struct YaepStateSet YaepStateSet;

struct YaepDottedRule;
typedef struct YaepDottedRule YaepDottedRule;

struct YaepInputToken;
typedef struct YaepInputToken YaepInputToken;

struct YaepStateSetTermLookAhead;
typedef struct YaepStateSetTermLookAhead YaepStateSetTermLookAhead;

struct YaepParseTreeBuildState;
typedef struct YaepParseTreeBuildState YaepParseTreeBuildState;

struct YaepTreeNodeVisit;
typedef struct YaepTreeNodeVisit YaepTreeNodeVisit;

struct YaepRecoveryState;
typedef struct YaepRecoveryState YaepRecoveryState;

// Structure definitions ////////////////////////////////////////////////////

struct YaepGrammar
{
    /* Set to true if the grammar is undefined(you should set up the grammar
       by yaep_read_grammar or yaep_parse_grammar) or bad(error was occured
       in setting up the grammar). */
    bool undefined_p;

    /* This member always contains the last occurred error code for given grammar. */
    int error_code;

    /* This member contains message are always contains error message
       corresponding to the last occurred error code. */
    char error_message[YAEP_MAX_ERROR_MESSAGE_LENGTH + 1];

    /* The grammar axiom is named $. */
    YaepSymbol *axiom;

    /* The end marker denotes EOF in the input token sequence. */
    YaepSymbol *end_marker;

    /* The term error is used for create error recovery nodes. */
    YaepSymbol *term_error;

    /* And its internal id. */
    int term_error_id;

    /* The level of usage of lookaheads:
       0    - no usage
       1    - statik lookaheads
       >= 2 - dynamic lookaheads*/
    int lookahead_level;

    /* How many subsequent tokens should be successfuly shifted to finish error recovery. */
    int recovery_token_matches;

    /* If true then stop at first successful parse.
       If false then follow all possible parses. */
    bool one_parse_p;

    /* If true then find parse with minimal cost. */
    bool cost_p;

    /* If true them try to recover from errors. */
    bool error_recovery_p;

    /* These are all the symbols used in this grammar. */
    YaepSymbolStorage *symbs_ptr;

    /* All rules used in this grammar are stored here. */
    YaepRuleStorage *rulestorage_ptr;

    /* The following terminal sets used for this grammar. */
    YaepTerminalSetStorage *term_sets_ptr;

    /* Allocator. */
    YaepAllocator *alloc;

    /* A user supplied pointer that is available to user callbacks through the grammar pointer. */
    void *user_data;

    /* Long jump here if there is an error. */
    jmp_buf error_longjump_buff;
};

struct YaepSymbol
{
    /* A unique number 0,1,2... (num_terminals + num_non_terminals -1) */
    int id;
    /* The following is external representation of the symbol.  It
       should be allocated by parse_alloc because the string will be
       referred from parse tree. */
    const char *repr;
    char hr[7];    // #1ffff or ' ' or #78 or #0 (for eof)
    union
    {
        struct
        {
            /* The code is a unique number per terminal type and is specified when
               read_grammar fetches the terminals. For grammars with a lexer preprocessing
               step, the code means 1 = "BEGIN", 2 = "END, 3 = "IDENTIFIER" etc.
               For ixml grammars, each code is instead a unicode codepoint.
               I.e. 65 = A, 0x1f600 = 😀  etc. */
            int code;
            /* Each term is given a unique integer starting from 0. If the code range
               starts with 100 and ends with 129,then the term_ids goes from 0 to 29.
               The term_ids are used for picking the bit in the bit arrays. */
            int term_id;
        } terminal;
        struct
        {
            /* The following refers for all rules with the nonterminal
               symbol is in the left hand side of the rules. */
            YaepRule *rules;
            /* Each nonterm is given a unique integer starting from 0. */
            int nonterm_id;
            /* The following value is nonzero if nonterminal may derivate
               itself.  In other words there is a grammar loop for this
               nonterminal. */
            bool loop_p;
            /* The following members are FIRST and FOLLOW sets of the nonterminal. */
            terminal_bitset_t *first, *follow;
        } nonterminal;
    } u;
    /* If true, the use terminal in union. */
    bool is_terminal;
    /* The following member value(if defined) is true if the symbol is
       accessible(derivated) from the axiom. */
    bool access_p;
    /* The following member is true if it is a termainal/nonterminal which derives a terminal string. */
    bool derivation_p;
    /* The following is true if it is a nonterminal which may derive the empty string. */
    bool empty_p;
#ifdef USE_CORE_SYMB_HASH_TABLE
    /* The following is used as cache for subsequent search for
       core_symb_to_predcomps with given symb. */
    YaepCoreSymbToPredComps *cached_core_symb_to_predcomps;
#endif
    /* Is it a not operator? E.g. !"CHAPTER" ![L] */
    bool is_not_operator;
};

struct YaepSymbolStorage
{
    int num_terminals, num_nonterminals;

    /* All symbols are placed in the following object.*/
    os_t symbs_os;

    /* All references to the symbols, terminals, nonterminals are stored
       in the following vlos.  The indexes in the arrays are the same as
       corresponding symbol, terminal, and nonterminal numbers.*/
    vlo_t symbs_vlo;
    vlo_t terminals_vlo;
    vlo_t nonterminals_vlo;

    /* The following are tables to find terminal by its code and symbol by
       its representation.*/
    hash_table_t map_repr_to_symb;        /* key is `repr'*/
    hash_table_t map_code_to_symb;        /* key is `code'*/

    /* If terminal symbol codes are not spared(in this case the member
       value is not NULL, we use translation vector instead of hash table. */
    YaepSymbol **symb_code_trans_vect;
    int symb_code_trans_vect_start;
    int symb_code_trans_vect_end;
};

/* A set of terminals represented as a bit array. */
struct YaepTerminalSet
{
    // Set identity.
    int id;

    // Number of long ints (terminal_bitset_t) used to store the bit array.
    int num_elements;

    // The bit array itself.
    terminal_bitset_t *set;
};

/* The following container for the abstract data.*/
struct YaepTerminalSetStorage
{
    /* All terminal sets are stored in the following os. */
    os_t terminal_bitset_os;

    /* Their values are number of terminal sets and their overall size. */
    int n_term_sets, n_term_sets_size;

    /* The YaepTerminalSet objects are stored in this vlo. */
    vlo_t terminal_bitset_vlo;

    /* Hashmap from key set (a bit array) to the YaepTerminalSet object from which we use the id. */
    hash_table_t map_terminal_bitset_to_id;
};

/* This page contains table for fast search for vector of indexes of
   dotted_rules with symbol after dot in given set core. */
struct YaepVect
{
    /* The following member is used internally.  The value is
       nonnegative for core_symb_to_predcomps being formed.  It is index of vlo
       in vlos array which contains the vector elements. */
    int intern;

    /* The following memebers defines array of ids of dotted_rules in a state set core. */
    int len;
    int *ids;
};

struct YaepCoreSymbToPredComps
{
    /* Unique incrementing id. Not strictly necessary but useful for debugging. */
    int id;

    /* The set core. */
    YaepStateSetCore *core;

    /* The symbol. */
    YaepSymbol *symb;

    /* The following vector contains ids of dotted_rules with given symb in dotted_rule after dot.
       We use this to predict the next set of dotted rules to add after symb has been reach in state core. */
    YaepVect predictions;

    /* The following vector contains id of completed dotted_rule with given symb in lhs. */
    YaepVect completions;
};

/* A StateSetCore is a state set in Earley's algorithm but without matched lengths for the dotted rules.
   The state set cores can be reused between state sets and thus save memory. */
struct YaepStateSetCore
{
    /* The following is unique number of the set core. It is defined only after forming all set. */
    int id;

    /* The state set core hash.  We save it as it is used several times. */
    unsigned int hash;

    /* The following is term shifting which resulted into this core.  It
       is defined only after forming all set. */
    YaepSymbol *term;

    /* The variable num_dotted_rules are all dotted_rules in the set. Both starting and predicted. */
    int num_dotted_rules;
    int num_started_dotted_rules;
    // num_not_yet_started_dotted_rules== num_dotted_rules-num_started_dotted_rules

    /* Array of dotted_rules.  Started dotted_rules are always placed the
       first in the order of their creation(with subsequent duplicates
       are removed), then not_yet_started noninitial(dotted_rule with at least
       one symbol before the dot) dotted_rules are placed and then initial
       dotted_rules are placed.  You should access to a set dotted_rule only
       through this member or variable `new_dotted_rules' (in other words don't
       save the member value in another variable). */
    YaepDottedRule **dotted_rules;

    /* The following member is number of started dotted_rules and not-yet-started
       (noninitial) dotted_rules whose matched_length is defined from a start
       dotted_rule matched_length.  All not-yet-started initial dotted_rules have zero
       matched_lengths.  This matched_lengths are not stored. */
    int num_all_matched_lengths;

    /* The following is an array containing the parent rule index from
       which matched_length of dotted_rule with given index (between n_start_dotted_rules -> num_all_matched_lengths) is taken. */
    int *to_parent_rule_index;
};

/* A YaepStateSet (aka parse list) stores chart entries (aka items) [from, to, S →  VP 🞄 NP ]
   Scanning an input token triggers the creation of a state set. If we have n input tokens,
   then we have n+2  state sets (we add the final eof token and a final state after eof
   has been scanned.) */
struct YaepStateSet
{
    /* The following is unique number of the state set. */
    int id;

    /* The following is set core of the set.  You should access to set
       core only through this member or variable `new_core'(in other
       words don't save the member value in another variable). */
    YaepStateSetCore *core;

    /* Hash of the array of matched_lengths. We save it as it is used several times. */
    unsigned int matched_lengths_hash;

    /* The following is matched_lengths only for started dotted_rules.  Not-yet-started
       dotted_rules have their matched_lengths set to 0 implicitly.  A started dotted_rule
       in the set core and its corresponding matched_length matched_length have the same index.
       You should access matched_lengths only through this variable or the variable
       new_matched_lengths, the location of the arrays can move. */
    int *matched_lengths;
};

/* A dotted_rule stores:
       reference to a rule,
       current dot position in the rule,
       lookup bitset for a quick terminal lookahead check, to see if this dotted_rule should be applied.

   This is stored separately and this means that we can reuse dotted_rule objects to save memory,
   since rule,dot,lookup is recurring often. */
struct YaepDottedRule
{
    /* Unique dotted_rule identifier. Starts at 0 and increments for each new dotted_rule. */
    int id;

    /* The following is the rule being dotted. */
    YaepRule *rule;

    /* The following is position of dot in rhs of the dotted rule.
       Starts at 0 (left of all rhs terms) and ends at rhs.len (right of all rhs terms). */
    short dot_j;

    /* The following member is true if the tail can derive empty string. */
    bool empty_tail_p;

    /* The following is number of dotted_rule dyn_lookahead_context which is number of
       the corresponding terminal set in the table.  It is really used
       only for dynamic lookahead. */
    int dyn_lookahead_context;

    /* The following member is the dotted_rule lookahead it is equal to
       FIRST(the dotted_rule tail || FOLLOW(lhs)) for statik lookaheads
       and FIRST(the dotted_rule tail || dyn_lookahead_context) for dynamic ones. */
    terminal_bitset_t *lookahead;
};

struct YaepInputToken
{
    /* A symbol has a name "BEGIN",code 17, or for ixml "A",code 65. */
    YaepSymbol *symb;

    /* The token can have a provided attribute attached. This does not affect
       the parse, but can be extracted from the final parse tree. */
    void *attr;
};

/* The set-term-lookahead maps to goto sets. */
struct YaepStateSetTermLookAhead
{
    /* Keys */
    YaepStateSet *set;
    YaepSymbol *term;
    int lookahead_term; // The next terminal to be scanned.

    /* Saved goto sets form a queue.  The last goto is saved at the
       following array elements whose index is given by curr.
       When curr reaches MAX_CACHED_GOTO_RESULTS it loops back to 0. */
    int curr;

    /* Saved goto sets to which we can go from SET by the terminal with
       subsequent terminal LOOKAHEAD given by its code. */
    YaepStateSet *result[MAX_CACHED_GOTO_RESULTS];

    /* Corresponding places of the goto sets in the parsing list. */
    int place[MAX_CACHED_GOTO_RESULTS];
};

struct YaepRule
{
    /* The following is order number of rule. */
    int num;

    /* The following is length of rhs. */
    int rhs_len;

    /* The following is the next grammar rule. */
    YaepRule *next;

    /* The following is the next grammar rule with the same nonterminal
       in lhs of the rule. */
    YaepRule *lhs_next;

    /* The following is nonterminal in the left hand side of the rule. */
    YaepSymbol *lhs;

    /* The ixml default mark of the rule. -@^ */
    char mark;

    /* The following is symbols in the right hand side of the rule. */
    YaepSymbol **rhs;

    /* The ixml marks for all the terms in the right hand side of the rule. */
    char *marks;
    /* The following three members define rule translation. */

    const char *anode;             /* abstract node name if any. */
    int anode_cost;                /* the cost of the abstract node if any, otherwise 0. */
    int trans_len;                 /* number of symbol translations in the rule translation. */

    /* The following array elements correspond to element of rhs with
       the same index.  The element value is order number of the
       corresponding symbol translation in the rule translation.  If the
       symbol translation is rejected, the corresponding element value is
       negative. */
    int *order;

    /* The following member value is equal to size of all previous rule
       lengths + number of the previous rules.  Imagine that all left
       hand symbol and right hand size symbols of the rules are stored
       in array.  Then the following member is index of the rule lhs in
       the array. */
    int rule_start_offset;

    /* The following is the same string as anode but memory allocated in parse_alloc. */
    char *caller_anode;

    /* True if the rule contains any not operator. */
    bool contains_not_operator;
};

/* The following container for the abstract data.*/
struct YaepRuleStorage
{
    /* The following is number of all rules and their summary rhs length. */
    int num_rules, n_rhs_lens;

    /* The following is the first rule. */
    YaepRule *first_rule;

    /* The following is rule being formed. */
    YaepRule *current_rule;

    /* All rules are placed in the following object. */
    os_t rules_os;
};

/* This state is used when reconstricting the parse tree from the dotted_rules. */
struct YaepParseTreeBuildState
{
    /* The rule which we are processing. */
    YaepRule *rule;

    /* The source dotted rule id. */
    YaepDottedRule *dotted_rule;

    /* Current position in rule->rhs[]. */
    int dot_j;

    /* An index into input[] and is the starting point of the matched tokens for the rule. */
    int from_i;

    /* The current state set index into YaepParseState->state_sets. */
    int state_set_k;

    /* If the following value is NULL, then we do not need to create
       translation for this rule.  If we should create abstract node
       for this rule, the value refers for the abstract node and the
       parent_rhs_offset is undefined.  Otherwise, the two members is
       place into which we should place the translation of the rule.
       The following member is used only for states in the stack. */
    YaepParseTreeBuildState *parent_anode_state;

    /* The parent anode index into input[] */
    int parent_rhs_offset;

    /* The following is used only for states in the table. */
    YaepTreeNode *anode;
};

/* To make better traversing and don't waist tree parse memory,
   we use the following structures to enumerate the tree node. */
struct YaepTreeNodeVisit
{
    /* The following member is order number of the node.  This value is
       negative if we did not visit the node yet. */
    int num;

    /* The tree node itself. */
    YaepTreeNode*node;
};

/* The following strucrture describes an error recovery state(an
   error recovery alternative.*/
struct YaepRecoveryState
{
    /* The following three members define start state set used to given error
       recovery state(alternative). */
    /* The following members define what part of original(at the error
       recovery start) state set will be head of error recovery state.  The
       head will be all states from original state set with indexes in range
       [0, last_original_state_set_el]. */
    int last_original_state_set_el;
    /* The following two members define tail of state set for this error recovery state. */
    int state_set_tail_length;
    YaepStateSet **state_set_tail;
    /* The following member is index of start token for given error recovery state. */
    int start_tok;
    /* The following member value is number of tokens already ignored in
       order to achieved given error recovery state. */
    int backward_move_cost;
};

struct YaepParseState
{
    YaepParseRun run;
    int magic_cookie; // Must be set to 736268273 when the state is created.

    /* The input token array to be parsed. */
    YaepInputToken *input;
    int input_len;
    vlo_t input_vlo;

    /* When parsing, the current input token is incremented from 0 to len. */
    int tok_i;

    /* The following says that variables new_set and new_core are defined
       including their members. Before new set is ready, then the access to
       data of the set being formed is only possible through the new_dotted_rules,
       new_matched_lenghts and new_num_dotted_rules variables. */
    int new_set_ready_p;

    /* The following variable is set being created.
       This variable is defined only when new_set_ready_p is true. */
    YaepStateSet *new_set;

   /* The following variable is always set core of set being created.
      Member core of new_set has always the following value.
      This variable is defined only when new_set_ready_p is true. */
    YaepStateSetCore *new_core;

   /* To optimize code we use the following variables to access to data
      of new set. I.e. the point into the new set if new_set_read_p.
      Theses variables are always defined and correspondingly
      dotted_rules, matched_lengths,
      and the current number of started dotted_rules
      of the set being formed. */
    YaepDottedRule **new_dotted_rules;
    int *new_matched_lengths;
    int new_num_leading_dotted_rules;

    /* The following are number of unique set cores and their start
       dotted_rules, unique matched_length vectors and their summary length, and
       number of parent indexes. */
    int num_set_cores, num_set_core_start_dotted_rules;
    int num_set_matched_lengths, num_set_matched_lengths_len;
    int num_parent_dotted_rule_ids;

    /* Number of state sets and their number of dotted_rules. */
    int num_sets_total, num_dotted_rules_total;

    /* Number of lookaheads (set, term, lookahead). */
    int num_set_term_lookahead;

    /* The set cores of formed sets are placed in the following os.*/
    os_t set_cores_os;

    /* The dotted_rules of formed sets are placed in the following os.*/
    os_t set_dotted_rules_os;

    /* The indexes of the parent start dotted_rules whose matched_lengths are used
       to get matched_lengths of some not_yet_started dotted_rules are placed in the
       following os.*/
    os_t set_parent_dotted_rule_ids_os;

    /* The matched_lengths of formed sets are placed in the following os.*/
    os_t set_matched_lengths_os;

    /* The sets themself are placed in the following os.*/
    os_t sets_os;

    /* Container for lookaheads (set, core, term, lookahead. */
    os_t set_term_lookahead_os;

    hash_table_t cache_stateset_cores;                /* key is the started dotted rules from a stateset. */
    hash_table_t cache_stateset_matched_lengths;      /* key is matched_lengths from a stateset. */
    hash_table_t cache_stateset_core_matched_lengths; /* key is (core, matched_lengths). */
    hash_table_t cache_stateset_term_lookahead;       /* key is (set, term, lookeahed). */

    /* The following contains current number of unique dotted_rules. */
    int num_all_dotted_rules;

    /* The following two dimensional array(the first dimension is dyn_lookahead_context
       number, the second one is dotted_rule number) contains references to
       all possible dotted_rules.*/
    YaepDottedRule ***dotted_rules_table;

    /* The following vlo is indexed by dotted_rule dyn_lookahead_context number and gives
       array which is indexed by dotted_rule number
      (dotted_rule->rule->rule_start_offset + dotted_rule->dot_j).*/
    vlo_t dotted_rules_table_vlo;

    /* All dotted_rules are placed in the following object.*/
    os_t dotted_rules_os;

    /* The set of pairs (dotted_rule,matched_length) used for test-setting such pairs
       is implemented using a vec[dotted_rule id] -> vec[matched_length] -> generation since id
       is unique and incrementing, we use a vector[max_id] to find another vector[max_matched_length]
       each matched_length entry storing a generation number. To clear the set of pairs
       we only need to increment the current generation below. Yay! No need to free, alloc, memset.*/
    vlo_t dotted_rule_matched_length_vec_vlo;

    /* The value used to check the validity of elements of check_dist structures. */
    int dotted_rule_matched_length_vec_generation;

    /* The following are number of unique(set core, symbol) pairs and
       their summary(transitive) prediction and completed vectors length,
       unique(transitive) prediction vectors and their summary length,
       and unique completed vectors and their summary length. */
    int n_core_symb_pairs, n_core_symb_to_predcomps_len;
    int n_transition_vects, n_transition_vect_len;
    int n_reduce_vects, n_reduce_vect_len;

    /* All triples(set core, symbol, vect) are placed in the following object. */
    os_t core_symb_to_predcomps_os;

    /* Pointers to triples(set core, symbol, vect) being formed are
       placed in the following object. */
    vlo_t new_core_symb_to_predcomps_vlo;

    /* All elements of vectors (transitive_)predictions and completions, are placed in the following os. */
    os_t vect_ids_os;

#ifdef USE_CORE_SYMB_HASH_TABLE
    hash_table_t map_core_symb_to_predcomps;        /* key is core and symb.*/
#else
    /* The following two variables contains table(set core,
       symbol)->core_symb_to_predcomps implemented as two dimensional array.*/
    /* The following object contains pointers to the table rows for each
       set core.*/
    vlo_t core_symb_table_vlo;

    /* The following is always start of the previous object.*/
    YaepCoreSymbToPredComps ***core_symb_table;

    /* The following contains rows of the table.  The element in the rows
       are indexed by symbol number.*/
    os_t core_symb_tab_rows;
#endif

    /* The following tables contains references for core_symb_to_predcomps which
       (through(transitive) predictions and completions correspondingly)
       refers for elements which are in the tables.  Sequence elements are
       stored in one exemplar to save memory.*/
    hash_table_t map_transition_to_coresymbvect;        /* key is elements.*/
    hash_table_t map_reduce_to_coresymbvect;        /* key is elements.*/

    /* Store state sets in a growing array. Even though early parser
       specifies a new state set per token, we can reuse a state set if
       the matched lengths are the same. This means that the
       state_set_k can increment fewer times than tok_i. */
    YaepStateSet **state_sets;
    int state_set_k;

    /* The following is number of created terminal, abstract, and
       alternative nodes. */
    int n_parse_term_nodes, n_parse_abstract_nodes, n_parse_alt_nodes;

    /* All tail sets of error recovery are saved in the following os. */
    os_t recovery_state_tail_sets;

    /* The following variable values is state_set_k and tok_i at error
       recovery start(when the original syntax error has been fixed). */
    int recovery_start_set_k, recovery_start_tok_i;

    /* The following variable value means that all error sets in pl with
       indexes [back_state_set_frontier, recovery_start_set_k] are being processed or
       have been processed. */
    int back_state_set_frontier;

    /* The following variable stores original state set tail in reversed order.
       This object only grows.  The last object sets may be used to
       restore original state set in order to try another error recovery state
       (alternative). */
    vlo_t original_state_set_tail_stack;

    /* The following variable value is last state set element which is original
       set(set before the error_recovery start). */
    int original_last_state_set_el;

    /* This page contains code for work with array of vlos.  It is used
       only to implement abstract data `core_symb_to_predcomps'. */

    /* All vlos being formed are placed in the following object. */
    vlo_t vlo_array;

    /* The following is current number of elements in vlo_array. */
    int vlo_array_len;

    /* The following table is used to find allocated memory which should not be freed. */
    hash_table_t set_of_reserved_memory; // (key is memory pointer)

    /* The following vlo will contain references to memory which should be
       freed.  The same reference can be represented more on time. */
    vlo_t tnodes_vlo;

    /* The key of the following table is node itself. */
    hash_table_t map_node_to_visit;

    /* All translation visit nodes are placed in the following stack.  All
       the nodes are in the table.*/
    os_t node_visits_os;

    /* The following value is number of translation visit nodes. */
    int num_nodes_visits;

    /* How many times we reuse Earley's sets without their recalculation. */
    int n_goto_successes;

    /* The following vlo is error recovery states stack.  The stack
       contains error recovery state which should be investigated to find
       the best error recovery. */
    vlo_t recovery_state_stack;

    /* The following os contains all allocated parser states. */
    os_t parse_state_os;

    /* The following variable refers to head of chain of already allocated
       and then freed parser states. */
    YaepParseTreeBuildState *free_parse_state;

    /* The following table is used to make translation for ambiguous
       grammar more compact.  It is used only when we want all
       translations. */
    hash_table_t map_rule_orig_statesetind_to_internalstate;        /* Key is rule, origin, state_set_k.*/

    int core_symb_to_pred_comps_counter;

    /* Jump here when error. */
    jmp_buf error_longjump_buff;
};
typedef struct YaepParseState YaepParseState;

#define CHECK_PARSE_STATE_MAGIC(ps) (ps->magic_cookie == 736268273)
#define INSTALL_PARSE_STATE_MAGIC(ps) ps->magic_cookie=736268273

struct StateVars;
typedef struct StateVars StateVars;

struct StateVars
{
    int state_id;
    int core_id;
    int num_started_dotted_rules;
    int num_dotted_rules;
    int num_all_matched_lengths;
    YaepDottedRule **dotted_rules;
    int *matched_lengths;
    int *to_parent_rule_index;
};

// PART H YAEP_CSPC ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include "yaep_structs.h"

#endif

YaepCoreSymbToPredComps *core_symb_to_predcomps_new(YaepParseState *ps, YaepStateSetCore*core, YaepSymbol*symb);
void core_symb_to_predcomps_init(YaepParseState *ps);
YaepCoreSymbToPredComps *core_symb_to_predcomps_find(YaepParseState *ps, YaepStateSetCore *core, YaepSymbol *symb);
void free_core_symb_to_vect_lookup(YaepParseState *ps);
void core_symb_to_predcomps_add_predict(YaepParseState *ps,
                                        YaepCoreSymbToPredComps *core_symb_to_predcomps,
                                        int rule_index_in_core);
void core_symb_to_predcomps_add_complete(YaepParseState *ps,
                                         YaepCoreSymbToPredComps *core_symb_to_predcomps,
                                         int rule_index_in_core);
void core_symb_to_predcomps_new_all_stop(YaepParseState *ps);

#define YAEP_CSPC_MODULE

// PART H YAEP_UTIL ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include <stdio.h>

#endif

// Debug functions available to gdb.

void dbg_breakpoint();

void dbg_print_core(YaepParseState *ps, YaepStateSetCore *c);
void dbg_print_coresymbvects(YaepParseState *ps, YaepCoreSymbToPredComps *v);
void dbg_print_dotted_rule(YaepParseState *ps, YaepDottedRule *dotted_rule);
void fetch_state_vars(YaepParseState *ps, YaepStateSet *state_set, StateVars *out);
int find_matched_length(YaepParseState *ps, YaepStateSet *state_set, StateVars *vars, int dotted_rule_id);

void yaep_view(YaepParseState *ps, const char *format, ...);
void yaep_debug(YaepParseState *ps, const char *format, ...);
void yaep_trace(YaepParseState *ps, const char *format, ...);

#define YAEP_UTIL_MODULE

// PART H YAEP_SYMBOLS ////////////////////////////////////////

unsigned symb_repr_hash(hash_table_entry_t s);
bool symb_repr_eq(hash_table_entry_t s1, hash_table_entry_t s2);
unsigned symb_code_hash(hash_table_entry_t s);
bool symb_code_eq(hash_table_entry_t s1, hash_table_entry_t s2);
YaepSymbolStorage *symbolstorage_create(YaepGrammar *grammar);

/* Return symbol(or NULL if it does not exist) whose representation is REPR.*/
YaepSymbol *symb_find_by_repr(YaepParseState *ps, const char*repr);

/* Return symbol(or NULL if it does not exist) which is terminal with CODE. */
YaepSymbol *symb_find_by_code(YaepParseState *ps, int code);

YaepSymbol *symb_find_by_term_id(YaepParseState *ps, int term_id);

/* The function creates new terminal symbol and returns reference for
   it.  The symbol should be not in the tables.  The function should
   create own copy of name for the new symbol. */
YaepSymbol *symb_add_terminal(YaepParseState *ps, const char*name, int code);

/* The function creates new nonterminal symbol and returns reference
   for it.  The symbol should be not in the table. The function
   should create own copy of name for the new symbol. */
YaepSymbol *symb_add_nonterm(YaepParseState *ps, const char *name);

/* The following function return N-th symbol(if any) or NULL otherwise. */
YaepSymbol *symb_get(YaepParseState *ps, int id);

/* The following function return N-th symbol(if any) or NULL otherwise. */
YaepSymbol *term_get(YaepParseState *ps, int n);

/* The following function return N-th symbol(if any) or NULL otherwise. */
YaepSymbol *nonterm_get(YaepParseState *ps, int n);

void symb_finish_adding_terms(YaepParseState *ps);

/* Free memory for symbols. */
void symb_empty(YaepParseState *ps, YaepSymbolStorage *symbs);

void symbolstorage_free(YaepParseState *ps, YaepSymbolStorage *symbs);


#define YAEP_SYMBOLS_MODULE

// PART H YAEP_TERMINAL_BITSET ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include "yaep.h"
#include "yaep_allocate.h"
#include "yaep_hashtab.h"
#include "yaep_structs.h"

#endif

unsigned terminal_bitset_hash(hash_table_entry_t s);
bool terminal_bitset_eq(hash_table_entry_t s1, hash_table_entry_t s2);
YaepTerminalSetStorage *termsetstorage_create(YaepGrammar *grammar);
terminal_bitset_t *terminal_bitset_create(YaepParseState *ps);
void terminal_bitset_clear(YaepParseState *ps, terminal_bitset_t* set);
void terminal_bitset_fill(YaepParseState *ps, terminal_bitset_t* set);
void terminal_bitset_copy(YaepParseState *ps, terminal_bitset_t *dest, terminal_bitset_t *src);
/* Add all terminals from set OP with to SET.  Return true if SET has been changed.*/
bool terminal_bitset_or(YaepParseState *ps, terminal_bitset_t *set, terminal_bitset_t *op);
/* Add terminal with number NUM to SET.  Return true if SET has been changed.*/
bool terminal_bitset_up(YaepParseState *ps, terminal_bitset_t *set, int num);
/* Remove terminal with number NUM from SET.  Return true if SET has been changed.*/
bool terminal_bitset_down(YaepParseState *ps, terminal_bitset_t *set, int num);
/* Return true if terminal with number NUM is in SET. */
int terminal_bitset_test(YaepParseState *ps, terminal_bitset_t *set, int num);
/* The following function inserts terminal SET into the table and
   returns its number.  If the set is already in table it returns -its
   number - 1(which is always negative).  Don't set after
   insertion!!! */
int terminal_bitset_insert(YaepParseState *ps, terminal_bitset_t *set);
/* The following function returns set which is in the table with number NUM. */
terminal_bitset_t *terminal_bitset_from_table(YaepParseState *ps, int num);
/* Print terminal SET into file F. */
void terminal_bitset_print(MemBuffer *mb, YaepParseState *ps, terminal_bitset_t *set);
/* Free memory for terminal sets. */
void terminal_bitset_empty(YaepTerminalSetStorage *term_sets);
void termsetstorage_free(YaepGrammar *grammar, YaepTerminalSetStorage *term_sets);

#define YAEP_TERMINAL_BITSET_MODULE

// PART H YAEP_TREE ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include "yaep_structs.h"

#endif

/* Build the parse tree from the yaep parse. */
YaepTreeNode *build_parse_tree(YaepParseState *ps, bool *ambiguous_p);

/* Hash of translation visit node.*/
unsigned trans_visit_node_hash(hash_table_entry_t n);

/* Equality of translation visit nodes.*/
bool trans_visit_node_eq(hash_table_entry_t n1, hash_table_entry_t n2);

/* The following function returns the positive order number of node with number NUM.*/
int canon_node_id(int id);

/* The following function checks presence translation visit node with
   given NODE in the table and if it is not present in the table, the
   function creates the translation visit node and inserts it into
   the table.*/
YaepTreeNodeVisit *visit_node(YaepParseState *ps, YaepTreeNode*node);

#define YAEP_TREE_MODULE

// PART H YAEP_PRINT ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include <stdio.h>
#include "yaep_structs.h"

#endif

void print_core(MemBuffer*mb, YaepStateSetCore *c);
void print_coresymbvects(MemBuffer*mb, YaepParseState *ps, YaepCoreSymbToPredComps *v);
void print_dotted_rule(MemBuffer *mb, YaepParseState *ps, int from_i, YaepDottedRule *dotted_rule, int matched_length, int parent_id);
void print_dotted_rule_blocked(MemBuffer *mb, YaepParseState *ps, int from_i, YaepDottedRule *dotted_rule, int matched_length, int parent_id);
void print_matched_lenghts(MemBuffer *mb, YaepStateSet *s);
void print_parse(YaepParseState *ps, FILE* f, YaepTreeNode*root);
void print_rule(MemBuffer *mb, YaepParseState *ps, YaepRule *rule);
void print_rule_with_dot(MemBuffer *mb, YaepParseState *ps, YaepRule *rule, int pos);
void print_state_set(MemBuffer *mb, YaepParseState *ps, YaepStateSet *set, int from_i);
void print_symbol(MemBuffer *mb, YaepSymbol *symb, bool code_p);
void print_terminal_bitset(MemBuffer *mb, YaepParseState *ps, terminal_bitset_t *set);
void print_yaep_node(YaepParseState *ps, FILE *f, YaepTreeNode *node);
void rule_print(MemBuffer *mb, YaepParseState *ps, YaepRule *rule, bool trans_p);

#define YAEP_PRINT_MODULE


// XMQ STRUCTURES ////////////////////////////////////////////////

// PART H XMQ_INTERNALS ////////////////////////////////////////

#include<assert.h>
#include<ctype.h>
#include<errno.h>
#include<math.h>
#include<setjmp.h>
#include<stdarg.h>
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>

#include"xmq.h"
#include<libxml/tree.h>
#include<libxml/parser.h>
#include<libxml/HTMLparser.h>
#include<libxml/HTMLtree.h>
#include<libxml/xmlreader.h>
#include<libxml/xpath.h>
#include<libxml/xpathInternals.h>

#ifndef BUILDING_DIST_XMQ
#include"colors.h"
#endif

// STACK /////////////////////

struct Stack;
typedef struct Stack Stack;

struct Vector;
typedef struct Vector Vector;

struct HashMap;
typedef struct HashMap HashMap;

struct HashMapIterator;
typedef struct HashMapIterator HashMapIterator;

struct MemBuffer;
typedef struct MemBuffer MemBuffer;

struct YaepParseRun;
typedef struct YaepParseRun YaepParseRun;

struct YaepGrammar;
typedef struct YaepGrammar YaepGrammar;

struct XMQParseState;
typedef struct XMQParseState XMQParseState;

// DECLARATIONS /////////////////////////////////////////////////

#define LIST_OF_XMQ_TOKENS  \
    X(whitespace)           \
    X(equals)               \
    X(brace_left)           \
    X(brace_right)          \
    X(apar_left)            \
    X(apar_right)           \
    X(cpar_left)            \
    X(cpar_right)           \
    X(quote)                \
    X(entity)               \
    X(comment)              \
    X(comment_continuation) \
    X(element_ns)           \
    X(element_name)         \
    X(element_key)          \
    X(element_value_text)   \
    X(element_value_quote)  \
    X(element_value_entity) \
    X(element_value_compound_quote)  \
    X(element_value_compound_entity) \
    X(attr_ns)             \
    X(attr_key)            \
    X(attr_value_text)     \
    X(attr_value_quote)    \
    X(attr_value_entity)   \
    X(attr_value_compound_quote)    \
    X(attr_value_compound_entity)   \
    X(ns_declaration) \
    X(ns_colon)  \

#define MAGIC_COOKIE 7287528

struct XMQNode
{
    xmlNodePtr node;
};

struct XMQDoc
{
    union {
        xmlDocPtr xml;
        htmlDocPtr html;
    } docptr_; // Is both xmlDocPtr and htmlDocPtr.
    const char *source_name_; // File name or url from which the documented was fetched.
    int errno_; // A parse error is assigned a number.
    const char *error_; // And the error is explained here.
    XMQNode root_; // The root node.
    XMQContentType original_content_type_; // Remember if this document was created from xmq/xml etc.
    size_t original_size_; // Remember the original source size of the document it was loaded from.

    // For now these references are here. I am not quite sure where to put them in the future.
    // The IXML grammar can be parsed, but not actually turned into a yaep grammar until
    // we have seen the content to be parsed, since we want to shrink the charset membership tests to a minimum.
    // I.e. if the content only contains a:s and b:s (eg 'abbabbabaaab') then the charset test
    // ['a'-'z'] shrinks down to ['a';'b']
    YaepParseRun *yaep_parse_run_; // The currently executing parse variables.
    YaepGrammar *yaep_grammar_; // The yaep grammar to be used by the run.
    XMQParseState *yaep_parse_state_; // The parse state used to parse the ixml grammar.
};

#ifdef __cplusplus
enum Level : short {
#else
enum Level {
#endif
    LEVEL_XMQ = 0,
    LEVEL_ELEMENT_VALUE = 1,
    LEVEL_ELEMENT_VALUE_COMPOUND = 2,
    LEVEL_ATTR_VALUE = 3,
    LEVEL_ATTR_VALUE_COMPOUND = 4
};
typedef enum Level Level;

/**
    XMQOutputSettings:
    @add_indent: Default is 4. Indentation starts at 0 which means no spaces prepended.
    @compact: Print on a single line limiting whitespace to a minimum.
    @escape_newlines: Replace newlines with &#10; this is implied if compact is set.
    @escape_non_7bit: Replace all chars above 126 with char entities, ie &#10;
    @escape_tabs: Replace tabs with &#9;
    @allow_json_quotes: Allow quoting with "..." for content with no newlines but single quotes.
    @always_json_quotes: Always quote using "..." when quoting is needed.
    @output_format: Print xmq/xml/html/json
    @render_to: Render to terminal, html, tex.
    @render_raw: If true do not write surrounding html and css colors, likewise for tex.
    @only_style: Print only style sheet header.
    @write_content: Write content to buffer.
    @buffer_content: Supplied as buffer above.
    @write_error: Write error to buffer.
    @buffer_error: Supplied as buffer above.
    @colorings: Map from namespace (default is the empty string) to  prefixes/postfixes to colorize the output for ANSI/HTML/TEX.
*/
struct XMQOutputSettings
{
    int  add_indent;
    bool compact;
    bool omit_decl;
    bool use_color;
    bool bg_dark_mode;
    bool prefer_double_quotes;
    bool escape_newlines;
    bool escape_non_7bit;
    bool escape_tabs;
    bool allow_json_quotes;
    bool always_json_quotes;

    XMQContentType output_format;
    XMQRenderFormat render_to;
    bool render_raw;
    bool only_style;
    const char *render_theme;

    XMQWriter content;
    XMQWriter error;

    // If printing to memory:
    MemBuffer *output_buffer;
    char **output_buffer_start;
    char **output_buffer_stop;
    size_t *output_skip;

    const char *indentation_space; // If NULL use " " can be replaced with any other string.
    const char *explicit_space; // If NULL use " " can be replaced with any other string.
    const char *explicit_tab; // If NULL use "\t" can be replaced with any other string.
    const char *explicit_cr; // If NULL use "\t" can be replaced with any other string.
    const char *explicit_nl; // If NULL use "\n" can be replaced with any other string.
    const char *prefix_line; // If non-NULL print this as the leader before each line.
    const char *postfix_line; // If non-NULL print this as the ending after each line.

    const char *use_id; // If non-NULL inser this id in the pre tag.
    const char *use_class; // If non-NULL insert this class in the pre tag.

    XMQTheme *theme; // The theme used to print.
    void *free_me;
    void *free_and_me;
};
typedef struct XMQOutputSettings XMQOutputSettings;

enum IXMLTermType
{
    IXML_TERMINAL, IXML_NON_TERMINAL, IXML_CHARSET
};
typedef enum IXMLTermType IXMLTermType;

struct IXMLCharsetPart;
typedef struct IXMLCharsetPart IXMLCharsetPart;
struct IXMLCharsetPart
{
    IXMLCharsetPart *next;
    // A charset range [ 'a' - 'z' ]
    // A single char is [ 'a' - 'a' ] used for each element in a string set definition, eg 'abc'
    int from, to;
    // A charset category [ Lc ] for lower case character or [Zs] for whitespace,
    // stored as two characters and the null terminatr.
    char category[3];
};

struct IXMLCharset
{
    // Negate the check, exclude the specified characters.
    bool exclude;
    // Charset contents.
    IXMLCharsetPart *first;
    IXMLCharsetPart *last;
};
typedef struct IXMLCharset IXMLCharset;

struct IXMLTerminal
{
    char *name;
    int code;
};
typedef struct IXMLTerminal IXMLTerminal;

struct IXMLNonTerminal
{
    char *name;
    char *alias;
    IXMLCharset *charset; // If the rule embodies a charset.
};
typedef struct IXMLNonTerminal IXMLNonTerminal;

struct IXMLTerm // A term is an element in the rhs vector in the rule.
{
    IXMLTermType type;
    char mark; // Exclude a term in a rule's rhs: rr: -A, B;  then only the contents of A will be printed.
    IXMLTerminal *t;
    IXMLNonTerminal *nt;
};
typedef struct IXMLTerm IXMLTerm;

struct IXMLRule
{
    IXMLNonTerminal *rule_name;
    char mark; // ^@-+
    int cost;  // 0, 1, 2 or more. I higher cost is avoided when ambiguity arises.
    bool expected_ambiguity; // Set to true means we want the multiple parses.
    Vector *rhs_terms;
};
typedef struct IXMLRule IXMLRule;

struct XMQParseState
{
    char *source_name; // Only used for generating any error messages.
    void *out;
    const char *buffer_start; // Points to first byte in buffer.
    const char *buffer_stop;   // Points to byte >after< last byte in buffer.
    const char *i; // Current parsing position.
    size_t line; // Current line.
    size_t col; // Current col.
    XMQParseError error_nr; // A standard parse error enum that maps to text.
    const char *error_info; // Additional info printed with the error nr.
    char *generated_error_msg; // Additional error information.
    MemBuffer *generating_error_msg;
    jmp_buf error_handler;

    bool simulated; // When true, this is generated from JSON parser to simulate an xmq element name.
    XMQParseCallbacks *parse;
    XMQDoc *doq;
    const char *implicit_root; // Assume that this is the first element name
    Stack *element_stack; // Top is last created node
    void *element_last; // Last added sibling to stack top node.
    bool parsing_doctype; // True when parsing a doctype.
    void *add_pre_node_before; // Used when retrofitting pre-root comments and doctype found in json.
    bool root_found; // Used to decide if _// should be printed before or after root.
    void *add_post_node_after; // Used when retrofitting post-root comments found in json.
    bool doctype_found; // True after a doctype has been parsed.
    bool parsing_pi; // True when parsing a processing instruction, pi.
    bool merge_text; // Merge text nodes and character entities.
    bool no_trim_quotes; // No trimming if quotes, used when reading json strings.
    bool ixml_all_parses; // If IXML parse is ambiguous then print all parses.
    bool ixml_try_to_recover; // If IXML parse fails, try to recover.
    const char *pi_name; // Name of the pi node just started.
    XMQOutputSettings *output_settings; // Used when coloring existing text using the tokenizer.
    int magic_cookie; // Used to check that the state has been properly initialized.

    char *element_namespace; // The element namespace is found before the element name. Remember the namespace name here.
    char *attribute_namespace; // The attribute namespace is found before the attribute key. Remember the namespace name here.
    bool declaring_xmlns; // Set to true when the xmlns declaration is found, the next attr value will be a href
    void *declaring_xmlns_namespace; // The namespace to be updated with attribute value, eg. xmlns=uri or xmlns:prefix=uri

    void *default_namespace; // If xmlns=http... has been set, then a pointer to the namespace object is stored here.

    // These are used for better error reporting.
    const char *last_body_start;
    size_t last_body_start_line;
    size_t last_body_start_col;
    const char *last_attr_start;
    size_t last_attr_start_line;
    size_t last_attr_start_col;
    const char *last_quote_start;
    size_t last_quote_start_line;
    size_t last_quote_start_col;
    const char *last_compound_start;
    size_t last_compound_start_line;
    size_t last_compound_start_col;
    const char *last_equals_start;
    size_t last_equals_start_line;
    size_t last_equals_start_col;
    const char *last_suspicios_quote_end;
    size_t last_suspicios_quote_end_line;
    size_t last_suspicios_quote_end_col;

    ///////// The following variables are used when parsing ixml. //////////////////////////////////
    // Collect all unique terminals in this map from the name. #78 and 'x' is the same terminal with code 120.
    // The name is a #hex version of unicode codepoint.
    HashMap *ixml_terminals_map;
    // Collect all non-terminal names here, map name to non-terminal.
    HashMap *ixml_non_terminals_map;
    // References to non-terminals, ie rules (includes mark -^@).
    Vector *ixml_non_terminals;
    // Store all rules here. The rule has a rule_name which is a non-terminal ref to itself.
    Vector *ixml_rules;
    // Rule stack is pushed by groups (..), ?, *, **, + and ++.
    Stack  *ixml_rule_stack;
    // Generated rule counter. Starts at 0 and increments after each generated
    // anonymous rule, ie (..), ? etc. The rules will have the names /0 /1 /2 etc.
    int num_generated_rules;
    // The ixml_rule is the current rule that we are building.
    IXMLRule *ixml_rule;
    // The ixml_nonterminal is the current rule reference.
    IXMLNonTerminal *ixml_nonterminal;
    // The ixml_tmp_terminals is the current collection of terminals, ie characters.
    // A single ixml #78 or 'x' or "x" adds a single terminal to this vector.
    // The string 'xy' adds two terminals to this vector, etc.
    // These terminals are then added to the rule's rhs.
    Vector *ixml_tmp_terminals;
    // The ixml_tmp_terms is the current collection of terms (terminal or non-terminals),
    // These terms are then added to the rule's rhs.
//    Vector *ixml_rhs_tmp_terms;
    // These are the marks @-^ for the terminals.
    Vector *ixml_rhs_tmp_marks;
    // Has the cost marker "=<" or ":<" or "=<<<" etc been used for a rule in this ixml grammar?
    bool ixml_costs_enabled;
    // Has a rule been marked with "*" which means we want all parses!
    bool ixml_controlled_ambiguity_enabled;
    // The most recently parsed mark.
    char ixml_mark;
    // The most recently parsed encoded value #41
    int ixml_encoded;
    // Any charset we are currently building.
    IXMLCharset *ixml_charset;
    // Parsing ranges of charsets.
    int ixml_charset_from;
    int ixml_charset_to;
    // When parsing the grammar, collect all charset categories that we use.
    HashMap *ixml_found_categories;

    // These are used when traversing the IXML parse tree and building the yaep grammar.
    char **yaep_tmp_rhs_;
    char *yaep_tmp_marks_;
    int *yaep_tmp_transl_;
    HashMapIterator *yaep_i_;
    size_t yaep_j_;

    // When debugging parsing of ixml, track indentation of depth here.
    int depth;
    // Generate xml as well.
    bool build_xml_of_ixml;

    // When fixing ixml and using --lines the already added unicode chars
    // are stored here between lines.
    char *used_unicodes;
};

/**
   XMQPrintState:
   @current_indent: The current_indent stores how far we have printed on the current line.
                    0 means nothing has been printed yet.
   @line_indent:  The line_indent stores the current indentation level from which print
                  starts for each new line. The line_indent is 0 when we start printing the xmq.
   @last_char: the last_char remembers the most recent printed character. or 0 if no char has been printed yet.
   @color_pre: The active color prefix.
   @prev_color_pre: The previous color prefix, used for restoring utf8 text after coloring unicode whitespace.
   @restart_line: after nl_and_indent print this to restart coloring of line.
   @ns: the last namespace reference.
   @output_settings: the output settings.
   @doc: The xmq document that is being printed.
*/
struct XMQPrintState
{
    size_t current_indent;
    size_t line_indent;
    int last_char;
    const char *replay_active_color_pre;
    const char *restart_line;
    const char *last_namespace;
    Stack *pre_nodes; // Used to remember leading comments/doctype when printing json.
    size_t pre_post_num_comments_total; // Number of comments outside of the root element.
    size_t pre_post_num_comments_used; // Active number of comment outside of the root element.
    Stack *post_nodes; // Used to remember ending comments when printing json.
    XMQOutputSettings *output_settings;
    XMQDoc *doq;
};
typedef struct XMQPrintState XMQPrintState;

/**
    XMQQuoteSettings:
    @force:           Always add single quotes. More quotes if necessary.
    @compact:         Generate compact quote on a single line. Using &#10; and no superfluous whitespace.
    @value_after_key: If enties are introduced by the quoting, then use compound ( ) around the content.
    @prefer_double_quotes: // By default xmq uses the single quote, but we can change this here.

    @indentation_space: Use this as the indentation character. If NULL default to " ".
    @explicit_space: Use this as the explicit space/indentation character. If NULL default to " ".
    @newline:     Use this as the newline character. If NULL default to "\n".
    @prefix_line: Prepend each line with this. If NULL default to empty string.
    @postfix_line Suffix each line whith this. If NULL default to empty string.
    @prefix_entity: Prepend each entity with this. If NULL default to empty string.
    @postfix_entity: Suffix each entity whith this. If NULL default to empty string.
    @prefix_doublep: Prepend each ( ) with this. If NULL default to empty string.
    @postfix_doublep: Suffix each ( ) whith this. If NULL default to empty string.
*/
struct XMQQuoteSettings
{
    bool force; // Always add single quotes. More quotes if necessary.
    bool compact; // Generate compact quote on a single line. Using &#10; and no superfluous whitespace.
    bool allow_json_quotes; // Permit "..." json quoting.
    bool value_after_key; // If enties are introduced by the quoting, then use compound ( ) around the content.
    bool prefer_double_quotes; // By default xmq uses the single quote, but we can change this here.

    const char *indentation_space;  // Use this as the indentation character. If NULL default to " ".
    const char *explicit_space;  // Use this as the explicit space character inside quotes. If NULL default to " ".
    const char *explicit_nl;      // Use this as the newline character. If NULL default to "\n".
    const char *explicit_tab;      // Use this as the tab character. If NULL default to "\t".
    const char *explicit_cr;      // Use this as the cr character. If NULL default to "\r".
    const char *prefix_line;  // Prepend each line with this. If NULL default to empty string.
    const char *postfix_line; // Append each line whith this, before any newline.
    const char *prefix_entity;  // Prepend each entity with this. If NULL default to empty string.
    const char *postfix_entity; // Append each entity whith this. If NULL default to empty string.
    const char *prefix_doublep;  // Prepend each ( ) with this. If NULL default to empty string.
    const char *postfix_doublep; // Append each ( ) whith this. If NULL default to empty string.
};
typedef struct XMQQuoteSettings XMQQuoteSettings;

void generate_state_error_message(XMQParseState *state, XMQParseError error_nr, const char *start, const char *stop);

// Common parser functions ///////////////////////////////////////

Level enter_compound_level(Level l);
XMQColor level_to_quote_color(Level l);
XMQColor level_to_entity_color(Level l);
void attr_strlen_name_prefix(xmlAttr *attr, const char **name, const char **prefix, size_t *total_u_len);
void element_strlen_name_prefix(xmlNode *attr, const char **name, const char **prefix, size_t *total_u_len);
void namespace_strlen_prefix(xmlNs *ns, const char **prefix, size_t *total_u_len);
const char *find_word_ignore_case(const char *start, const char *stop, const char *word);

void setup_terminal_coloring(XMQOutputSettings *os, XMQTheme *c, bool dark_mode, bool use_color, bool render_raw);
void setup_html_coloring(XMQOutputSettings *os, XMQTheme *c, bool dark_mode, bool use_color, bool render_raw);
void setup_tex_coloring(XMQOutputSettings *os, XMQTheme *c, bool dark_mode, bool use_color, bool render_raw);

// XMQ tokenizer functions ///////////////////////////////////////////////////////////

size_t count_xmq_quotes(const char *i, const char *stop);
void eat_xmq_quote(XMQParseState *state, const char **start, const char **stop);
size_t calculate_incidental_indent(const char *start, const char *stop);
char *xmq_trim_quote(const char *start, const char *stop, bool is_xmq, bool is_comment);
char *escape_xml_comment(const char *comment);
char *unescape_xml_comment(const char *comment);
void xmq_fixup_html_before_writeout(XMQDoc *doq);
void xmq_fixup_comments_after_readin(XMQDoc *doq);

char *xmq_comment(int indent,
                 const char *start,
                 const char *stop,
                 XMQQuoteSettings *settings);
char *xmq_un_comment(const char *start, const char *stop);
char *xmq_un_quote(const char *start, const char *stop, bool remove_qs, bool is_xmq);

// XML/HTML dom functions ///////////////////////////////////////////////////////////////

xmlDtdPtr parse_doctype_raw(XMQDoc *doq, const char *start, const char *stop);
void trim_node(xmlNode *node, int flags);
void trim_text_node(xmlNode *node, int flags);

// Output buffer functions ////////////////////////////////////////////////////////

const char *needs_escape(XMQRenderFormat f, const char *start, const char *stop);

const char *build_error_message(const char *fmt, ...);

struct YaepGrammar;
typedef struct YaepGrammar YaepGrammar;
struct YaepParseRun;
typedef struct YaepParseRun YaepParseRun;
struct yaep_tree_node;

bool xmq_parse_buffer_ixml(XMQDoc *ixml_grammar, const char *start, const char *stop, int flags);

typedef void (*XMQContentCallback)(XMQParseState *state,
                                   size_t start_line,
                                   size_t start_col,
                                   const char *start,
                                   const char *stop,
                                   const char *suffix);

struct XMQParseCallbacks
{
#define X(TYPE) \
    XMQContentCallback handle_##TYPE;
LIST_OF_XMQ_TOKENS
#undef X

    void (*init)(XMQParseState*);
    void (*done)(XMQParseState*);

    int magic_cookie;
};

struct XMQPrintCallbacks
{
    void (*writeIndent)(int n);
    void (*writeElementName)(char *start, char *stop);
    void (*writeElementContent)(char *start, char *stop);
};

#define DO_CALLBACK(TYPE, state, start_line, start_col, start, stop, suffix) \
    { if (state->parse->handle_##TYPE != NULL) state->parse->handle_##TYPE(state,start_line,start_col,start,stop,suffix); }

#define DO_CALLBACK_SIM(TYPE, state, start_line, start_col, start, stop, suffix) \
    { if (state->parse->handle_##TYPE != NULL) { state->simulated=true; state->parse->handle_##TYPE(state,start_line,start_col,start,stop,suffix); state->simulated=false; } }

bool debug_enabled();

void xmq_setup_parse_callbacks(XMQParseCallbacks *callbacks);
void xmq_set_yaep_grammar(XMQDoc *doc, YaepGrammar *g);
YaepGrammar *xmq_get_yaep_grammar(XMQDoc *doc);
YaepParseRun *xmq_get_yaep_parse_run(XMQDoc *doc);
XMQParseState *xmq_get_yaep_parse_state(XMQDoc *doc);

void set_node_namespace(XMQParseState *state, xmlNodePtr node, const char *node_name);

bool load_file(XMQDoc *doq, const char *file, size_t *out_fsize, const char **out_buffer);
bool load_stdin(XMQDoc *doq, size_t *out_fsize, const char **out_buffer);

// Multicolor terminals like gnome-term etc.

#define NOCOLOR      "\033[0m"

#define XMQ_INTERNALS_MODULE


// FUNCTIONALITY /////////////////////////////////////////////////

// PART H JSON ////////////////////////////////////////

#include"xmq.h"
#include<libxml/tree.h>

struct XMQPrintState;
typedef struct XMQPrintState XMQPrintState;

bool xmq_parse_buffer_json(XMQDoc *doq, const char *start, const char *stop, const char *implicit_root);
void xmq_fixup_json_before_writeout(XMQDoc *doq);
void json_print_object_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to);
void collect_leading_ending_comments_doctype(XMQPrintState *ps, xmlNodePtr *first, xmlNodePtr *last);
void json_print_array_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to);
bool xmq_tokenize_buffer_json(XMQParseState *state, const char *start, const char *stop);

#define JSON_MODULE

// PART H IXML ////////////////////////////////////////

#ifndef BUILDING_DIST_XMQ

#include"xmq.h"

#endif

struct YaepGrammar;
typedef struct YaepGrammar YaepGrammar;
struct YaepParseRun;
typedef struct YaepParseRun YaepParseRun;
struct yaep_tree_node;

struct IXMLRule;
typedef struct IXMLRule IXMLRule;
struct IXMLTerminal;
typedef struct IXMLTerminal IXMLTerminal;
struct IXMLNonTerminal;
typedef struct IXMLNonTerminal IXMLNonTerminal;

bool ixml_build_yaep_grammar(YaepParseRun *pr,
                             YaepGrammar *g,
                             XMQParseState *state,
                             const char *grammar_start,
                             const char *grammar_stop,
                             const char *content_start, // Needed to minimize charset rule sizes.
                             const char *content_stop);

IXMLTerminal *new_ixml_terminal();
IXMLNonTerminal *new_ixml_nonterminal();
void free_ixml_rule(IXMLRule *r);
void free_ixml_terminal(IXMLTerminal *t);
void free_ixml_nonterminal(IXMLNonTerminal *nt);
const char *ixml_to_yaep_read_terminal(YaepParseRun *pr,
                                       YaepGrammar *g,
                                       int *code);
const char *ixml_to_yaep_read_rule(YaepParseRun *pr,
                                   YaepGrammar *g,
                                   const char ***rhs,
                                   const char **abs_node,
                                   int *cost,
                                   int **transl,
                                   char *mark,
                                   char **marks);

void scan_content_fixup_charsets(XMQParseState *state, const char *start, const char *stop);

void ixml_print_grammar(XMQParseState *state);

#define IXML_MODULE


//////////////////////////////////////////////////////////////////////////////////

void add_nl(XMQParseState *state);
XMQProceed catch_single_content(XMQDoc *doc, XMQNode *node, void *user_data);
size_t calculate_buffer_size(const char *start, const char *stop, int indent, const char *pre_line, const char *post_line);
bool check_leading_space_nl(const char *start, const char *stop);
void copy_and_insert(MemBuffer *mb, const char *start, const char *stop, int num_prefix_spaces, const char *implicit_indentation, const char *explicit_space, const char *newline, const char *prefix_line, const char *postfix_line);
char *copy_lines(int num_prefix_spaces, const char *start, const char *stop, int num_quotes, bool use_dqs, bool add_nls, bool add_compound, const char *implicit_indentation, const char *explicit_space, const char *newline, const char *prefix_line, const char *postfix_line);
void copy_quote_settings_from_output_settings(XMQQuoteSettings *qs, XMQOutputSettings *os);
xmlNodePtr create_entity(XMQParseState *state, size_t l, size_t c, const char *cstart, const char *cstop, const char*stop, xmlNodePtr parent);
void create_node(XMQParseState *state, const char *start, const char *stop);
void update_namespace_href(XMQParseState *state, xmlNsPtr ns, const char *start, const char *stop);
xmlNodePtr create_quote(XMQParseState *state, size_t l, size_t col, const char *start, const char *stop, const char *suffix,  xmlNodePtr parent);
void debug_content_comment(XMQParseState *state, size_t line, size_t start_col, const char *start, const char *stop, const char *suffix);
void debug_content_value(XMQParseState *state, size_t line, size_t start_col, const char *start, const char *stop, const char *suffix);
void debug_content_quote(XMQParseState *state, size_t line, size_t start_col, const char *start, const char *stop, const char *suffix);
void do_attr_key(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_attr_ns(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_ns_declaration(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_attr_value_compound_entity(XMQParseState *state, size_t l, size_t c, const char *cstart, const char *cstop, const char*stop);
void do_attr_value_compound_quote(XMQParseState *state, size_t l, size_t c, const char *cstart, const char *cstop, const char*stop);
void do_attr_value_entity(XMQParseState *state, size_t l, size_t c, const char *cstart, const char *cstop, const char*stop);
void do_attr_value_text(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_attr_value_quote(XMQParseState*state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_comment(XMQParseState*state, size_t l, size_t c, const char *start, const char *stop, const char *suffix);
void do_comment_continuation(XMQParseState*state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_apar_left(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_apar_right(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_brace_left(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_brace_right(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_cpar_left(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_cpar_right(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_equals(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_element_key(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_element_name(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_element_ns(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_element_value_compound_entity(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_element_value_compound_quote(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_element_value_entity(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_element_value_text(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_element_value_quote(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_entity(XMQParseState *state, size_t l, size_t c, const char *cstart, const char *cstop, const char*stop);
void do_ns_colon(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
void do_quote(XMQParseState *state, size_t l, size_t col, const char *start, const char *stop, const char *suffix);
void do_whitespace(XMQParseState *state, size_t line, size_t col, const char *start, const char *stop, const char *suffix);
bool find_line(const char *start, const char *stop, size_t *indent, const char **after_last_non_space, const char **eol);
void fixup_html(XMQDoc *doq, xmlNode *node, bool inside_cdata_declared);
void fixup_comments(XMQDoc *doq, xmlNode *node, int depth);
void generate_dom_from_yaep_node(xmlDocPtr doc, xmlNodePtr node, YaepTreeNode *n, YaepTreeNode *parent, int depth, int index);
void handle_yaep_syntax_error(YaepParseRun *pr,
                              int err_tok_num,
                              void *err_tok_attr,
                              int start_ignored_tok_num,
                              void *start_ignored_tok_attr,
                              int start_recovered_tok_num,
                              void *start_recovered_tok_attr);

size_t line_length(const char *start, const char *stop, int *numq, int *lq, int *eq);
const char *node_yaep_type_to_string(YaepTreeNodeType t);
void reset_ansi(XMQParseState *state);
void reset_ansi_nl(XMQParseState *state);
const char *skip_any_potential_bom(const char *start, const char *stop);
void text_print_node(XMQPrintState *ps, xmlNode *node);
void text_print_nodes(XMQPrintState *ps, xmlNode *from);
void cline_print_node(XMQPrintState *ps, xmlNode *node);
void cline_print_nodes(XMQPrintState *ps, xmlNode *from);
void cline_print_xpath(XMQPrintState *ps, xmlNode *node);
void cline_print_attributes(XMQPrintState *ps, xmlNode *node);
void cline_print_attr(XMQPrintState *ps, xmlAttr *a);
bool write_print_stderr(void *writer_state_ignored, const char *start, const char *stop);
bool write_print_stdout(void *writer_state_ignored, const char *start, const char *stop);
void write_safe_html(XMQWrite write, void *writer_state, const char *start, const char *stop);
void write_safe_tex(XMQWrite write, void *writer_state, const char *start, const char *stop);
bool xmqVerbose();
void xmqSetupParseCallbacksNoop(XMQParseCallbacks *callbacks);
bool xmq_parse_buffer_html(XMQDoc *doq, const char *start, const char *stop, int flags);
bool xmq_parse_buffer_xml(XMQDoc *doq, const char *start, const char *stop, int flags);
bool xmq_parse_buffer_text(XMQDoc *doq, const char *start, const char *stop, const char *implicit_root);
bool xmq_parse_buffer_clines(XMQDoc *doq, const char *start, const char *stop);
void xmq_print_html(XMQDoc *doq, XMQOutputSettings *output_settings);
void xmq_print_xml(XMQDoc *doq, XMQOutputSettings *output_settings);
void xmq_print_xmq(XMQDoc *doq, XMQOutputSettings *output_settings);
void xmq_print_json(XMQDoc *doq, XMQOutputSettings *output_settings);
void xmq_print_text(XMQDoc *doq, XMQOutputSettings *output_settings);
void xmq_print_clines(XMQDoc *doq, XMQOutputSettings *output_settings);
char *xmq_quote_with_entity_newlines(const char *start, const char *stop, XMQQuoteSettings *settings);
char *xmq_quote_default(int indent, const char *start, const char *stop, XMQQuoteSettings *settings);
const char *xml_element_type_to_string(xmlElementType type);
const char *indent_depth(int i);
void free_indent_depths();

// Declare tokenize_whitespace tokenize_name functions etc...
#define X(TYPE) void tokenize_##TYPE(XMQParseState*state, size_t line, size_t col,const char *start, const char *stop, const char *suffix);
LIST_OF_XMQ_TOKENS
#undef X

// Declare debug_whitespace debug_name functions etc...
#define X(TYPE) void debug_token_##TYPE(XMQParseState*state,size_t line,size_t col,const char*start,const char*stop,const char*suffix);
LIST_OF_XMQ_TOKENS
#undef X

//////////////////////////////////////////////////////////////////////////////////

char ansi_reset_color[] = "\033[0m";

void xmqSetupDefaultColors(XMQOutputSettings *os)
{
    bool dark_mode = os->bg_dark_mode;
    XMQTheme *theme = os->theme;
    if (os->render_theme == NULL)
    {
        if (os->render_to == XMQ_RENDER_TEX) dark_mode = false;
        os->render_theme = dark_mode?"darkbg":"lightbg";
    }
    else
    {
        if (!strcmp(os->render_theme, "darkbg"))
        {
            dark_mode = true;
        }
        else if (!strcmp(os->render_theme, "lightbg"))
        {
            dark_mode = false;
        }
    }

    verbose("xmq=", "use theme %s", os->render_theme);
    installDefaultThemeColors(theme);

    os->indentation_space = theme->indentation_space; // " ";
    os->explicit_space = theme->explicit_space; // " ";
    os->explicit_nl = theme->explicit_nl; // "\n";
    os->explicit_tab = theme->explicit_tab; // "\t";
    os->explicit_cr = theme->explicit_cr; // "\r";

    if (os->render_to == XMQ_RENDER_PLAIN)
    {
    }
    else
    if (os->render_to == XMQ_RENDER_TERMINAL)
    {
        setup_terminal_coloring(os, theme, dark_mode, os->use_color, os->render_raw);
    }
    else if (os->render_to == XMQ_RENDER_HTML)
    {
        setup_html_coloring(os, theme, dark_mode, os->use_color, os->render_raw);
    }
    else if (os->render_to == XMQ_RENDER_TEX)
    {
        setup_tex_coloring(os, theme, dark_mode, os->use_color, os->render_raw);
    }

    if (os->only_style)
    {
        printf("%s\n", theme->style.pre);
        exit(0);
    }

}

const char *add_color(XMQColorDef *colors, XMQColorName n, char **pp);
const char *add_color(XMQColorDef *colors, XMQColorName n, char **pp)
{
#ifdef PLATFORM_WINAPI
    const char *tmp = ansiWin((int)n);
    char *p = *pp;
    char *color = p;
    strcpy(p, tmp);
    p += strlen(tmp);
    *p++ = 0;
    *pp = p;
    return color;
#else
    XMQColorDef *def = &colors[n];
    char *p = *pp;
    // Remember where the color starts in the buffer.
    char *color = p;
    char tmp[128];
    generate_ansi_color(tmp, 128, def);
    // Append the new color to the buffer.
    strcpy(p, tmp);
    p += strlen(tmp);
    *p++ = 0;
    // Export the new position in the buffer
    *pp = p;
    // Return the color position;
    return color;
#endif
}
void setup_terminal_coloring(XMQOutputSettings *os, XMQTheme *theme, bool dark_mode, bool use_color, bool render_raw)
{
    if (!use_color) return;

    XMQColorDef *colors = theme->colors_darkbg;
    if (!dark_mode) colors = theme->colors_lightbg;

    char *commands = (char*)malloc(4096);
    os->free_me = commands;
    char *p = commands;

    const char *c = add_color(colors, XMQ_COLOR_C, &p);
    theme->comment.pre = c;
    theme->comment_continuation.pre = c;

    c = add_color(colors, XMQ_COLOR_Q, &p);
    theme->quote.pre = c;

    c = add_color(colors, XMQ_COLOR_E, &p);
    theme->entity.pre = c;
    theme->element_value_entity.pre = c;
    theme->element_value_compound_entity.pre = c;
    theme->attr_value_entity.pre = c;
    theme->attr_value_compound_entity.pre = c;

    c = add_color(colors, XMQ_COLOR_NS, &p);
    theme->element_ns.pre = c;
    theme->attr_ns.pre = c;

    c = add_color(colors, XMQ_COLOR_EN, &p);
    theme->element_name.pre = c;

    c = add_color(colors,XMQ_COLOR_EK, &p);
    theme->element_key.pre = c;

    c = add_color(colors, XMQ_COLOR_EKV, &p);
    theme->element_value_text.pre = c;
    theme->element_value_quote.pre = c;
    theme->element_value_compound_quote.pre = c;

    c = add_color(colors, XMQ_COLOR_AK, &p);
    theme->attr_key.pre = c;

    c = add_color(colors, XMQ_COLOR_AKV, &p);
    theme->attr_value_text.pre = c;
    theme->attr_value_quote.pre = c;
    theme->attr_value_compound_quote.pre = c;

    c = add_color(colors, XMQ_COLOR_CP, &p);
    theme->cpar_left.pre  = c;
    theme->cpar_right.pre = c;

    c = add_color(colors, XMQ_COLOR_NSD, &p);
    theme->ns_declaration.pre = c;

    c = add_color(colors, XMQ_COLOR_UW, &p);
    theme->unicode_whitespace.pre = c;

    c = add_color(colors, XMQ_COLOR_XLS, &p);
    theme->ns_override_xsl.pre = c;

    theme->whitespace.pre  = NOCOLOR;
    theme->equals.pre      = NOCOLOR;
    theme->brace_left.pre  = NOCOLOR;
    theme->brace_right.pre = NOCOLOR;
    theme->apar_left.pre    = NOCOLOR;
    theme->apar_right.pre   = NOCOLOR;
    theme->ns_colon.pre = NOCOLOR;

    theme->document.post = NOCOLOR;
}

void setup_html_coloring(XMQOutputSettings *os, XMQTheme *theme, bool dark_mode, bool use_color, bool render_raw)
{
    os->indentation_space = " ";
    os->explicit_nl = "\n";
    if (!render_raw)
    {
        theme->document.pre =
            "<!DOCTYPE html>\n<html>\n";
        theme->document.post =
            "</html>";
        theme->header.pre =
            "<head><meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">"
            "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=5\"><style>";
        theme->header.post =
            "</style></head>";

        MemBuffer *style_pre = new_membuffer();

        membuffer_append(style_pre,
                         "@media screen and (orientation: portrait) { pre { font-size: 2vw; } }"
                         "@media screen and (orientation: landscape) { pre { max-width: 98%; } }"
                         "pre.xmq_dark {white-space:pre-wrap;word-break:break-all;border-radius:2px;background-color:#263338;border:solid 1px #555555;display:inline-block;padding:1em;color:white;}\n"
                         "pre.xmq_light{white-space:pre-wrap;word-break:break-all;border-radius:2px;background-color:#ffffcc;border:solid 1px #888888;display:inline-block;padding:1em;color:black;}\n"
                         "body.xmq_dark {background-color:black;}\n"
                         "body.xmq_light {}\n");

        for (int i=0; i<NUM_XMQ_COLOR_NAMES; ++i)
        {
            char buf[128];
            generate_html_color(buf, 128, &theme->colors_darkbg[i], colorName(i));
            membuffer_append(style_pre, buf);
        }
        membuffer_append(style_pre, "pre.xmq_light {\n");

        for (int i=0; i<NUM_XMQ_COLOR_NAMES; ++i)
        {
            char buf[128];
            generate_html_color(buf, 128, &theme->colors_lightbg[i], colorName(i));
            membuffer_append(style_pre, buf);
        }

        membuffer_append(style_pre, "pre.xmq_dark {}\n}\n");
        membuffer_append_null(style_pre);

        theme->style.pre = free_membuffer_but_return_trimmed_content(style_pre);
        os->free_me = (void*)theme->style.pre;
        if (dark_mode)
        {
            theme->body.pre = "<body class=\"xmq_dark\">";
        }
        else
        {
            theme->body.pre = "<body class=\"xmq_light\">";
        }

        theme->body.post =
            "</body>";
    }

    theme->content.pre = "<pre>";
    theme->content.post = "</pre>";

    const char *mode = "xmq_light";
    if (dark_mode) mode = "xmq_dark";

    char *buf = (char*)malloc(1024);
    os->free_and_me = buf;
    const char *id = os->use_id;
    const char *idb = "id=\"";
    const char *ide = "\" ";
    if (!id)
    {
        id = "";
        idb = "";
        ide = "";
    }
    const char *clazz = os->use_class;
    const char *space = " ";
    if (!clazz)
    {
        clazz = "";
        space = "";
    }
    snprintf(buf, 1023, "<pre %s%s%sclass=\"xmq %s%s%s\">", idb, id, ide, mode, space, clazz);
    theme->content.pre = buf;

    theme->whitespace.pre  = NULL;
    theme->indentation_whitespace.pre = NULL;
    theme->unicode_whitespace.pre  = "<xmqUW>";
    theme->unicode_whitespace.post  = "</xmqUW>";
    theme->equals.pre      = NULL;
    theme->brace_left.pre  = NULL;
    theme->brace_right.pre = NULL;
    theme->apar_left.pre    = NULL;
    theme->apar_right.pre   = NULL;
    theme->cpar_left.pre = "<xmqCP>";
    theme->cpar_left.post = "</xmqCP>";
    theme->cpar_right.pre = "<xmqCP>";
    theme->cpar_right.post = "</xmqCP>";
    theme->quote.pre = "<xmqQ>";
    theme->quote.post = "</xmqQ>";
    theme->entity.pre = "<xmqE>";
    theme->entity.post = "</xmqE>";
    theme->comment.pre = "<xmqC>";
    theme->comment.post = "</xmqC>";
    theme->comment_continuation.pre = "<xmqC>";
    theme->comment_continuation.post = "</xmqC>";
    theme->element_ns.pre = "<xmqNS>";
    theme->element_ns.post = "</xmqNS>";
    theme->element_name.pre = "<xmqEN>";
    theme->element_name.post = "</xmqEN>";
    theme->element_key.pre = "<xmqEK>";
    theme->element_key.post = "</xmqEK>";
    theme->element_value_text.pre = "<xmqEKV>";
    theme->element_value_text.post = "</xmqEKV>";
    theme->element_value_quote.pre = "<xmqEKV>";
    theme->element_value_quote.post = "</xmqEKV>";
    theme->element_value_entity.pre = "<xmqE>";
    theme->element_value_entity.post = "</xmqE>";
    theme->element_value_compound_quote.pre = "<xmqEKV>";
    theme->element_value_compound_quote.post = "</xmqEKV>";
    theme->element_value_compound_entity.pre = "<xmqE>";
    theme->element_value_compound_entity.post = "</xmqE>";
    theme->attr_ns.pre = "<xmqNS>";
    theme->attr_ns.post = "</xmqNS>";
    theme->attr_key.pre = "<xmqAK>";
    theme->attr_key.post = "</xmqAK>";
    theme->attr_value_text.pre = "<xmqAKV>";
    theme->attr_value_text.post = "</xmqAKV>";
    theme->attr_value_quote.pre = "<xmqAKV>";
    theme->attr_value_quote.post = "</xmqAKV>";
    theme->attr_value_entity.pre = "<xmqE>";
    theme->attr_value_entity.post = "</xmqE>";
    theme->attr_value_compound_quote.pre = "<xmqAKV>";
    theme->attr_value_compound_quote.post = "</xmqAKV>";
    theme->attr_value_compound_entity.pre = "<xmqE>";
    theme->attr_value_compound_entity.post = "</xmqE>";
    theme->ns_declaration.pre = "<xmqNSD>";
    theme->ns_declaration.post = "</xmqNSD>";
    theme->ns_override_xsl.pre = "<xmqXSL>";
    theme->ns_override_xsl.post = "</xmqXSL>";
    theme->ns_colon.pre = NULL;
}

void setup_tex_coloring(XMQOutputSettings *os, XMQTheme *theme, bool dark_mode, bool use_color, bool render_raw)
{
    XMQColorDef *colors = theme->colors_darkbg;
    if (!dark_mode) colors = theme->colors_lightbg;

    os->indentation_space = "\\xmqI ";
    os->explicit_space = " ";
    os->explicit_nl = "\\linebreak\n";

    if (!render_raw)
    {
        theme->document.pre =
            "\\documentclass[10pt,a4paper]{article}\n"
            "\\usepackage{color}\n"
            "\\usepackage{bold-extra}\n";

        char *style_pre = (char*)malloc(4096);
        char *p = style_pre;

        for (int i=0; i<NUM_XMQ_COLOR_NAMES; ++i)
        {
            char buf[128];
            generate_tex_color(buf, 128, &theme->colors_lightbg[i], colorName(i));
            strcpy(p, buf);
            p += strlen(p);
            *p++ = '\n';
        }

        for (int i=0; i<NUM_XMQ_COLOR_NAMES; ++i)
        {
            char buf[128];
            const char *bold_pre = "";
            const char *bold_post = "";
            const char *underline_pre = "";
            const char *underline_post = "";

            if (colors[i].bold)
            {
                bold_pre = "\\textbf{";
                bold_post = "}";
            }
            if (colors[i].underline)
            {
                underline_pre = "\\underline{";
                underline_post = "}";
            }

            snprintf(buf, 128, "\\newcommand{\\%s}[1]{{\\color{%s}%s%s#1%s%s}}\n",
                     colorName(i), colorName(i), bold_pre, underline_pre, bold_post, underline_post);

            strcpy(p, buf);
            p += strlen(p);
        }

        const char *cmds =
            "\\newcommand{\\xmqI}[0]{{\\mbox{\\ }}}\n";

        strcpy(p, cmds);
        p += strlen(p);
        *p = 0;

        theme->style.pre = style_pre;

        theme->body.pre = "\n\\begin{document}\n";
        theme->body.post = "\n\\end{document}\n";
    }

    theme->content.pre = "\\texttt{\\flushleft\\noindent ";
    theme->content.post = "\n}\n";
    theme->whitespace.pre  = NULL;
    theme->indentation_whitespace.pre = NULL;
    theme->unicode_whitespace.pre  = "\\xmqUW{";
    theme->unicode_whitespace.post  = "}";
    theme->equals.pre      = NULL;
    theme->brace_left.pre  = NULL;
    theme->brace_right.pre = NULL;
    theme->apar_left.pre    = NULL;
    theme->apar_right.pre   = NULL;
    theme->cpar_left.pre = "\\xmqCP{";
    theme->cpar_left.post = "}";
    theme->cpar_right.pre = "\\xmqCP{";
    theme->cpar_right.post = "}";
    theme->quote.pre = "\\xmqQ{";
    theme->quote.post = "}";
    theme->entity.pre = "\\xmqE{";
    theme->entity.post = "}";
    theme->comment.pre = "\\xmqC{";
    theme->comment.post = "}";
    theme->comment_continuation.pre = "\\xmqC{";
    theme->comment_continuation.post = "}";
    theme->element_ns.pre = "\\xmqNS{";
    theme->element_ns.post = "}";
    theme->element_name.pre = "\\xmqEN{";
    theme->element_name.post = "}";
    theme->element_key.pre = "\\xmqEK{";
    theme->element_key.post = "}";
    theme->element_value_text.pre = "\\xmqEKV{";
    theme->element_value_text.post = "}";
    theme->element_value_quote.pre = "\\xmqEKV{";
    theme->element_value_quote.post = "}";
    theme->element_value_entity.pre = "\\xmqE{";
    theme->element_value_entity.post = "}";
    theme->element_value_compound_quote.pre = "\\xmqEKV{";
    theme->element_value_compound_quote.post = "}";
    theme->element_value_compound_entity.pre = "\\xmqE{";
    theme->element_value_compound_entity.post = "}";
    theme->attr_ns.pre = "\\xmqNS{";
    theme->attr_ns.post = "}";
    theme->attr_key.pre = "\\xmqAK{";
    theme->attr_key.post = "}";
    theme->attr_value_text.pre = "\\xmqAKV{";
    theme->attr_value_text.post = "}";
    theme->attr_value_quote.pre = "\\xmqAKV{";
    theme->attr_value_quote.post = "}";
    theme->attr_value_entity.pre = "\\xmqE{";
    theme->attr_value_entity.post = "}";
    theme->attr_value_compound_quote.pre = "\\xmqAKV{";
    theme->attr_value_compound_quote.post = "}";
    theme->attr_value_compound_entity.pre = "\\xmqE{";
    theme->attr_value_compound_entity.post = "}";
    theme->ns_declaration.pre = "\\xmqNSD{";
    theme->ns_declaration.post = "}";
    theme->ns_override_xsl.pre = "\\xmqXSL{";
    theme->ns_override_xsl.post = "}";
    theme->ns_colon.pre = NULL;
}

void xmqOverrideSettings(XMQOutputSettings *settings,
                         const char *indentation_space,
                         const char *explicit_space,
                         const char *explicit_tab,
                         const char *explicit_cr,
                         const char *explicit_nl)
{
    if (indentation_space) settings->indentation_space = indentation_space;
    if (explicit_space) settings->explicit_space = explicit_space;
    if (explicit_tab) settings->explicit_tab = explicit_tab;
    if (explicit_cr) settings->explicit_cr = explicit_cr;
    if (explicit_nl) settings->explicit_nl = explicit_nl;
}

void xmqRenderHtmlSettings(XMQOutputSettings *settings,
                           const char *use_id,
                           const char *use_class)
{
    if (use_id) settings->use_id = use_id;
    if (use_class) settings->use_class = use_class;
}

void xmqOverrideColor(XMQOutputSettings *os, const char *render_style, XMQSyntax sy, const char *pre, const char *post, const char *ns)
{
    //
}

int xmqStateErrno(XMQParseState *state)
{
    return (int)state->error_nr;
}

#define X(TYPE) \
    void tokenize_##TYPE(XMQParseState*state, size_t line, size_t col,const char *start,const char *stop,const char *suffix) { \
        if (!state->simulated) { \
            const char *pre, *post;  \
            getThemeStrings(state->output_settings, COLOR_##TYPE, &pre, &post); \
            if (pre) state->output_settings->content.write(state->output_settings->content.writer_state, pre, NULL); \
            if (state->output_settings->render_to == XMQ_RENDER_TERMINAL) { \
                state->output_settings->content.write(state->output_settings->content.writer_state, start, stop); \
            } else if (state->output_settings->render_to == XMQ_RENDER_HTML) { \
                write_safe_html(state->output_settings->content.write, state->output_settings->content.writer_state, start, stop); \
            } else if (state->output_settings->render_to == XMQ_RENDER_TEX) { \
                write_safe_tex(state->output_settings->content.write, state->output_settings->content.writer_state, start, stop); \
            } \
            if (post) state->output_settings->content.write(state->output_settings->content.writer_state, post, NULL); \
        } \
    }
LIST_OF_XMQ_TOKENS
#undef X


const char *xmqStateErrorMsg(XMQParseState *state)
{
    if (!state->generated_error_msg && state->generating_error_msg)
    {
        state->generated_error_msg = free_membuffer_but_return_trimmed_content(state->generating_error_msg);
        state->generating_error_msg = NULL;
    }
    return state->generated_error_msg;
}

void reset_ansi(XMQParseState *state)
{
    state->output_settings->content.write(state->output_settings->content.writer_state, ansi_reset_color, NULL);
}

void reset_ansi_nl(XMQParseState *state)
{
    state->output_settings->content.write(state->output_settings->content.writer_state, ansi_reset_color, NULL);
    state->output_settings->content.write(state->output_settings->content.writer_state, "\n", NULL);
}

void add_nl(XMQParseState *state)
{
    state->output_settings->content.write(state->output_settings->content.writer_state, "\n", NULL);
}

XMQOutputSettings *xmqNewOutputSettings()
{
    XMQOutputSettings *os = (XMQOutputSettings*)malloc(sizeof(XMQOutputSettings));
    memset(os, 0, sizeof(XMQOutputSettings));
    XMQTheme *theme = (XMQTheme*)malloc(sizeof(XMQTheme));
    memset(theme, 0, sizeof(XMQTheme));
    os->theme = theme;

    os->indentation_space = theme->indentation_space = " ";
    os->explicit_space = theme->explicit_space = " ";
    os->explicit_nl = theme->explicit_nl = "\n";
    os->explicit_tab = theme->explicit_tab = "\t";
    os->explicit_cr = theme->explicit_cr = "\r";
    os->add_indent = 4;
    os->use_color = false;
    os->allow_json_quotes = true;

    return os;
}

void xmqFreeOutputSettings(XMQOutputSettings *os)
{
    if (os->theme)
    {
        free(os->theme);
        os->theme = NULL;
    }
    if (os->free_me)
    {
        free(os->free_me);
        os->free_me = NULL;
    }
    if (os->free_and_me)
    {
        free(os->free_and_me);
        os->free_and_me = NULL;
    }
    free(os);
}

void xmqSetAddIndent(XMQOutputSettings *os, int add_indent)
{
    os->add_indent = add_indent;
}

void xmqSetCompact(XMQOutputSettings *os, bool compact)
{
    os->compact = compact;
}

void xmqSetUseColor(XMQOutputSettings *os, bool use_color)
{
    os->use_color = use_color;
}

void xmqSetBackgroundMode(XMQOutputSettings *os, bool bg_dark_mode)
{
    os->bg_dark_mode = bg_dark_mode;
}

void xmqSetPreferDoubleQuotes(XMQOutputSettings *os, bool prefer_double_quotes)
{
    os->prefer_double_quotes = prefer_double_quotes;
}

void xmqSetEscapeNewlines(XMQOutputSettings *os, bool escape_newlines)
{
    os->escape_newlines = escape_newlines;
}

void xmqSetEscapeNon7bit(XMQOutputSettings *os, bool escape_non_7bit)
{
    os->escape_non_7bit = escape_non_7bit;
}

void xmqSetEscapeTabs(XMQOutputSettings *os, bool escape_tabs)
{
    os->escape_tabs = escape_tabs;
}

void xmqSetOutputFormat(XMQOutputSettings *os, XMQContentType output_format)
{
    os->output_format = output_format;
}

void xmqSetOmitDecl(XMQOutputSettings *os, bool omit_decl)
{
    os->omit_decl = omit_decl;
}

void xmqSetRenderFormat(XMQOutputSettings *os, XMQRenderFormat render_to)
{
    os->render_to = render_to;
}

void xmqSetRenderRaw(XMQOutputSettings *os, bool render_raw)
{
    os->render_raw = render_raw;
}

void xmqSetRenderTheme(XMQOutputSettings *os, const char *theme_name)
{
    os->render_theme = theme_name;
}

void xmqSetRenderOnlyStyle(XMQOutputSettings *os, bool only_style)
{
    os->only_style = only_style;
}

void xmqSetWriterContent(XMQOutputSettings *os, XMQWriter content)
{
    os->content = content;
}

void xmqSetWriterError(XMQOutputSettings *os, XMQWriter error)
{
    os->error = error;
}

bool write_print_stdout(void *writer_state_ignored, const char *start, const char *stop)
{
    if (!start) return true;
    if (!stop)
    {
        fputs(start, stdout);
    }
    else
    {
        assert(stop > start);
        fwrite(start, stop-start, 1, stdout);
    }
    return true;
}

bool write_print_stderr(void *writer_state_ignored, const char *start, const char *stop)
{
    if (!start) return true;
    if (!stop)
    {
        fputs(start, stderr);
    }
    else
    {
        fwrite(start, stop-start, 1, stderr);
    }
    return true;
}

void write_safe_html(XMQWrite write, void *writer_state, const char *start, const char *stop)
{
    for (const char *i = start; i < stop; ++i)
    {
        const char *amp = "&amp;";
        const char *lt = "&lt;";
        const char *gt = "&gt;";
        const char *quot = "&quot;";
        if (*i == '&') write(writer_state, amp, amp+5);
        else if (*i == '<') write(writer_state, lt, lt+4);
        else if (*i == '>') write(writer_state, gt, gt+4);
        else if (*i == '"') write(writer_state, quot, quot+6); //"
        else write(writer_state, i, i+1);
    }
}

void write_safe_tex(XMQWrite write, void *writer_state, const char *start, const char *stop)
{
    for (const char *i = start; i < stop; ++i)
    {
        const char *amp = "\\&";
        const char *bs = "\\\\";
        const char *us = "\\_";
        if (*i == '&') write(writer_state, amp, amp+2);
        else if (*i == '\\') write(writer_state, bs, bs+2);
        else if (*i == '_') write(writer_state, us, us+2);
        else write(writer_state, i, i+1);
    }
}

void xmqSetupPrintStdOutStdErr(XMQOutputSettings *ps)
{
    ps->content.writer_state = NULL; // Not needed
    ps->content.write = write_print_stdout;
    ps->error.writer_state = NULL; // Not needed
    ps->error.write = write_print_stderr;
}

void xmqSetupPrintMemory(XMQOutputSettings *os, char **start, char **stop)
{
    os->output_buffer_start = start;
    os->output_buffer_stop = stop;
    os->output_buffer = new_membuffer();
    os->content.writer_state = os->output_buffer;
    os->content.write = (XMQWrite)(void*)membuffer_append_region;
    os->error.writer_state = os->output_buffer;
    os->error.write = (XMQWrite)(void*)membuffer_append_region;
}

void xmqSetupPrintSkip(XMQOutputSettings *os, size_t *skip)
{
    os->output_skip = skip;
}

XMQParseCallbacks *xmqNewParseCallbacks()
{
    XMQParseCallbacks *callbacks = (XMQParseCallbacks*)malloc(sizeof(XMQParseCallbacks));
    memset(callbacks, 0, sizeof(XMQParseCallbacks));
    return callbacks;
}

XMQParseState *xmqNewParseState(XMQParseCallbacks *callbacks, XMQOutputSettings *output_settings)
{
    if (!callbacks)
    {
        PRINT_ERROR("xmqNewParseState is given a NULL callback structure!\n");
        assert(0);
        exit(1);
    }
    if (!output_settings)
    {
        PRINT_ERROR("xmqNewParseState is given a NULL print output_settings structure!\n");
        assert(0);
        exit(1);
    }
    if (callbacks->magic_cookie != MAGIC_COOKIE)
    {
        PRINT_ERROR("xmqNewParseState is given a callback structure which is not initialized!\n");
        assert(0);
        exit(1);
    }
    XMQParseState *state = (XMQParseState*)malloc(sizeof(XMQParseState));
    memset(state, 0, sizeof(XMQParseState));
    state->parse = callbacks;
    state->output_settings = output_settings;
    state->magic_cookie = MAGIC_COOKIE;
    state->element_stack = stack_create();

    return state;
}

bool xmqTokenizeBuffer(XMQParseState *state, const char *start, const char *stopp)
{
    if (state->magic_cookie != MAGIC_COOKIE)
    {
        PRINT_ERROR("Parser state not initialized!\n");
        assert(0);
        exit(1);
    }

    state->buffer_start = start;
    state->buffer_stop =  stopp?stopp:start + strlen(start);
    state->i = start;
    state->line = 1;
    state->col = 1;
    state->error_nr = XMQ_ERROR_NONE;

    XMQContentType detected_ct = xmqDetectContentType(state->buffer_start, state->buffer_stop);
    if (detected_ct != XMQ_CONTENT_XMQ)
    {
        state->generated_error_msg = strdup("xmq: you can only tokenize the xmq format");
        state->error_nr = XMQ_ERROR_NOT_XMQ;
        return false;
    }


    if (state->parse->init) state->parse->init(state);

    XMQOutputSettings *output_settings = state->output_settings;
    XMQWrite write = output_settings->content.write;
    void *writer_state = output_settings->content.writer_state;

    const char *pre = output_settings->theme->content.pre;
    const char *post = output_settings->theme->content.post;
    if (pre) write(writer_state, pre, NULL);

    if (!setjmp(state->error_handler))
    {
        // Start parsing!
        parse_xmq(state);
        if (state->i < state->buffer_stop)
        {
            state->error_nr = XMQ_ERROR_UNEXPECTED_CLOSING_BRACE;
            longjmp(state->error_handler, 1);
        }
    }
    else
    {
        XMQParseError error_nr = state->error_nr;
        if (error_nr == XMQ_ERROR_INVALID_CHAR && state->last_suspicios_quote_end)
        {
            // Add warning about suspicious quote before the error.
            generate_state_error_message(state, XMQ_WARNING_QUOTES_NEEDED, state->buffer_start, state->buffer_stop);
        }
        generate_state_error_message(state, error_nr, state->buffer_start, state->buffer_stop);

        return false;
    }

    if (post) write(writer_state, post, NULL);

    if (state->parse->done) state->parse->done(state);

    if (output_settings->output_buffer &&
        output_settings->output_buffer_start &&
        output_settings->output_buffer_stop)
    {
        size_t size = membuffer_used(output_settings->output_buffer);
        char *buffer = free_membuffer_but_return_trimmed_content(output_settings->output_buffer);
        *output_settings->output_buffer_start = buffer;
        *output_settings->output_buffer_stop = buffer+size;
    }

    return true;
}

bool xmqTokenizeFile(XMQParseState *state, const char *file)
{
    bool rc = false;
    const char *buffer = NULL;
    size_t fsize = 0;
    XMQContentType content = XMQ_CONTENT_XMQ;

    XMQDoc *doq = xmqNewDoc();

    if (file)
    {
        xmqSetDocSourceName(doq, file);
        rc = load_file(doq, file, &fsize, &buffer);
    }
    else
    {
        xmqSetDocSourceName(doq, "-");
        rc = load_stdin(doq, &fsize, &buffer);
    }
    if (!rc) return false;

    xmqSetStateSourceName(state, file);

    content = xmqDetectContentType(buffer, buffer+fsize);
    if (content != XMQ_CONTENT_XMQ)
    {
        state->generated_error_msg = strdup("You can only tokenize xmq!");
        state->error_nr = XMQ_ERROR_NOT_XMQ;
        rc = false;
        goto exit;
    }

    rc = xmqTokenizeBuffer(state, buffer, buffer+fsize);

    exit:

    free((void*)buffer);
    xmqFreeDoc(doq);

    return rc;
}

/** This function is used only for detecting the kind of content: xmq, xml, html, json. */
const char *find_word_ignore_case(const char *start, const char *stop, const char *word)
{
    const char *i = start;
    size_t len = strlen(word);

    while (i < stop && is_xml_whitespace(*i)) i++;
    if (!strncasecmp(i, word, len))
    {
        const char *next = i+len;
        if (next <= stop && (is_xml_whitespace(*next) || *next == 0 || !isalnum(*next)))
        {
            // The word was properly terminated with a 0, or a whitespace or something not alpha numeric.
            return i+strlen(word);
        }
    }
    return NULL;
}

XMQContentType xmqDetectContentType(const char *start, const char *stop)
{
    const char *i = start;

    while (i < stop)
    {
        char c = *i;
        if (!is_xml_whitespace(c))
        {
            if (c == '<')
            {
                if (i+4 < stop &&
                    *(i+1) == '?' &&
                    *(i+2) == 'x' &&
                    *(i+3) == 'm' &&
                    *(i+4) == 'l')
                {
                    debug("xmq=", "content detected as xml since <?xml found");
                    return XMQ_CONTENT_XML;
                }

                if (i+3 < stop &&
                    *(i+1) == '!' &&
                    *(i+2) == '-' &&
                    *(i+3) == '-')
                {
                    // This is a comment, zip past it.
                    while (i+2 < stop &&
                           !(*(i+0) == '-' &&
                             *(i+1) == '-' &&
                             *(i+2) == '>'))
                    {
                        i++;
                    }
                    i += 3;
                    // No closing comment, return as xml.
                    if (i >= stop)
                    {
                        debug("xmq=", "content detected as xml since comment start found");
                        return XMQ_CONTENT_XML;
                    }
                    // Pick up after the comment.
                    c = *i;
                }

                // Starts with <html or < html
                const char *is_html = find_word_ignore_case(i+1, stop, "html");
                if (is_html)
                {
                    debug("xmq=", "content detected as html since html found");
                    return XMQ_CONTENT_HTML;
                }

                // Starts with <!doctype  html
                const char *is_doctype = find_word_ignore_case(i, stop, "<!doctype");
                if (is_doctype)
                {
                    i = is_doctype;
                    is_html = find_word_ignore_case(is_doctype+1, stop, "html");
                    if (is_html)
                    {
                        debug("xmq=", "content detected as html since doctype html found");
                        return XMQ_CONTENT_HTML;
                    }
                }
                // Otherwise we assume it is xml. If you are working with html content inside
                // the html, then use --html
                debug("xmq=", "content assumed to be xml");
                return XMQ_CONTENT_XML; // Or HTML...
            }
            if (c == '{' || c == '"' || c == '[' || (c >= '0' && c <= '9')) // "
            {
                debug("xmq=", "content detected as json");
                return XMQ_CONTENT_JSON;
            }
            // Strictly speaking true,false and null are valid xmq files. But we assume
            // it is json since it must be very rare with a single <true> <false> <null> tag in xml/xmq/html/htmq etc.
            // Force xmq with --xmq for the cli command.
            size_t l = 0;
            if (c == 't' || c == 'n') l = 4;
            else if (c == 'f') l = 5;

            if (l != 0)
            {
                if (i+l-1 < stop)
                {
                    if (i+l == stop || (*(i+l) == '\n' && i+l+1 == stop))
                    {
                        if (!strncmp(i, "true", 4) ||
                            !strncmp(i, "false", 5) ||
                            !strncmp(i, "null", 4))
                        {
                            debug("xmq=", "content detected as json since true/false/null found");
                            return XMQ_CONTENT_JSON;
                        }
                    }
                }
            }
            debug("xmq=", "content assumed to be xmq");
            return XMQ_CONTENT_XMQ;
        }
        i++;
    }

    debug("xmq=", "empty content assumed to be xmq");
    return XMQ_CONTENT_XMQ;
}


/** Scan a line, ie until \n pr \r\n or \r or NULL.
    Return true if a newline was found. */
bool find_line(const char *start, // Start scanning the line from here.
               const char *stop,  // Points to char after end of buffer.
               size_t *indent,  // Store indentation in number of spaces.
               const char **after_last_non_space, // Points to char after last non-space char on line, start if whole line is spaces.
               const char **eol)    // Points to char after '\n' or to stop.
{
    assert(start <= stop);

    bool has_nl = false;
    size_t ndnt = 0;
    const char *lnws = start;
    const char *i = start;

    // Skip spaces as indententation.
    while (i < stop && (*i == ' ' || *i == '\t'))
    {
        if (*i == ' ') ndnt++;
        else ndnt += 8; // Count tab as 8 chars.
        i++;
    }
    *indent = ndnt;

    // Find eol \n and the last non-space char.
    while (i < stop)
    {
        if (*i == '\n' || *i == '\r')
        {
            if (*i == '\r' && *(i+1) == '\n') i++;
            i++;
            has_nl = true;
            break;
        }
        if (*i != ' ' && *i != '\t') lnws = i+1;
        i++;
    }

    *after_last_non_space = lnws;
    *eol = i;

    return has_nl;
}

void xmqSetDebug(bool e)
{
    xmq_debug_enabled_ = e;
}

bool xmqDebugging()
{
    return xmq_debug_enabled_;
}

void xmqSetTrace(bool e)
{
    xmq_trace_enabled_ = e;
}

bool xmqTracing()
{
    return xmq_trace_enabled_;
}

void xmqSetVerbose(bool e)
{
    xmq_verbose_enabled_ = e;
}

bool xmqVerbose() {
    return xmq_verbose_enabled_;
}

void xmqSetLogHumanReadable(bool e)
{
    xmq_log_line_config_.human_readable_ = e;
}

void xmqLogFilter(const char *log_filter)
{
    xmq_log_filter_ = log_filter;
}

/**
    xmq_un_quote:
    @indent: The number of chars before the quote starts on the first line.
    @space: Use this space to prepend the first line if needed due to indent.
    @start: Start of buffer to unquote.
    @stop: Points to byte after buffer.

    Do the reverse of xmq_quote, take a quote (with or without the surrounding single quotes)
    and removes any incidental indentation. Returns a newly malloced buffer
    that must be free:ed later.

    Use indent 0 if the quote ' is first on the line.
    The indent is 1 if there is a single space before the starting quote ' etc.

    As a special case, if both indent is 0 and space is 0, then the indent of the
    first line is picked from the second line.
*/
char *xmq_un_quote(const char *start, const char *stop, bool remove_qs, bool is_xmq)
{
    if (!stop) stop = start+strlen(start);

    // Remove the surrounding quotes.
    size_t j = 0;
    if (remove_qs)
    {
        const char q = *start;
        assert(q == '\'' || q == '"');
        while (*(start+j) == q && *(stop-j-1) == q && (start+j) < (stop-j)) j++;
    }

    start = start+j;
    stop = stop-j;

    return xmq_trim_quote(start, stop, is_xmq, false);
}

/**
    xmq_un_comment:

    Do the reverse of xmq_comment, Takes a comment (including /✻ ✻/ ///✻ ✻///) and removes any incidental
    indentation and trailing spaces. Returns a newly malloced buffer
    that must be free:ed later.

    The indent is 0 if the / first on the line.
    The indent is 1 if there is a single space before the starting / etc.
*/
char *xmq_un_comment(const char *start, const char *stop)
{
    assert(start < stop);

    const char *i = start;
    while (i < stop && *i == '/') i++;

    if (i == stop)
    {
        // Single line all slashes. Drop the two first slashes which are the single line comment.
        return xmq_trim_quote(start+2, stop, true, true);
    }

    if (*i != '*')
    {
        // No asterisk * after the slashes. This is a single line comment.
        // If there is a space after //, skip it.
        if (*i == ' ') {
            i++;
        }
        // Remove trailing spaces.
        while (i < stop && *(stop-1) == ' ') stop--;
        assert(i <= stop);
        return xmq_trim_quote(i, stop, true, true);
    }

    // There is an asterisk after the slashes. A standard /* */ comment
    // Remove the surrounding / slashes.
    size_t j = 0;
    while (*(start+j) == '/' && *(stop-j-1) == '/' && (start+j) < (stop-j)) j++;

    start = start+j;
    stop = stop-j;

    // Check that the star is there.
    assert(*start == '*' && *(stop-1) == '*');
    start++;
    stop--;

    // Skip a single space immediately after the asterisk or before the ending asterisk.
    // I.e. /* Comment */ results in the comment text "Comment"
    if (*start == ' ')
    {
        start++;
    }
    if (*(stop-1) == ' ')
    {
        if (stop-1 >= start)
        {
            stop--;
        }
    }

    assert(start <= stop);
    char *foo = xmq_trim_quote(start, stop, true, true);
    return foo;
}

bool check_leading_space_nl(const char *start, const char *stop)
{
    while (start < stop && *start == ' ') start++;
    // All spaces, not space_nl.
    if (start == stop) return false;
    // Spaces and now nl, then it is space_nl!
    if (*start == '\n') return true;
    // Anything else, then it is not space_nl.
    return false;
}

size_t calculate_incidental_indent(const char *start, const char *stop)
{
    size_t indent = (size_t)-1; // Maximum indentation possible.
    const char *i = start;

    // Find first newline.
    while (i < stop && *i != 10) i++;

    // No newlines?
    if (i >= stop) return (size_t)-1; // No incidental indentation.

    for (;;)
    {
        // We have reached a newline character.
        assert(*i == '\n');
        i++;

        const char *line_start = i;
        // Look for content.
        while (i < stop && *i == ' ') i++;
        if (i >= stop) break;
        if (*i != 10 && *i != 13)
        {
            // We reached real content.
            size_t ind = i-line_start;
            if (ind < indent) indent = ind;
        }
        // Look for end of line.
        while (i < stop && *i != 10) i++;
        if (i >= stop)
        {
            break;
        }
    }

    return indent;
}

char *xmq_trim_quote(const char *start, const char *stop, bool is_xmq, bool is_comment)
{
    size_t append_newlines = 0;
    size_t last_line_spaces = (size_t)-1;

    if (has_ending_nl_space(start, stop, NULL))
    {
        // So it is, now trim from the end.
        while (stop > start)
        {
            char c = *(stop-1);
            if (c == '\n') append_newlines++;
            if (c == ' ' && append_newlines == 0)
            {
                if (last_line_spaces == (size_t)-1) last_line_spaces = 0;
                last_line_spaces++;
            }
            if (c != ' ' && c != '\t' && c != '\n' && c != '\r') break;
            stop--;
        }
    }
    // If there is one or more ending newlines, then one is dropped, but keep the rest.
    if (append_newlines > 0) append_newlines--;

    if (stop == start)
    {
        // Oups! Quote was all space and newlines.
        char *buf = (char*)malloc(append_newlines+1);
        size_t i;
        for (i = 0; i < append_newlines; ++i) buf[i] = '\n';
        buf[i] = 0;
        return buf;
    }

    // Set to false if quote starts with 'content\n...
    // Set to true if quote starts with '    \n....
    bool leads_space_nl = check_leading_space_nl(start, stop);
    // The leading space nl is used to determine the last lines incidental effect.
    // If we have foo = 'quote
    //                     again
    //                   again
    //                  '
    // Since the quotes are aligned, then we assume that the incidental is the last line+1
    // But if we have foo = '
    //                      quote
    //                        again
    //                      again
    //                      '
    // Then we assume that the incidental is the last line + 0.
    // This makes more sense when using 3 or more quotes, ie.
    //                foo = ''''
    //                      quote
    //                        again
    //                      again
    //                      ''''
    // and            foo = ''''quote
    //                            again
    //                          again
    //                      ''''

    size_t incidental = calculate_incidental_indent(start, stop);
    if (is_xmq && last_line_spaces < incidental)
    {
        incidental = last_line_spaces;
        if (!leads_space_nl)
        {
            incidental++;
            if (is_comment) incidental += 2;
        }
    }

    if (incidental == (size_t)-1)
    {
        // No newline was found, then do not trim, but re-add ending newlines.
        char *buf = (char*)malloc(stop-start+append_newlines+1);
        memcpy(buf, start, stop-start);
        size_t i = stop-start;
        for (size_t j = 0; j < append_newlines; ++j) buf[i++] = '\n';
        buf[i++] = 0;
        return buf;
    }

    size_t prepend_newlines = 0;

    // For each found line, this is where the line ends after trimming line ending whitespace.
    const char *after_last_non_space;

    // This is where the newline char was found.
    const char *eol;

    size_t found_indent = 0;

    bool first_line = true;

    // Now find the first line.
    find_line(start, stop, &found_indent, &after_last_non_space, &eol);

    // Check if the first line is all spaces.
    if (has_leading_space_nl(start, stop, NULL))
    {
        // There is no first line (there was leading space/newline) thus we
        // start removing incidental indentation immediately below.
        first_line = false;
        // Skip the already scanned first line.
        start = eol;
        const char *i = start;
        while (i < stop)
        {
            char c = *i;
            if (c == '\n')
            {
                start = i+1; // Restart find lines from here.
                prepend_newlines++;
            }
            else if (c != '\r') break;
            i++;
        }
    }

    // Allocate max size of output buffer, it usually becomes smaller
    // when incidental indentation and trailing whitespace is removed.
    size_t n = stop-start+prepend_newlines+append_newlines+1;
    char *output = (char*)malloc(n);
    char *o = output;

    // Insert any necessary prepended newlines.
    while (prepend_newlines) { *o++ = '\n'; prepend_newlines--; }

    // Start scanning the lines from beginning.
    // Now use the found incidental to copy the right parts.
    const char *i = start;

    while (i < stop)
    {
        bool has_nl = find_line(i, stop, &found_indent, &after_last_non_space, &eol);

        if (first_line)
        {
            first_line = false;
        }
        else
        {
            // Skip the incidental indentation, if there is indentation to be skipped.
            // Since empty lines do not count.
            if (*i == ' ')
            {
                size_t n = incidental;
                assert(found_indent >= incidental);
                while (n > 0)
                {
                    char c = *i;
                    assert(c == ' ');
                    i++;
                    n--;
                }
            }
        }
        // Copy content, but not line ending xml whitespace ie space, tab, cr.
        while (i < after_last_non_space) { *o++ = *i++; }

        if (has_nl)
        {
            *o++ = '\n';
        }
        else
        {
            // The final line has no nl, here we copy any ending spaces as well!
            while (i < eol) { *o++ = *i++; }
        }
        i = eol;
    }

    // Insert any necessary appended newlines.
    while (append_newlines) { *o++ = '\n'; append_newlines--; }
    *o++ = 0;
    size_t real_size = o-output;
    output = (char*)realloc(output, real_size);
    return output;
}

void xmqSetupParseCallbacksNoop(XMQParseCallbacks *callbacks)
{
    memset(callbacks, 0, sizeof(*callbacks));

#define X(TYPE) callbacks->handle_##TYPE = NULL;
LIST_OF_XMQ_TOKENS
#undef X

    callbacks->magic_cookie = MAGIC_COOKIE;
}

#define WRITE_ARGS(...) state->output_settings->content.write(state->output_settings->content.writer_state, __VA_ARGS__)

#define X(TYPE) void debug_token_##TYPE(XMQParseState*state,size_t line,size_t col,const char*start,const char*stop,const char*suffix) { \
    WRITE_ARGS("["#TYPE, NULL); \
    if (state->simulated) { WRITE_ARGS(" SIM", NULL); } \
    WRITE_ARGS(" \"", NULL); \
    char *tmp = xmq_quote_as_c(start, stop, false);   \
    WRITE_ARGS(tmp, NULL); \
    free(tmp); \
    WRITE_ARGS("\"", NULL); \
    char buf[32]; \
    snprintf(buf, 32, " %zu:%zu]", line, col); \
    buf[31] = 0; \
    WRITE_ARGS(buf, NULL); \
};
LIST_OF_XMQ_TOKENS
#undef X

void xmqSetupParseCallbacksDebugTokens(XMQParseCallbacks *callbacks)
{
    memset(callbacks, 0, sizeof(*callbacks));
#define X(TYPE) callbacks->handle_##TYPE = debug_token_##TYPE ;
LIST_OF_XMQ_TOKENS
#undef X

    callbacks->done = add_nl;

    callbacks->magic_cookie = MAGIC_COOKIE;
}

void debug_content_value(XMQParseState *state,
                         size_t line,
                         size_t start_col,
                         const char *start,
                         const char *stop,
                         const char *suffix)
{
    char *tmp = xmq_quote_as_c(start, stop, false);
    WRITE_ARGS("{value \"", NULL);
    WRITE_ARGS(tmp, NULL);
    WRITE_ARGS("\"}", NULL);
    free(tmp);
}


void debug_content_quote(XMQParseState *state,
                         size_t line,
                         size_t start_col,
                         const char *start,
                         const char *stop,
                         const char *suffix)
{
    char *trimmed = xmq_un_quote(start, stop, true, true);
    char *tmp = xmq_quote_as_c(trimmed, trimmed+strlen(trimmed), false);
    WRITE_ARGS("{quote \"", NULL);
    WRITE_ARGS(tmp, NULL);
    WRITE_ARGS("\"}", NULL);
    free(tmp);
    free(trimmed);
}

void debug_content_comment(XMQParseState *state,
                           size_t line,
                           size_t start_col,
                           const char *start,
                           const char *stop,
                           const char *suffix)
{
    char *trimmed = xmq_un_comment(start, stop);
    char *tmp = xmq_quote_as_c(trimmed, trimmed+strlen(trimmed), false);
    WRITE_ARGS("{comment \"", NULL);
    WRITE_ARGS(tmp, NULL);
    WRITE_ARGS("\"}", NULL);
    free(tmp);
    free(trimmed);
}

void xmqSetupParseCallbacksDebugContent(XMQParseCallbacks *callbacks)
{
    memset(callbacks, 0, sizeof(*callbacks));
    callbacks->handle_element_value_text = debug_content_value;
    callbacks->handle_attr_value_text = debug_content_value;
    callbacks->handle_quote = debug_content_quote;
    callbacks->handle_comment = debug_content_comment;
    callbacks->handle_element_value_quote = debug_content_quote;
    callbacks->handle_element_value_compound_quote = debug_content_quote;
    callbacks->handle_attr_value_quote = debug_content_quote;
    callbacks->handle_attr_value_compound_quote = debug_content_quote;
    callbacks->done = add_nl;

    callbacks->magic_cookie = MAGIC_COOKIE;
}

void xmqSetupParseCallbacksColorizeTokens(XMQParseCallbacks *callbacks, XMQRenderFormat render_format)
{
    memset(callbacks, 0, sizeof(*callbacks));

#define X(TYPE) callbacks->handle_##TYPE = tokenize_##TYPE ;
LIST_OF_XMQ_TOKENS
#undef X

    callbacks->magic_cookie = MAGIC_COOKIE;
}

XMQDoc *xmqNewDoc()
{
    XMQDoc *d = (XMQDoc*)malloc(sizeof(XMQDoc));
    memset(d, 0, sizeof(XMQDoc));
    d->docptr_.xml = xmlNewDoc((const xmlChar*)"1.0");
    return d;
}

void *xmqGetImplementationDoc(XMQDoc *doq)
{
    return doq->docptr_.xml;
}

void xmqSetImplementationDoc(XMQDoc *doq, void *doc)
{
    doq->docptr_.xml = (xmlDocPtr)doc;
}

void xmqSetDocSourceName(XMQDoc *doq, const char *source_name)
{
    if (source_name)
    {
        char *buf = (char*)malloc(strlen(source_name)+1);
        strcpy(buf, source_name);
        doq->source_name_ = buf;
    }
}

const char *xmqGetDocSourceName(XMQDoc *doq)
{
    return doq->source_name_;
}

XMQContentType xmqGetOriginalContentType(XMQDoc *doq)
{
    return doq->original_content_type_;
}

size_t xmqGetOriginalSize(XMQDoc *doq)
{
    return doq->original_size_;
}

void xmqSetOriginalSize(XMQDoc *doq, size_t size)
{
    doq->original_size_ = size;
}

XMQNode *xmqGetRootNode(XMQDoc *doq)
{
    return &doq->root_;
}

void xmqFreeParseCallbacks(XMQParseCallbacks *cb)
{
    free(cb);
}

void xmqFreeParseState(XMQParseState *state)
{
    if (!state) return;

    free(state->source_name);
    state->source_name = NULL;
    free(state->generated_error_msg);
    state->generated_error_msg = NULL;
    if (state->generating_error_msg)
    {
        free_membuffer_and_free_content(state->generating_error_msg);
        state->generating_error_msg = NULL;
    }
    stack_free(state->element_stack);
    state->element_stack = NULL;
    // Settings are not freed here.
    state->output_settings = NULL;

    if (state->yaep_tmp_rhs_) free(state->yaep_tmp_rhs_);
    state->yaep_tmp_rhs_ = NULL;
    if (state->yaep_tmp_transl_) free(state->yaep_tmp_transl_);
    state->yaep_tmp_transl_ = NULL;
    if (state->yaep_tmp_marks_) free(state->yaep_tmp_marks_);
    state->yaep_tmp_marks_ = NULL;

    if (state->ixml_rules) vector_free_and_values(state->ixml_rules, (FreeFuncPtr)free_ixml_rule);
    state->ixml_rules = NULL;

    if (state->ixml_terminals_map) hashmap_free_and_values(state->ixml_terminals_map, (FreeFuncPtr)free_ixml_terminal);
    state->ixml_terminals_map = NULL;

    if (state->ixml_non_terminals_map) hashmap_free(state->ixml_non_terminals_map);
    state->ixml_non_terminals_map = NULL;

    if (state->ixml_non_terminals) vector_free_and_values(state->ixml_non_terminals, (FreeFuncPtr)free_ixml_nonterminal);
    state->ixml_non_terminals = NULL;

    if (state->ixml_rule_stack) stack_free(state->ixml_rule_stack);
    state->ixml_rule_stack = NULL;

    if (state->used_unicodes)
    {
        free(state->used_unicodes);
        state->used_unicodes = NULL;
    }
    free(state);
}

void xmqFreeDoc(XMQDoc *doq)
{
    if (!doq) return;
    if (doq->source_name_)
    {
        debug("xmq=", "freeing source name");
        free((void*)doq->source_name_);
        doq->source_name_ = NULL;
    }
    if (doq->error_)
    {
        debug("xmq=", "freeing error message");
        free((void*)doq->error_);
        doq->error_ = NULL;
    }
    if (doq->docptr_.xml)
    {
        debug("xmq=", "freeing xml doc");
        xmlFreeDoc(doq->docptr_.xml);
        doq->docptr_.xml = NULL;
    }
    if (doq->yaep_grammar_)
    {
        yaepFreeGrammar (doq->yaep_parse_run_, doq->yaep_grammar_);
        yaepFreeParseRun (doq->yaep_parse_run_);
        xmqFreeParseState(doq->yaep_parse_state_);
        doq->yaep_grammar_ = NULL;
        doq->yaep_parse_run_ = NULL;
        doq->yaep_parse_state_ = NULL;
    }

    debug("xmq=", "freeing xmq doc");
    free(doq);
}

const char *skip_any_potential_bom(const char *start, const char *stop)
{
    if (start + 3 < stop)
    {
        int a = (unsigned char)*(start+0);
        int b = (unsigned char)*(start+1);
        int c = (unsigned char)*(start+2);
        if (a == 0xef && b == 0xbb && c == 0xbf)
        {
            // The UTF8 bom, this is fine, just skip it.
            return start+3;
        }
    }

    if (start+2 < stop)
    {
        unsigned char a = *(start+0);
        unsigned char b = *(start+1);
        if ((a == 0xff && b == 0xfe) ||
            (a == 0xfe && b == 0xff))
        {
            // We cannot handle UTF-16 files.
            return NULL;
        }
    }

    // Assume content, no bom.
    return start;
}

bool xmqParseBuffer(XMQDoc *doq, const char *start, const char *stop, const char *implicit_root, int flags)
{
    bool rc = true;
    XMQOutputSettings *output_settings = xmqNewOutputSettings();
    XMQParseCallbacks *parse = xmqNewParseCallbacks();

    xmq_setup_parse_callbacks(parse);

    XMQParseState *state = xmqNewParseState(parse, output_settings);
    state->merge_text = !(flags & XMQ_FLAG_NOMERGE);
    state->doq = doq;
    xmqSetStateSourceName(state, doq->source_name_);

    if (implicit_root != NULL && implicit_root[0] == 0) implicit_root = NULL;

    state->implicit_root = implicit_root;

    stack_push(state->element_stack, doq->docptr_.xml);
    // Now state->element_stack->top->data points to doq->docptr_;
    state->element_last = NULL; // Will be set when the first node is created.
    // The doc root will be set when the first element node is created.

    // Time to tokenize the buffer and invoke the parse callbacks.
    xmqTokenizeBuffer(state, start, stop);

    if (xmqStateErrno(state))
    {
        rc = false;
        doq->errno_ = xmqStateErrno(state);
        doq->error_ = build_error_message("%s\n", xmqStateErrorMsg(state));
    }

    xmqFreeParseState(state);
    xmqFreeParseCallbacks(parse);
    xmqFreeOutputSettings(output_settings);

    return rc;
}

bool xmqParseFile(XMQDoc *doq, const char *file, const char *implicit_root, int flags)
{
    bool ok = true;
    char *buffer = NULL;
    size_t fsize = 0;
    XMQContentType content = XMQ_CONTENT_XMQ;
    size_t block_size = 0;
    size_t n = 0;

    xmqSetDocSourceName(doq, file);

    FILE *f = fopen(file, "rb");
    if (!f) {
        doq->errno_ = XMQ_ERROR_CANNOT_READ_FILE;
        doq->error_ = build_error_message("xmq: %s: No such file or directory\n", file);
        ok = false;
        goto exit;
    }

    fseek(f, 0, SEEK_END);
    fsize = ftell(f);
    fseek(f, 0, SEEK_SET);

    buffer = (char*)malloc(fsize + 1);
    if (!buffer)
    {
        doq->errno_ = XMQ_ERROR_OOM;
        doq->error_ = build_error_message("xmq: %s: File too big, out of memory\n", file);
        ok = false;
        goto exit;
    }

    block_size = fsize;
    if (block_size > 10000) block_size = 10000;
    n = 0;
    do {
        // We need to read smaller blocks because of a bug in Windows C-library..... blech.
        if (n + block_size > fsize) block_size = fsize - n;
        size_t r = fread(buffer+n, 1, block_size, f);
        debug("xmq=", "read %zu bytes total %zu", r, n);
        if (!r) break;
        n += r;
    } while (n < fsize);

    debug("xmq=", "read total %zu bytes", n);

    if (n != fsize)
    {
        ok = false;
        doq->errno_ = XMQ_ERROR_CANNOT_READ_FILE;
        goto exit;
    }
    fclose(f);
    buffer[fsize] = 0;

    content = xmqDetectContentType(buffer, buffer+fsize);
    if (content != XMQ_CONTENT_XMQ)
    {
        ok = false;
        doq->errno_ = XMQ_ERROR_NOT_XMQ;
        goto exit;
    }

    ok = xmqParseBuffer(doq, buffer, buffer+fsize, implicit_root, flags);

    exit:

    free(buffer);

    return ok;
}

const char *xmqVersion()
{
    return "4.0.1-modified";
}

void do_whitespace(XMQParseState *state,
                   size_t line,
                   size_t col,
                   const char *start,
                   const char *stop,
                   const char *suffix)
{
}

xmlNodePtr create_quote(XMQParseState *state,
                       size_t l,
                       size_t col,
                       const char *start,
                       const char *stop,
                       const char *suffix,
                       xmlNodePtr parent)
{
    char *trimmed = (state->no_trim_quotes)?strndup(start, stop-start):xmq_un_quote(start, stop, true, true);
    xmlNodePtr n = xmlNewDocText(state->doq->docptr_.xml, (const xmlChar *)trimmed);
    if (state->merge_text)
    {
        n = xmlAddChild(parent, n);
    }
    else
    {
        // I want to prevent merging of this new text node with previous text nodes....
        // Alas there is no such setting in libxml2 so I perform the addition explicit here.
        // Check the source for xmlAddChild.
        n->parent = parent;
        if (parent->children == NULL)
        {
            parent->children = n;
            parent->last = n;
        }
        else
        {
            xmlNodePtr prev = parent->last;
	    prev->next = n;
            n->prev = prev;
            parent->last = n;
        }
    }
    free(trimmed);
    return n;
}

void do_quote(XMQParseState *state,
              size_t l,
              size_t col,
              const char *start,
              const char *stop,
              const char *suffix)
{
    state->element_last = create_quote(state, l, col, start, stop, suffix,
                                       (xmlNode*)state->element_stack->top->data);
}

xmlNodePtr create_entity(XMQParseState *state,
                         size_t l,
                         size_t c,
                         const char *start,
                         const char *stop,
                         const char *suffix,
                         xmlNodePtr parent)
{
    char *tmp = strndup(start, stop-start);
    xmlNodePtr n = NULL;
    if (tmp[1] == '#')
    {
        // Character entity.
        if (!state->merge_text)
        {
            // Do not merge with surrounding text.
            n = xmlNewCharRef(state->doq->docptr_.xml, (const xmlChar *)tmp);
        }
        else
        {
            // Make inte text that will be merged.
            UTF8Char uni;
            int uc = 0;
            if (tmp[2] == 'x') uc = strtol(tmp+3, NULL, 16);
            else uc = strtol(tmp+2, NULL, 10);
            size_t len = encode_utf8(uc, &uni);
            char buf[len+1];
            memcpy(buf, uni.bytes, len);
            buf[len] = 0;
            n = xmlNewDocText(state->doq->docptr_.xml, (xmlChar*)buf);
        }
    }
    else
    {
        // Named references are kept as is.
        n = xmlNewReference(state->doq->docptr_.xml, (const xmlChar *)tmp);
    }
    n = xmlAddChild(parent, n);
    free(tmp);

    return n;
}

void do_entity(XMQParseState *state,
               size_t l,
               size_t c,
               const char *start,
               const char *stop,
               const char *suffix)
{
    state->element_last = create_entity(state, l, c, start, stop, suffix, (xmlNode*)state->element_stack->top->data);
}

void do_comment(XMQParseState*state,
                size_t line,
                size_t col,
                const char *start,
                const char *stop,
                const char *suffix)
{
    xmlNodePtr parent = (xmlNode*)state->element_stack->top->data;
    char *trimmed = (state->no_trim_quotes)?strndup(start, stop-start):xmq_un_comment(start, stop);
    xmlNodePtr n = xmlNewDocComment(state->doq->docptr_.xml, (const xmlChar *)trimmed);

    if (state->add_pre_node_before)
    {
        // Insert comment before this node.
        xmlAddPrevSibling((xmlNodePtr)state->add_pre_node_before, n);
    }
    else if (state->add_post_node_after)
    {
        // Insert comment after this node.
        xmlAddNextSibling((xmlNodePtr)state->add_post_node_after, n);
    }
    else
    {
        xmlAddChild(parent, n);
    }
    state->element_last = n;
    free(trimmed);
}

void do_comment_continuation(XMQParseState*state,
                             size_t line,
                             size_t col,
                             const char *start,
                             const char *stop,
                             const char *suffix)
{
    xmlNodePtr last = (xmlNode*)state->element_last;
    // We have ///* alfa *///* beta *///* gamma *///
    // and this function is invoked with "* beta *///"
    const char *i = stop-1;
    // Count n slashes from the end
    size_t n = 0;
    while (i > start && *i == '/') { n++; i--; }
    // Since we know that we are invoked pointing into a buffer with /// before start, we
    // can safely do start-n.
    char *trimmed = xmq_un_comment(start-n, stop);
    size_t l = strlen(trimmed);
    char *tmp = (char*)malloc(l+2);
    tmp[0] = '\n';
    memcpy(tmp+1, trimmed, l);
    tmp[l+1] = 0;
    xmlNodeAddContent(last, (const xmlChar *)tmp);
    free(trimmed);
    free(tmp);
}

void do_element_value_text(XMQParseState *state,
                           size_t line,
                           size_t col,
                           const char *start,
                           const char *stop,
                           const char *suffix)
{
    if (state->parsing_pi)
    {
        char *content = potentially_add_leading_ending_space(start, stop);
        xmlNodePtr n = (xmlNodePtr)xmlNewPI((xmlChar*)state->pi_name, (xmlChar*)content);
        xmlNodePtr parent = (xmlNode*)state->element_stack->top->data;
        xmlAddChild(parent, n);
        free(content);

        state->parsing_pi = false;
        free((char*)state->pi_name);
        state->pi_name = NULL;
    }
    else if (state->parsing_doctype)
    {
        size_t l = stop-start;
        char *tmp = (char*)malloc(l+1);
        memcpy(tmp, start, l);
        tmp[l] = 0;
        state->doq->docptr_.xml->intSubset = xmlNewDtd(state->doq->docptr_.xml, (xmlChar*)tmp, NULL, NULL);
        xmlNodePtr n = (xmlNodePtr)state->doq->docptr_.xml->intSubset;
        xmlNodePtr parent = (xmlNode*)state->element_stack->top->data;
        xmlAddChild(parent, n);
        free(tmp);

        state->parsing_doctype = false;
        state->doctype_found = true;
    }
    else
    {
        xmlNodePtr n = xmlNewDocTextLen(state->doq->docptr_.xml, (const xmlChar *)start, stop-start);
        xmlAddChild((xmlNode*)state->element_last, n);
    }
}

void do_element_value_quote(XMQParseState *state,
                            size_t line,
                            size_t col,
                            const char *start,
                            const char *stop,
                            const char *suffix)
{
    char *trimmed = (state->no_trim_quotes)?strndup(start, stop-start):xmq_un_quote(start, stop, true, true);
    if (state->parsing_pi)
    {
        char *content = potentially_add_leading_ending_space(trimmed, trimmed+strlen(trimmed));
        xmlNodePtr n = (xmlNodePtr)xmlNewPI((xmlChar*)state->pi_name, (xmlChar*)content);
        xmlNodePtr parent = (xmlNode*)state->element_stack->top->data;
        xmlAddChild(parent, n);
        state->parsing_pi = false;
        free((char*)state->pi_name);
        state->pi_name = NULL;
        free(content);
    }
    else if (state->parsing_doctype)
    {
        // "<!DOCTYPE "=10  ">"=1 NULL=1
        size_t tn = strlen(trimmed);
        size_t n = tn+10+1+12;
        char *buf = (char*)malloc(n);
        strcpy(buf, "<!DOCTYPE ");
        memcpy(buf+10, trimmed, tn);
        memcpy(buf+10+tn, "><foo></foo>", 12);
        buf[n-1] = 0;
        xmlDtdPtr dtd = parse_doctype_raw(state->doq, buf, buf+n-1);
        if (!dtd)
        {
            state->error_nr = XMQ_ERROR_BAD_DOCTYPE;
            longjmp(state->error_handler, 1);
        }
        state->doq->docptr_.xml->intSubset = dtd;
        if (state->add_pre_node_before)
        {
            // Insert doctype before this node.
            xmlAddPrevSibling((xmlNodePtr)state->add_pre_node_before, (xmlNodePtr)dtd);
        }
        else
        {
            // Append doctype to document.
            xmlNodePtr parent = (xmlNode*)state->element_stack->top->data;
            xmlAddChild(parent, (xmlNodePtr)dtd);
        }
        state->parsing_doctype = false;
        state->doctype_found = true;
        free(buf);
    }
    else
    {
        xmlNodePtr n = xmlNewDocText(state->doq->docptr_.xml, (const xmlChar *)trimmed);
        xmlAddChild((xmlNode*)state->element_last, n);
    }
    free(trimmed);
}

void do_element_value_entity(XMQParseState *state,
                             size_t line,
                             size_t col,
                             const char *start,
                             const char *stop,
                             const char *suffix)
{
    create_entity(state, line, col, start, stop, suffix, (xmlNode*)state->element_last);
}

void do_element_value_compound_quote(XMQParseState *state,
                                     size_t line,
                                     size_t col,
                                     const char *start,
                                     const char *stop,
                                     const char *suffix)
{
    do_quote(state, line, col, start, stop, suffix);
}

void do_element_value_compound_entity(XMQParseState *state,
                                      size_t line,
                                      size_t col,
                                      const char *start,
                                      const char *stop,
                                      const char *suffix)
{
    do_entity(state, line, col, start, stop, suffix);
}

void do_attr_ns(XMQParseState *state,
                size_t line,
                size_t col,
                const char *start,
                const char *stop,
                const char *suffix)
{
    if (!state->declaring_xmlns)
    {
        // Normal attribute namespace found before the attribute key, eg x:alfa=123 xlink:href=http...
        char *ns = strndup(start, stop-start);
        state->attribute_namespace = ns;
    }
    else
    {
        // This is the first namespace after the xmlns declaration, eg. xmlns:xsl = http....
        // The xsl has already been handled in do_ns_declaration that used suffix
        // to peek ahead to the xsl name.
    }
}

void do_ns_declaration(XMQParseState *state,
                       size_t line,
                       size_t col,
                       const char *start,
                       const char *stop,
                       const char *suffix)
{
    // We found a namespace. It is either a default declaration xmlns=... or xmlns:prefix=...
    //
    // We can see the difference here since the parser will invoke with suffix
    // either pointing to stop (xmlns=) or after stop (xmlns:foo=)
    xmlNodePtr element = (xmlNode*)state->element_stack->top->data;
    xmlNsPtr ns = NULL;
    if (stop == suffix)
    {
        // Stop is the same as suffix, so no prefix has been added.
        // I.e. this is a default namespace, eg: xmlns=uri
        ns = xmlNewNs(element,
                      NULL,
                      NULL);
        debug("xmq=", "create default namespace in element %s", element->name);
        if (!ns)
        {
            // Oups, this element already has a default namespace.
            // This is probably due to multiple xmlns=uri declarations. Is this an error or not?
            xmlNsPtr *list = xmlGetNsList(state->doq->docptr_.xml,
                                          element);
            for (int i = 0; list[i]; ++i)
            {
                if (!list[i]->prefix)
                {
                    ns = list[i];
                    break;
                }
            }
            free(list);
        }
        if (element->ns == NULL)
        {
            debug("xmq=", "set default namespace in element %s prefix=%s href=%s", element->name, ns->prefix, ns->href);
            xmlSetNs(element, ns);
        }
        state->default_namespace = ns;
    }
    else
    {
        // This a new namespace with a prefix. xmlns:prefix=uri
        // Stop points to the colon, suffix points to =.
        // The prefix starts at stop+1.
        size_t len = suffix-(stop+1);
        char *name = strndup(stop+1, len);
        ns = xmlNewNs(element,
                      NULL,
                      (const xmlChar *)name);

        if (!ns)
        {
            // Oups, this namespace has already been created, for example due to the namespace prefix
            // of the element itself, eg: abc:element(xmlns:abc = uri)
            // Lets pick this ns up and reuse it.
            xmlNsPtr *list = xmlGetNsList(state->doq->docptr_.xml,
                                          element);
            for (int i = 0; list[i]; ++i)
            {
                if (list[i]->prefix && !strcmp((char*)list[i]->prefix, name))
                {
                    ns = list[i];
                    break;
                }
            }
            free(list);
        }
        free(name);
    }

    if (!ns)
    {
        fprintf(stderr, "Internal error: expected namespace to be created/found.\n");
        assert(ns);
    }
    state->declaring_xmlns = true;
    state->declaring_xmlns_namespace = ns;
}

void do_attr_key(XMQParseState *state,
                 size_t line,
                 size_t col,
                 const char *start,
                 const char *stop,
                 const char *suffix)
{
    size_t n = stop - start;
    char *key = (char*)malloc(n+1);
    memcpy(key, start, n);
    key[n] = 0;

    xmlNodePtr parent = (xmlNode*)state->element_stack->top->data;
    xmlAttrPtr attr = NULL;

    if (!state->attribute_namespace)
    {
        // A normal attribute with no namespace.
        attr =  xmlNewProp(parent, (xmlChar*)key, NULL);
    }
    else
    {
        xmlNsPtr ns = xmlSearchNs(state->doq->docptr_.xml,
                                  parent,
                                  (const xmlChar *)state->attribute_namespace);
        if (!ns)
        {
            // The namespaces does not yet exist. Lets create it.. Lets hope it will be declared
            // inside the attributes of this node. Use a temporary href for now.
            ns = xmlNewNs(parent,
                          NULL,
                          (const xmlChar *)state->attribute_namespace);
        }
        attr = xmlNewNsProp(parent, ns, (xmlChar*)key, NULL);
        free(state->attribute_namespace);
        state->attribute_namespace = NULL;
    }

    // The new attribute attr should now be added to the parent elements: properties list.
    // Remember this attr as the last element so that we can set the value.
    state->element_last = attr;

    free(key);
}

void update_namespace_href(XMQParseState *state,
                           xmlNsPtr ns,
                           const char *start,
                           const char *stop)
{
    if (!stop) stop = start+strlen(start);

    char *href = strndup(start, stop-start);
    ns->href = (const xmlChar*)href;
    debug("xmq=", "update namespace prefix=%s with href=%s", ns->prefix, href);

    if (start[0] == 0 && ns == state->default_namespace)
    {
        xmlNodePtr element = (xmlNode*)state->element_stack->top->data;
        debug("xmq=", "remove default namespace in element %s", element->name);
        xmlSetNs(element, NULL);
        state->default_namespace = NULL;
        return;
    }
}

void do_attr_value_text(XMQParseState *state,
                        size_t line,
                        size_t col,
                        const char *start,
                        const char *stop,
                        const char *suffix)
{
    if (state->declaring_xmlns)
    {
        assert(state->declaring_xmlns_namespace);

        update_namespace_href(state, (xmlNsPtr)state->declaring_xmlns_namespace, start, stop);
        state->declaring_xmlns = false;
        state->declaring_xmlns_namespace = NULL;
        return;
    }
    xmlNodePtr n = xmlNewDocTextLen(state->doq->docptr_.xml, (const xmlChar *)start, stop-start);
    xmlAddChild((xmlNode*)state->element_last, n);
}

void do_attr_value_quote(XMQParseState*state,
                         size_t line,
                         size_t col,
                         const char *start,
                         const char *stop,
                         const char *suffix)
{
    if (state->declaring_xmlns)
    {
        char *trimmed = (state->no_trim_quotes)?strndup(start, stop-start):xmq_un_quote(start, stop, true, true);
        update_namespace_href(state, (xmlNsPtr)state->declaring_xmlns_namespace, trimmed, NULL);
        state->declaring_xmlns = false;
        state->declaring_xmlns_namespace = NULL;
        free(trimmed);
        return;
    }
    create_quote(state, line, col, start, stop, suffix, (xmlNode*)state->element_last);
}

void do_attr_value_entity(XMQParseState *state,
                          size_t line,
                          size_t col,
                          const char *start,
                          const char *stop,
                          const char *suffix)
{
    create_entity(state, line, col, start, stop, suffix, (xmlNode*)state->element_last);
}

void do_attr_value_compound_quote(XMQParseState *state,
                                  size_t line,
                                  size_t col,
                                  const char *start,
                                  const char *stop,
                                  const char *suffix)
{
    do_quote(state, line, col, start, stop, suffix);
}

void do_attr_value_compound_entity(XMQParseState *state,
                                             size_t line,
                                             size_t col,
                                             const char *start,
                                             const char *stop,
                                             const char *suffix)
{
    do_entity(state, line, col, start, stop, suffix);
}

void create_node(XMQParseState *state, const char *start, const char *stop)
{
    size_t len = stop-start;
    char *name = strndup(start, len);

    if (!strcmp(name, "!DOCTYPE"))
    {
        state->parsing_doctype = true;
    }
    else if (name[0] == '?')
    {
        state->parsing_pi = true;
        state->pi_name = strdup(name+1); // Drop the ?
    }
    else
    {
        xmlNodePtr new_node = xmlNewDocNode(state->doq->docptr_.xml, NULL, (const xmlChar *)name, NULL);
        if (state->element_last == NULL)
        {
            if (!state->implicit_root || !strcmp(name, state->implicit_root))
            {
                // There is no implicit root, or name is the same as the implicit root.
                // Then create the root node with name.
                state->element_last = new_node;
                xmlDocSetRootElement(state->doq->docptr_.xml, new_node);
                state->doq->root_.node = new_node;
            }
            else
            {
                // We have an implicit root and it is different from name.
                xmlNodePtr root = xmlNewDocNode(state->doq->docptr_.xml, NULL, (const xmlChar *)state->implicit_root, NULL);
                state->element_last = root;
                xmlDocSetRootElement(state->doq->docptr_.xml, root);
                state->doq->root_.node = root;
                stack_push(state->element_stack, state->element_last);
            }
        }
        xmlNodePtr parent = (xmlNode*)state->element_stack->top->data;
        xmlAddChild(parent, new_node);

        if (state->element_namespace)
        {
            // Have a namespace before the element name, eg abc:work
            xmlNsPtr ns = xmlSearchNs(state->doq->docptr_.xml,
                                      new_node,
                                      (const xmlChar *)state->element_namespace);
            if (!ns)
            {
                // The namespaces does not yet exist. Lets hope it will be declared
                // inside the attributes of this node. Use a temporary href for now.
                ns = xmlNewNs(new_node,
                              NULL,
                              (const xmlChar *)state->element_namespace);
                debug("xmq=", "created namespace prefix=%s in element %s", state->element_namespace, name);
            }
            debug("xmq=", "setting namespace prefix=%s for element %s", state->element_namespace, name);
            xmlSetNs(new_node, ns);
            free(state->element_namespace);
            state->element_namespace = NULL;
        }
        else if (state->default_namespace)
        {
            // We have a default namespace.
            xmlNsPtr ns = (xmlNsPtr)state->default_namespace;
            assert(ns->prefix == NULL);
            debug("xmq=", "set default namespace with href=%s for element %s", ns->href, name);
            xmlSetNs(new_node, ns);
        }

        state->element_last = new_node;
    }

    free(name);
}

void do_element_ns(XMQParseState *state,
                   size_t line,
                   size_t col,
                   const char *start,
                   const char *stop,
                   const char *suffix)
{
    char *ns = strndup(start, stop-start);
    state->element_namespace = ns;
}

void do_ns_colon(XMQParseState *state,
                 size_t line,
                 size_t col,
                 const char *start,
                 const char *stop,
                 const char *suffix)
{
}

void do_element_name(XMQParseState *state,
                     size_t line,
                     size_t col,
                     const char *start,
                     const char *stop,
                     const char *suffix)
{
    create_node(state, start, stop);
}

void do_element_key(XMQParseState *state,
                    size_t line,
                    size_t col,
                    const char *start,
                    const char *stop,
                    const char *suffix)
{
    create_node(state, start, stop);
}

void do_equals(XMQParseState *state,
               size_t line,
               size_t col,
               const char *start,
               const char *stop,
               const char *suffix)
{
}

void do_brace_left(XMQParseState *state,
                   size_t line,
                   size_t col,
                   const char *start,
                   const char *stop,
                   const char *suffix)
{
    stack_push(state->element_stack, state->element_last);
}

void do_brace_right(XMQParseState *state,
                    size_t line,
                    size_t col,
                    const char *start,
                    const char *stop,
                    const char *suffix)
{
    state->element_last = stack_pop(state->element_stack);
}

void do_apar_left(XMQParseState *state,
                 size_t line,
                 size_t col,
                 const char *start,
                 const char *stop,
                 const char *suffix)
{
    stack_push(state->element_stack, state->element_last);
}

void do_apar_right(XMQParseState *state,
                  size_t line,
                  size_t col,
                  const char *start,
                  const char *stop,
                  const char *suffix)
{
    state->element_last = stack_pop(state->element_stack);
}

void do_cpar_left(XMQParseState *state,
                  size_t line,
                  size_t col,
                  const char *start,
                  const char *stop,
                  const char *suffix)
{
    stack_push(state->element_stack, state->element_last);
}

void do_cpar_right(XMQParseState *state,
                   size_t line,
                   size_t col,
                   const char *start,
                   const char *stop,
                   const char *suffix)
{
    state->element_last = stack_pop(state->element_stack);
}

void xmq_setup_parse_callbacks(XMQParseCallbacks *callbacks)
{
    memset(callbacks, 0, sizeof(*callbacks));

#define X(TYPE) callbacks->handle_##TYPE = do_##TYPE;
LIST_OF_XMQ_TOKENS
#undef X

    callbacks->magic_cookie = MAGIC_COOKIE;
}

void copy_quote_settings_from_output_settings(XMQQuoteSettings *qs, XMQOutputSettings *os)
{
    qs->indentation_space = os->indentation_space;
    qs->explicit_space = os->explicit_space;
    qs->explicit_nl = os->explicit_nl;
    qs->prefix_line = os->prefix_line;
    qs->postfix_line = os->prefix_line;
    qs->compact = os->compact;
    qs->allow_json_quotes = os->allow_json_quotes;
}

void xmq_print_xml(XMQDoc *doq, XMQOutputSettings *output_settings)
{
    xmq_fixup_html_before_writeout(doq);

    xmlChar *buffer;
    int size;
    xmlDocDumpMemoryEnc(doq->docptr_.xml,
                        &buffer,
                        &size,
                        "utf-8");

    membuffer_reuse(output_settings->output_buffer,
                    (char*)buffer,
                    size);

    debug("xmq=", "xmq_print_xml wrote %zu bytes", size);
}

void xmq_print_html(XMQDoc *doq, XMQOutputSettings *output_settings)
{
    xmq_fixup_html_before_writeout(doq);
    xmlOutputBufferPtr out = xmlAllocOutputBuffer(NULL);
    if (out)
    {
        htmlDocContentDumpOutput(out, doq->docptr_.html, "utf8");
        const xmlChar *buffer = xmlBufferContent((xmlBuffer *)out->buffer);
        MemBuffer *membuf = output_settings->output_buffer;
        membuffer_append(membuf, (char*)buffer);
        debug("xmq=", "xmq_print_html wrote %zu bytes", membuf->used_);
        xmlOutputBufferClose(out);
    }
    /*
    xmlNodePtr child = doq->docptr_.xml->children;
    xmlBuffer *buffer = xmlBufferCreate();
    while (child != NULL)
    {
        xmlNodeDump(buffer, doq->docptr_.xml, child, 0, 0);
        child = child->next;
        }
    const char *c = (const char *)xmlBufferContent(out);
    fputs(c, stdout);
    fputs("\n", stdout);
    xmlBufferFree(buffer);
    */
}

void xmq_print_json(XMQDoc *doq, XMQOutputSettings *os)
{
    if (doq == NULL || doq->docptr_.xml == NULL) return;
    xmq_fixup_json_before_writeout(doq);

    void *first = doq->docptr_.xml->children;
    if (!doq || !first) return;
    void *last = doq->docptr_.xml->last;

    XMQPrintState ps = {};
    ps.pre_nodes = stack_create();
    ps.post_nodes = stack_create();
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;
    ps.doq = doq;
    if (os->compact) os->escape_newlines = true;
    ps.output_settings = os;
    assert(os->content.write);

    // Find any leading (doctype/comments) and ending (comments) nodes and store in pre_nodes and post_nodes inside ps.
    // Adjust the first and last pointer.
    collect_leading_ending_comments_doctype(&ps, (xmlNode**)&first, (xmlNode**)&last);
    json_print_object_nodes(&ps, NULL, (xmlNode*)first, (xmlNode*)last);
    write(writer_state, "\n", NULL);

    stack_free(ps.pre_nodes);
    stack_free(ps.post_nodes);
}

void text_print_node(XMQPrintState *ps, xmlNode *node)
{
    XMQOutputSettings *output_settings = ps->output_settings;
    XMQWrite write = output_settings->content.write;
    void *writer_state = output_settings->content.writer_state;

    if (is_content_node(node))
    {
        const char *content = xml_element_content(node);
        write(writer_state, content, NULL);
    }
    else if (is_entity_node(node))
    {
        const char *name = xml_element_name(node);
        write(writer_state, "<ENTITY>", NULL);
        write(writer_state, name, NULL);
    }
    else if (is_element_node(node))
    {
        text_print_nodes(ps, node->children);
    }
}

void text_print_nodes(XMQPrintState *ps, xmlNode *from)
{
    xmlNode *i = from;

    while (i)
    {
        text_print_node(ps, i);
        i = xml_next_sibling(i);
    }
}

void xmq_print_text(XMQDoc *doq, XMQOutputSettings *os)
{
    void *first = doq->docptr_.xml->children;
    if (!doq || !first) return;

    XMQPrintState ps = {};
    ps.doq = doq;
    ps.output_settings = os;

    text_print_nodes(&ps, (xmlNode*)first);
}

void cline_print_xpath(XMQPrintState *ps, xmlNode *node)
{
    XMQOutputSettings *output_settings = ps->output_settings;
    XMQWrite write = output_settings->content.write;
    void *writer_state = output_settings->content.writer_state;

    if (!node) return;
    if (node->parent)
    {
        cline_print_xpath(ps, node->parent);
        write(writer_state, "/", NULL);
    }
    write(writer_state, xml_element_name(node), NULL);
}

void cline_print_node(XMQPrintState *ps, xmlNode *node)
{
    XMQOutputSettings *output_settings = ps->output_settings;
    XMQWrite write = output_settings->content.write;
    void *writer_state = output_settings->content.writer_state;

    if (has_attributes(node))
    {
        cline_print_attributes(ps, node);
    }
    if (is_content_node(node))
    {
        cline_print_xpath(ps, node->parent);
        write(writer_state, "=", NULL);
        const char *content = xml_element_content(node);
        char *q = xmq_quote_as_c(content, NULL, false);
        write(writer_state, "\"", NULL);
        write(writer_state, q, NULL);
        write(writer_state, "\"", NULL);
        free(q);
        write(writer_state, "\n", NULL);
    }
    else if (node->children)
    {
        cline_print_nodes(ps, node->children);
    }
    else if (is_element_node(node))
    {
        cline_print_xpath(ps, node);
        write(writer_state, "=\"\"\n", NULL);
    }
    else if (is_entity_node(node))
    {
    }
}

void cline_print_nodes(XMQPrintState *ps, xmlNode *from)
{
    xmlNode *i = from;

    while (i)
    {
        cline_print_node(ps, i);
        i = xml_next_sibling(i);
    }
}

void cline_print_attributes(XMQPrintState *ps, xmlNode *node)
{
    xmlAttr *i = xml_first_attribute(node);

    while (i)
    {
        cline_print_attr(ps, i);
        i = xml_next_attribute(i);
    }
}

void cline_print_attr(XMQPrintState *ps, xmlAttr *a)
{
    cline_print_xpath(ps, a->parent);

    const char *key;
    const char *prefix;
    size_t total_u_len;
    attr_strlen_name_prefix(a, &key, &prefix, &total_u_len);

    print_utf8(ps, COLOR_none, 1, "/@", NULL);
    if (prefix)
    {
        print_utf8(ps, COLOR_none, 2, prefix, NULL, ":", NULL);
    }
    print_utf8(ps, COLOR_none, 2, key, NULL, "=", NULL);

    if (a->children != NULL)
    {
        char *value = (char*)xmlNodeListGetString(a->doc, a->children, 1);
        char *quoted_value = xmq_quote_as_c(value, value+strlen(value), true);
        print_utf8(ps, COLOR_none, 1, quoted_value, NULL);
        free(quoted_value);
        xmlFree(value);
    }
    else
    {
        print_utf8(ps, COLOR_none, 1, "\"\"", NULL);
    }

    print_utf8(ps, COLOR_none, 1, "\n", NULL);
}

void xmq_print_clines(XMQDoc *doq, XMQOutputSettings *os)
{
    void *first = doq->docptr_.xml->children;
    if (!doq || !first) return;

    XMQPrintState ps = {};
    ps.doq = doq;
    ps.output_settings = os;

    cline_print_nodes(&ps, (xmlNode*)first);
}

void xmq_print_xmq(XMQDoc *doq, XMQOutputSettings *os)
{
    if (doq == NULL || doq->docptr_.xml == NULL) return;
    void *first = doq->docptr_.xml->children;
    if (!doq || !first) return;
    void *last = doq->docptr_.xml->last;

    XMQPrintState ps = {};
    ps.doq = doq;
    if (os->compact) os->escape_newlines = true;
    ps.output_settings = os;
    assert(os->content.write);

    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;
    XMQTheme *theme = os->theme;

    if (theme->document.pre) write(writer_state, theme->document.pre, NULL);
    if (theme->header.pre) write(writer_state, theme->header.pre, NULL);
    if (theme->style.pre) write(writer_state, theme->style.pre, NULL);
    if (theme->header.post) write(writer_state, theme->header.post, NULL);
    if (theme->body.pre) write(writer_state, theme->body.pre, NULL);

    if (theme->content.pre) write(writer_state, theme->content.pre, NULL);
    print_nodes(&ps, (xmlNode*)first, (xmlNode*)last, 0);
    if (theme->content.post) write(writer_state, theme->content.post, NULL);

    if (theme->body.post) write(writer_state, theme->body.post, NULL);
    if (theme->document.post) write(writer_state, theme->document.post, NULL);

    write(writer_state, "\n", NULL);
}

void xmqPrint(XMQDoc *doq, XMQOutputSettings *output_settings)
{
    if (output_settings->output_format == XMQ_CONTENT_XML)
    {
        xmq_print_xml(doq, output_settings);
    }
    else if (output_settings->output_format == XMQ_CONTENT_HTML)
    {
        xmq_print_html(doq, output_settings);
    }
    else if (output_settings->output_format == XMQ_CONTENT_JSON)
    {
        xmq_print_json(doq, output_settings);
    }
    else if (output_settings->output_format == XMQ_CONTENT_TEXT)
    {
        xmq_print_text(doq, output_settings);
    }
    else if (output_settings->output_format == XMQ_CONTENT_CLINES)
    {
        xmq_print_clines(doq, output_settings);
    }
    else
    {
        xmq_print_xmq(doq, output_settings);
    }

    if (output_settings->output_buffer &&
        output_settings->output_buffer_start &&
        output_settings->output_buffer_stop)
    {
        membuffer_append_null(output_settings->output_buffer);
        size_t size = membuffer_used(output_settings->output_buffer);
        char *buffer = free_membuffer_but_return_trimmed_content(output_settings->output_buffer);
        *output_settings->output_buffer_start = buffer;
        *output_settings->output_buffer_stop = buffer+size;
        if (output_settings->output_skip)
        {
            *output_settings->output_skip = 0;
            if (output_settings->output_format == XMQ_CONTENT_XML && output_settings->omit_decl)
            {
                // Skip <?xml version="1.0" encoding="utf-8"?>\n
                *output_settings->output_skip = 39;
            }
        }
    }
}

// Use an internal bit for signaling comment trimming.
#define TRIM_COMMENT 65536
// Use an internal bit for signaling treating tabs as part of incidental indentation.
#define TRIM_TABS    65536*2

void trim_text_node(xmlNode *node, int flags)
{
    // If node has whitespace preserve set, then do not trim.
    //if (xmlNodeGetSpacePreserve (node)) return;

    const char *content = xml_element_content(node);
    // We remove any all whitespace node.
    // This ought to have been removed with XML_NOBLANKS alas that does not always happen.
    // Perhaps because libxml thinks that some of these are signficant whitespace.
    //
    // However we cannot really figure out exactly what is significant and what is not from
    // the default trimming. We will over-trim when going from html to htmq unless
    // --trim=none is used.
    if (is_all_xml_whitespace(content))
    {
        xmlUnlinkNode(node);
        xmlFreeNode(node);
        return;
    }
    // This is not entirely whitespace, now use the xmq_un_quote function to remove any incidental indentation.
    // Use indent==0 and space==0 to indicate to the unquote function to assume the the first line indent
    // is the same as the second line indent! This is necessary to gracefully handle all the possible xml indentations.
    const char *start = content;
    const char *stop = start+strlen(start);

    if (flags & TRIM_COMMENT)
    {
        // For comments we always remove leading and ending spaces.
        while (start < stop && *start == ' ') start++;
        while (stop > start && *(stop-1) == ' ') stop--;
    }

    char *trimmed = xmq_un_quote(start, stop, false, false);
    if (trimmed[0] == 0)
    {
        xmlUnlinkNode(node);
        xmlFreeNode(node);
        free(trimmed);
        return;
    }
    xmlNodeSetContent(node, (xmlChar*)trimmed);
    free(trimmed);
}

void trim_node(xmlNode *node, int flags)
{
    debug("xmq=", "trim %s", xml_element_type_to_string(node->type));

    if (is_content_node(node))
    {
        trim_text_node(node, flags);
        return;
    }

    if (is_comment_node(node))
    {
        trim_text_node(node, flags | TRIM_COMMENT);
        return;
    }

    // Do not recurse into these
    if (node->type == XML_ENTITY_DECL) return;

    xmlNodePtr i = xml_first_child(node);
    while (i)
    {
        xmlNode *next = xml_next_sibling(i); // i might be freed in trim.
        trim_node(i, flags);
        i = next;
    }
}

void xmqTrimWhitespace(XMQDoc *doq, int flags)
{
    xmlNodePtr i = doq->docptr_.xml->children;
    if (!doq || !i) return;

    while (i)
    {
        xmlNode *next = xml_next_sibling(i); // i might be freed in trim.
        trim_node(i, flags);
        i = next;
    }
}

/*
xmlNode *merge_surrounding_text_nodes(xmlNode *node)
{
    const char *val = (const char *)node->name;
    // Not a hex entity.
    if (val[0] != '#' || val[1] != 'x') return node->next;

    debug("xmq=", "merge hex %s chars %s", val, xml_element_type_to_string(node->type));

    UTF8Char uni;
    int uc = strtol(val+2, NULL, 16);
    size_t len = encode_utf8(uc, &uni);
    char buf[len+1];
    memcpy(buf, uni.bytes, len);
    buf[len] = 0;

    xmlNodePtr prev = node->prev;
    xmlNodePtr next = node->next;
    if (prev && prev->type == XML_TEXT_NODE)
    {
        xmlNodeAddContentLen(prev, (xmlChar*)buf, len);
        xmlUnlinkNode(node);
        xmlFreeNode(node);
        debug("xmq=", "merge left");
    }
    if (next && next->type == XML_TEXT_NODE)
    {
        xmlNodeAddContent(prev, next->content);
        xmlNode *n = next->next;
        xmlUnlinkNode(next);
        xmlFreeNode(next);
        next = n;
        debug("xmq=", "merge right");
    }

    return next;
}

xmlNode *merge_hex_chars_node(xmlNode *node)
{
    if (node->type == XML_ENTITY_REF_NODE)
    {
        return merge_surrounding_text_nodes(node);
    }

    // Do not recurse into these
    if (node->type == XML_ENTITY_DECL) return node->next;

    xmlNodePtr i = xml_first_child(node);
    while (i)
    {
        i = merge_hex_chars_node(i);
    }
    return node->next;
}

void xmqMergeHexCharEntities(XMQDoc *doq)
{
    xmlNodePtr i = doq->docptr_.xml->children;
    if (!doq || !i) return;

    while (i)
    {
        i = merge_hex_chars_node(i);
    }
}
*/

char *escape_xml_comment(const char *comment)
{
    // The escape char is ␐ which is utf8 0xe2 0x90 0x90
    size_t escapes = 0;
    const char *i = comment;
    for (; *i; ++i)
    {
        if (*i == '-' && ( *(i+1) == '-' ||
                           (*(const unsigned char*)(i+1) == 0xe2 &&
                            *(const unsigned char*)(i+2) == 0x90 &&
                            *(const unsigned char*)(i+3) == 0x90)))
        {
            escapes++;
        }
    }

    // If no escapes are needed, return NULL.
    if (!escapes) return NULL;

    size_t len = i-comment;
    size_t new_len = len+escapes*3+1;
    char *tmp = (char*)malloc(new_len);

    i = comment;
    char *j = tmp;
    for (; *i; ++i)
    {
        *j++ = *i;
        if (*i == '-' && ( *(i+1) == '-' ||
                           (*(const unsigned char*)(i+1) == 0xe2 &&
                            *(const unsigned char*)(i+2) == 0x90 &&
                            *(const unsigned char*)(i+3) == 0x90)))
        {
            *j++ = 0xe2;
            *j++ = 0x90;
            *j++ = 0x90;
        }
    }
    *j = 0;

    assert( (size_t)((j-tmp)+1) == new_len);
    return tmp;
}

char *unescape_xml_comment(const char *comment)
{
    // The escape char is ␐ which is utf8 0xe2 0x90 0x90
    size_t escapes = 0;
    const char *i = comment;

    for (; *i; ++i)
    {
        if (*i == '-' && (*(const unsigned char*)(i+1) == 0xe2 &&
                          *(const unsigned char*)(i+2) == 0x90 &&
                          *(const unsigned char*)(i+3) == 0x90))
        {
            escapes++;
        }
    }

    // If no escapes need to be removed, then return NULL.
    if (!escapes) return NULL;

    size_t len = i-comment;
    char *tmp = (char*)malloc(len+1);

    i = comment;
    char *j = tmp;
    for (; *i; ++i)
    {
        *j++ = *i;
        if (*i == '-' && (*(const unsigned char*)(i+1) == 0xe2 &&
                          *(const unsigned char*)(i+2) == 0x90 &&
                          *(const unsigned char*)(i+3) == 0x90))
        {
            // Skip the dle quote character.
            i += 3;
        }
    }
    *j++ = 0;

    size_t new_len = j-tmp;
    tmp = (char*)realloc(tmp, new_len);

    return tmp;
}

void fixup_html(XMQDoc *doq, xmlNode *node, bool inside_cdata_declared)
{
    if (node->type == XML_COMMENT_NODE)
    {
        // When writing an xml comment we must replace --- with -␐-␐-.
        // An already existing -␐- is replaced with -␐␐- etc.
        char *new_content = escape_xml_comment((const char*)node->content);
        if (new_content)
        {
            // Oups, the content contains -- which must be quoted as -␐-␐
            // Likewise, if the content contains -␐-␐ it will be quoted as -␐␐-␐␐
            xmlNodePtr new_node = xmlNewComment((const xmlChar*)new_content);
            xmlReplaceNode(node, new_node);
            xmlFreeNode(node);
            free(new_content);
        }
        return;
    }
    else if (node->type == XML_CDATA_SECTION_NODE)
    {
        // When the html is loaded by the libxml2 parser it creates a cdata
        // node instead of a text node for the style content.
        // If this is allowed to be printed as is, then this creates broken html.
        // I.e. <style><![CDATA[h1{color:red;}]]></style>
        // But we want: <style>h1{color:red;}</style>
        // Workaround until I understand the proper fix, just make it a text node.
        node->type = XML_TEXT_NODE;
    }
    else if (is_entity_node(node) && inside_cdata_declared)
    {
        const char *new_content = (const char*)node->content;
        char buf[2];
        if (!node->content)
        {
            if (node->name[0] == '#')
            {
                int v = atoi(((const char*)node->name)+1);
                buf[0] = v;
                buf[1] = 0;
                new_content = buf;
            }
        }
        xmlNodePtr new_node = xmlNewDocText(doq->docptr_.xml, (const xmlChar*)new_content);
        xmlReplaceNode(node, new_node);
        xmlFreeNode(node);
        return;
    }

    xmlNode *i = xml_first_child(node);
    while (i)
    {
        xmlNode *next = xml_next_sibling(i); // i might be freed in trim.

        bool r = inside_cdata_declared;

        if (i->name &&
            (!strcasecmp((const char*)i->name, "style") ||
             !strcasecmp((const char*)i->name, "script")))
        {
            // The html style and script nodes are declared as cdata nodes.
            // The &#10; will not be decoded, instead remain as is a ruin the style.
            // Since htmq does not care about this distinction, we have to remove any
            // quoting specified in the htmq before entering the cdata declared node.
            r = true;
        }

        fixup_html(doq, i, r);
        i = next;
    }
}

void xmq_fixup_html_before_writeout(XMQDoc *doq)
{
    xmlNodePtr i = doq->docptr_.xml->children;
    if (!doq || !i) return;

    while (i)
    {
        xmlNode *next = xml_next_sibling(i); // i might be freed in fixup_html.
        fixup_html(doq, i, false);
        i = next;
    }
}

char *depths_[64] = {};

const char *indent_depth(int i)
{
    if (i < 0 || i > 63) return "----";
    char *c = depths_[i];
    if (!c)
    {
        c = (char*)malloc(i*4+1);
        memset(c, ' ', i*4);
        c[i*4] = 0;
        depths_[i] = c;
    }
    return c;
}

void free_indent_depths()
{
    for (int i = 0; i < 64; ++i)
    {
        if (depths_[i])
        {
            free(depths_[i]);
            depths_[i] = NULL;
        }
    }
}

const char *xml_element_type_to_string(xmlElementType type)
{
    switch (type)
    {
	case XML_ELEMENT_NODE: return "element";
	case XML_ATTRIBUTE_NODE: return "attribute";
	case XML_TEXT_NODE: return "text";
	case XML_CDATA_SECTION_NODE: return "cdata";
	case XML_ENTITY_REF_NODE: return "entity_ref";
	case XML_ENTITY_NODE: return "entity";
	case XML_PI_NODE: return "pi";
	case XML_COMMENT_NODE: return "comment";
	case XML_DOCUMENT_NODE: return "document";
	case XML_DOCUMENT_TYPE_NODE: return "document_type";
	case XML_DOCUMENT_FRAG_NODE: return "document_frag";
	case XML_NOTATION_NODE: return "notation";
	case XML_HTML_DOCUMENT_NODE: return "html_document";
	case XML_DTD_NODE: return "dtd";
	case XML_ELEMENT_DECL: return "element_decl";
	case XML_ATTRIBUTE_DECL: return "attribute_decl";
	case XML_ENTITY_DECL: return "entity_decl";
	case XML_NAMESPACE_DECL: return "namespace_decl";
	case XML_XINCLUDE_START: return "xinclude_start";
	case XML_XINCLUDE_END: return "xinclude_end";
	case XML_DOCB_DOCUMENT_NODE: return "docb_document";
    }
    return "?";
}

void fixup_comments(XMQDoc *doq, xmlNode *node, int depth)
{
    debug("xmq=", "fixup comments %s|%s %s", indent_depth(depth), node->name, xml_element_type_to_string(node->type));
    if (node->type == XML_COMMENT_NODE)
    {
        // An xml comment containing dle escapes for example: -␐-␐- is replaceed with ---.
        // If multiple dle escapes exists, then for example: -␐␐- is replaced with -␐-.
        char *content_needed_escaping = unescape_xml_comment((const char*)node->content);
        if (content_needed_escaping)
        {
            if (xmq_debug_enabled_)
            {
                char *from = xmq_quote_as_c((const char*)node->content, NULL, false);
                char *to = xmq_quote_as_c(content_needed_escaping, NULL, false);
                debug("xmq=", "fix comment \"%s\" to \"%s\"", from, to);
            }

            xmlNodePtr new_node = xmlNewComment((const xmlChar*)content_needed_escaping);
            xmlReplaceNode(node, new_node);
            xmlFreeNode(node);
            free(content_needed_escaping);
        }
        return;
    }

    // Do not recurse into these
    if (node->type == XML_ENTITY_DECL) return;

    xmlNode *i = xml_first_child(node);
    while (i)
    {
        xmlNode *next = xml_next_sibling(i); // i might be freed in trim.
        fixup_comments(doq, i, depth+1);
        i = next;
    }
}

void xmq_fixup_comments_after_readin(XMQDoc *doq)
{
    xmlNodePtr i = doq->docptr_.xml->children;
    if (!doq || !i) return;

    debug("xmq=", "fixup comments after readin");

    while (i)
    {
        xmlNode *next = xml_next_sibling(i); // i might be freed in fixup_comments.
        fixup_comments(doq, i, 0);
        i = next;
    }
}

const char *xmqDocError(XMQDoc *doq)
{
    return doq->error_;
}

XMQParseError xmqDocErrno(XMQDoc *doq)
{
    return (XMQParseError)doq->errno_;
}

void xmqSetStateSourceName(XMQParseState *state, const char *source_name)
{
    if (source_name)
    {
        size_t l = strlen(source_name);
        state->source_name = (char*)malloc(l+1);
        strcpy(state->source_name, source_name);
    }
}

void xmqSetPrintAllParsesIXML(XMQParseState *state, bool all_parses)
{
    state->ixml_all_parses = all_parses;
}

void xmqSetTryToRecoverIXML(XMQParseState *state, bool try_recover)
{
    state->ixml_try_to_recover = try_recover;
}

size_t calculate_buffer_size(const char *start, const char *stop, int indent, const char *pre_line, const char *post_line)
{
    size_t pre_n = strlen(pre_line);
    size_t post_n = strlen(post_line);
    const char *o = start;
    for (const char *i = start; i < stop; ++i)
    {
        char c = *i;
        if (c == '\n')
        {
            // Indent the line.
            for (int i=0; i<indent; ++i) o++;
            o--; // Remove the \n
            o += pre_n; // Add any pre line prefixes.
            o += post_n; // Add any post line suffixes (default is 1 for "\n")
        }
        o++;
    }
    return o-start;
}

void copy_and_insert(MemBuffer *mb,
                     const char *start,
                     const char *stop,
                     int num_prefix_spaces,
                     const char *implicit_indentation,
                     const char *explicit_space,
                     const char *newline,
                     const char *prefix_line,
                     const char *postfix_line)
{
    for (const char *i = start; i < stop; ++i)
    {
        char c = *i;
        if (c == '\n')
        {
            membuffer_append_region(mb, postfix_line, NULL);
            membuffer_append_region(mb, newline, NULL);
            membuffer_append_region(mb, prefix_line, NULL);

            // Indent the next line.
            for (int i=0; i<num_prefix_spaces; ++i) membuffer_append_region(mb, implicit_indentation, NULL);
        }
        else if (c == ' ')
        {
            membuffer_append_region(mb, explicit_space, NULL);
        }
        else
        {
            membuffer_append_char(mb, c);
        }
    }
}

char *copy_lines(int num_prefix_spaces,
                 const char *start,
                 const char *stop,
                 int num_quotes,
                 bool use_dqs,
                 bool add_nls,
                 bool add_compound,
                 const char *implicit_indentation,
                 const char *explicit_space,
                 const char *newline,
                 const char *prefix_line,
                 const char *postfix_line)
{
    MemBuffer *mb = new_membuffer();

    const char *short_start = start;
    const char *short_stop = stop;

    if (add_compound)
    {
        membuffer_append(mb, "( ");

        short_start = has_leading_space_nl(start, stop, NULL);
        if (!short_start) short_start = start;
        short_stop = has_ending_nl_space(start, stop, NULL);
        if (!short_stop || short_stop == start) short_stop = stop;

        const char *i = start;

        while (i < short_start)
        {
            membuffer_append_entity(mb, *i);
            i++;
        }
    }

    const char q = use_dqs?'"':'\'';

    for (int i = 0; i < num_quotes; ++i) membuffer_append_char(mb, q);
    membuffer_append_region(mb, prefix_line, NULL);
    if (add_nls)
    {
        membuffer_append_region(mb, postfix_line, NULL);
        membuffer_append_region(mb, newline, NULL);
        membuffer_append_region(mb, prefix_line, NULL);
        for (int i = 0; i < num_prefix_spaces; ++i) membuffer_append_region(mb, implicit_indentation, NULL);
    }
    // Copy content into quote.
    copy_and_insert(mb, short_start, short_stop, num_prefix_spaces, implicit_indentation, explicit_space, newline, prefix_line, postfix_line);
    // Done copying content.

    if (add_nls)
    {
        membuffer_append_region(mb, postfix_line, NULL);
        membuffer_append_region(mb, newline, NULL);
        membuffer_append_region(mb, prefix_line, NULL);
        for (int i = 0; i < num_prefix_spaces; ++i) membuffer_append_region(mb, implicit_indentation, NULL);
    }

    membuffer_append_region(mb, postfix_line, NULL);
    for (int i = 0; i < num_quotes; ++i) membuffer_append_char(mb, q);

    if (add_compound)
    {
        const char *i = short_stop;

        while (i < stop)
        {
            membuffer_append_entity(mb, *i);
            i++;
        }

        membuffer_append(mb, " )");
    }

    membuffer_append_null(mb);

    return free_membuffer_but_return_trimmed_content(mb);
}

size_t line_length(const char *start, const char *stop, int *numq, int *lq, int *eq)
{
    const char *i = start;
    int llq = 0, eeq = 0;
    int num = 0, max = 0;
    // Skip line leading quotes
    const char q = *i;
    assert(q == '\'' || q == '"');
    while (*i == q) { i++; llq++;  }
    const char *lstart = i; // Points to text after leading quotes.
    // Find end of line.
    while (i < stop && *i != '\n') i++;
    const char *eol = i;
    i--;
    while (i > lstart && *i == q) { i--; eeq++; }
    i++;
    const char *lstop = i;
    // Mark endof text inside ending quotes.
    for (i = lstart; i < lstop; ++i)
    {
        if (*i == q)
        {
            num++;
            if (num > max) max = num;
        }
        else
        {
            num = 0;
        }
    }
    *numq = max;
    *lq = llq;
    *eq = eeq;
    assert( (llq+eeq+(lstop-lstart)) == eol-start);
    return lstop-lstart;
}

char *xmq_quote_with_entity_newlines(const char *start, const char *stop, XMQQuoteSettings *settings)
{
    // This code must only be invoked if there is at least one newline inside the to-be quoted text!
    MemBuffer *mb = new_membuffer();

    const char *i = start;
    bool found_nl = false;
    while (i < stop)
    {
        int numq;
        int lq = 0;
        int eq = 0;
        size_t line_len = line_length(i, stop, &numq, &lq, &eq);
        i += lq;
        for (int j = 0; j < lq; ++j) membuffer_append(mb, "&#39;");
        if (line_len > 0)
        {
            if (numq == 0 && (settings->force)) numq = 1; else numq++;
            if (numq == 2) numq++;
            for (int i=0; i<numq; ++i) membuffer_append(mb, "'");
            membuffer_append_region(mb, i, i+line_len);
            for (int i=0; i<numq; ++i) membuffer_append(mb, "'");
        }
        for (int j = 0; j < eq; ++j) membuffer_append(mb, "&#39;");
        i += line_len+eq;
        if (i < stop && *i == '\n')
        {
            if (!found_nl) found_nl = true;
            membuffer_append(mb, "&#10;");
            i++;
        }
    }
    return free_membuffer_but_return_trimmed_content(mb);
}

char *xmq_quote_default(int indent,
                        const char *start,
                        const char *stop,
                        XMQQuoteSettings *settings)
{
    bool add_nls = false;
    bool add_compound = false;
    bool use_double_quotes = false;
    bool prefer_double_quotes = false;
    int numq = count_necessary_quotes(start, stop, &add_nls, &add_compound, prefer_double_quotes, &use_double_quotes);

    if (numq > 0)
    {
        // If nl_begin is true and we have quotes, then we have to forced newline already due to quotes at
        // the beginning or end, therefore we use indent as is, however if
        if (add_nls == false) // then we might have to adjust the indent, or even introduce a nl_begin/nl_end.
        {
            if (indent == -1)
            {
                // Special case, -1 indentation requested this means the quote should be on its own line.
                // then we must insert newlines.
                // Otherwise the indentation will be 1.
                // e.g.
                // |'
                // |alfa beta
                // |gamma delta
                // |'
                add_nls = true;
                indent = 0;
            }
            else
            {
                // We have a nonzero indentation and number of quotes is 1 or 3.
                // Then the actual source indentation will be +1 or +3.
                if (numq < 4)
                {
                    // e.g. quote at 4 will have source at 5.
                    // |    'alfa beta
                    // |     gamma delta'
                    // e.g. quote at 4 will have source at 7.
                    // |    '''alfa beta
                    // |       gamma delta'
                    indent += numq;
                }
                else
                {
                    // More than 3 quotes, then we add newlines.
                    // e.g. quote at 4 will have source at 4.
                    // |    ''''
                    // |    alfa beta '''
                    // |    gamma delta
                    // |    ''''
                    add_nls = true;
                }
            }
        }
    }
    if (numq == 0 && settings->force) numq = 1;
    return copy_lines(indent,
                      start,
                      stop,
                      numq,
                      use_double_quotes,
                      add_nls,
                      add_compound,
                      settings->indentation_space,
                      settings->explicit_space,
                      settings->explicit_nl,
                      settings->prefix_line,
                      settings->postfix_line);
}


/**
    xmq_comment:

    Make a single line or multi line comment. Support compact mode with multiple line comments.
*/
char *xmq_comment(int indent, const char *start, const char *stop, XMQQuoteSettings *settings)
{
    assert(indent >= 0);
    assert(start);

    if (stop == NULL) stop = start+strlen(start);

    if (settings->compact)
    {
        return xmq_quote_with_entity_newlines(start, stop, settings);
    }

    return xmq_quote_default(indent, start, stop, settings);
}

int xmqForeach(XMQDoc *doq, const char *xpath, XMQNodeCallback cb, void *user_data)
{
    return xmqForeachRel(doq, xpath, cb, user_data, NULL);
}

int xmqForeachRel(XMQDoc *doq, const char *xpath, XMQNodeCallback cb, void *user_data, XMQNode *relative)
{
    xmlDocPtr doc = (xmlDocPtr)xmqGetImplementationDoc(doq);
    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (!ctx) return 0;

    if (relative && relative->node)
    {
        xmlXPathSetContextNode(relative->node, ctx);
    }

    xmlXPathObjectPtr objects = xmlXPathEvalExpression((const xmlChar*)xpath, ctx);

    if (objects == NULL)
    {
        xmlXPathFreeContext(ctx);
        return 0;
    }

    xmlNodeSetPtr nodes = objects->nodesetval;
    int size = (nodes) ? nodes->nodeNr : 0;

    if (cb)
    {
        for(int i = 0; i < size; i++)
        {
            xmlNodePtr node = nodes->nodeTab[i];
            XMQNode xn;
            xn.node = node;
            XMQProceed proceed = cb(doq, &xn, user_data);
            if (proceed == XMQ_STOP) break;
        }
    }

    xmlXPathFreeObject(objects);
    xmlXPathFreeContext(ctx);

    return size;
}

const char *xmqGetName(XMQNode *node)
{
    xmlNodePtr p = node->node;
    if (p)
    {
        return (const char*)p->name;
    }
    return NULL;
}

const char *xmqGetContent(XMQNode *node)
{
    xmlNodePtr p = node->node;
    if (p && p->children)
    {
        return (const char*)p->children->content;
    }
    return NULL;
}

XMQProceed catch_single_content(XMQDoc *doc, XMQNode *node, void *user_data)
{
    const char **out = (const char **)user_data;
    xmlNodePtr n = node->node;
    if (n && n->children)
    {
        *out = (const char*)n->children->content;
    }
    else
    {
        *out = NULL;
    }
    return XMQ_STOP;
}

int32_t xmqGetInt(XMQDoc *doq, const char *xpath)
{
    return xmqGetIntRel(doq, xpath, NULL);
}

int32_t xmqGetIntRel(XMQDoc *doq, const char *xpath, XMQNode *relative)
{
    const char *content = NULL;

    xmqForeachRel(doq, xpath, catch_single_content, (void*)&content, relative);

    if (!content) return 0;

    if (content[0] == '0' &&
        content[1] == 'x')
    {
        int64_t tmp = strtol(content, NULL, 16);
        return tmp;
    }

    if (content[0] == '0')
    {
        int64_t tmp = strtol(content, NULL, 8);
        return tmp;
    }

    return atoi(content);
}

int64_t xmqGetLong(XMQDoc *doq, const char *xpath)
{
    return xmqGetLongRel(doq, xpath, NULL);
}

int64_t xmqGetLongRel(XMQDoc *doq, const char *xpath, XMQNode *relative)
{
    const char *content = NULL;

    xmqForeachRel(doq, xpath, catch_single_content, (void*)&content, relative);

    if (!content) return 0;

    if (content[0] == '0' &&
        content[1] == 'x')
    {
        int64_t tmp = strtol(content, NULL, 16);
        return tmp;
    }

    if (content[0] == '0')
    {
        int64_t tmp = strtol(content, NULL, 8);
        return tmp;
    }

    return atol(content);
}

const char *xmqGetString(XMQDoc *doq, const char *xpath)
{
    return xmqGetStringRel(doq, xpath, NULL);
}

const char *xmqGetStringRel(XMQDoc *doq, const char *xpath, XMQNode *relative)
{
    const char *content = NULL;

    xmqForeachRel(doq, xpath, catch_single_content, (void*)&content, relative);

    return content;
}

double xmqGetDouble(XMQDoc *doq, const char *xpath)
{
    return xmqGetDoubleRel(doq, xpath, NULL);
}

double xmqGetDoubleRel(XMQDoc *doq, const char *xpath, XMQNode *relative)
{
    const char *content = NULL;

    xmqForeachRel(doq, xpath, catch_single_content, (void*)&content, relative);

    if (!content) return 0;

    return atof(content);
}

bool xmq_parse_buffer_xml(XMQDoc *doq, const char *start, const char *stop, int flags)
{
    /* Macro to check API for match with the DLL we are using */
    LIBXML_TEST_VERSION ;

    int parse_options = XML_PARSE_NOCDATA | XML_PARSE_NONET;
    bool should_trim = false;
    if ((flags & XMQ_FLAG_TRIM_HEURISTIC) ||
        (flags & XMQ_FLAG_TRIM_EXACT)) should_trim = true;
    if (flags & XMQ_FLAG_TRIM_NONE) should_trim = false;

    if (should_trim) parse_options |= XML_PARSE_NOBLANKS;

    xmlDocPtr doc = xmlReadMemory(start, stop-start, doq->source_name_, NULL, parse_options);
    if (doc == NULL)
    {
        doq->errno_ = XMQ_ERROR_PARSING_XML;
        // Let libxml2 print the error message.
        doq->error_ = NULL;
        return false;
    }

    if (doq->docptr_.xml)
    {
        xmlFreeDoc(doq->docptr_.xml);
    }

    doq->docptr_.xml = doc;

    xmq_fixup_comments_after_readin(doq);

    return true;
}

bool xmq_parse_buffer_html(XMQDoc *doq, const char *start, const char *stop, int flags)
{
    htmlDocPtr doc;
    xmlNode *roo_element = NULL;

    /* Macro to check API for match with the DLL we are using */
    LIBXML_TEST_VERSION

    int parse_options = HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_NONET;

    bool should_trim = false;
    if ((flags & XMQ_FLAG_TRIM_HEURISTIC) ||
        (flags & XMQ_FLAG_TRIM_EXACT)) should_trim = true;
    if (flags & XMQ_FLAG_TRIM_NONE) should_trim = false;

    if (should_trim) parse_options |= HTML_PARSE_NOBLANKS;

    doc = htmlReadMemory(start, stop-start, "foof", NULL, parse_options);

    if (doc == NULL)
    {
        doq->errno_ = XMQ_ERROR_PARSING_HTML;
        // Let libxml2 print the error message.
        doq->error_ = NULL;
        return false;
    }

    roo_element = xmlDocGetRootElement(doc);

    if (roo_element == NULL)
    {
        PRINT_ERROR("empty document\n");
        xmlFreeDoc(doc);
        return 0;
    }

    if (doq->docptr_.html)
    {
        xmlFreeDoc(doq->docptr_.html);
    }
    doq->docptr_.html = doc;

    xmq_fixup_comments_after_readin(doq);

    return true;
}

bool xmq_parse_buffer_text(XMQDoc *doq, const char *start, const char *stop, const char *implicit_root)
{
    char *buffer = strndup(start, stop-start);
    xmlNodePtr text = xmlNewDocText(doq->docptr_.xml, (xmlChar*)buffer);
    free(buffer);

    if (implicit_root && implicit_root[0])
    {
        // We have an implicit root must be created since input is text.
        xmlNodePtr root = xmlNewDocNode(doq->docptr_.xml, NULL, (const xmlChar *)implicit_root, NULL);
        xmlDocSetRootElement(doq->docptr_.xml, root);
        doq->root_.node = root;
        xmlAddChild(root, text);
    }
    else
    {
        // There is no implicit root. Text is the new root node.
        xmlDocSetRootElement(doq->docptr_.xml, text);
    }
    return true;
}

bool xmq_prepare_node(const char *start, const char *stop);

bool xmq_prepare_node(const char *start, const char *stop)
{
    const char *i = start;

    if (*i != '/') return false;

    while (i < stop)
    {
    }
    return false;
}

bool xmq_parse_buffer_clines(XMQDoc *doq, const char *start, const char *stop)
{
    char *buffer = strndup(start, stop-start);
    xmlNodePtr text = xmlNewDocText(doq->docptr_.xml, (xmlChar*)buffer);
    free(buffer);

    xmlDocSetRootElement(doq->docptr_.xml, text);

    return true;
}

bool xmqParseBufferWithType(XMQDoc *doq,
                            const char *start,
                            const char *stop,
                            const char *implicit_root,
                            XMQContentType ct,
                            int flags)
{
    bool ok = true;

    if (!stop) stop = start+strlen(start);

    // Unicode files might lead with a byte ordering mark.
    start = skip_any_potential_bom(start, stop);
    if (!start) return false;

    XMQContentType detected_ct = XMQ_CONTENT_UNKNOWN;
    if (ct != XMQ_CONTENT_IXML) detected_ct = xmqDetectContentType(start, stop);
    else ct = XMQ_CONTENT_IXML;

    if (ct == XMQ_CONTENT_DETECT)
    {
        ct = detected_ct;
    }
    else
    {
        if (ct != detected_ct && ct != XMQ_CONTENT_TEXT && ct != XMQ_CONTENT_IXML)
        {
            if (detected_ct == XMQ_CONTENT_XML && ct == XMQ_CONTENT_HTML)
            {
                // This is fine! We might be loading a fragment of html
                // that is detected as xml.
            }
            else
            {
                switch (ct) {
                case XMQ_CONTENT_XMQ: doq->errno_ = XMQ_ERROR_EXPECTED_XMQ; break;
                case XMQ_CONTENT_HTMQ: doq->errno_ = XMQ_ERROR_EXPECTED_HTMQ; break;
                case XMQ_CONTENT_XML: doq->errno_ = XMQ_ERROR_EXPECTED_XML; break;
                case XMQ_CONTENT_HTML: doq->errno_ = XMQ_ERROR_EXPECTED_HTML; break;
                case XMQ_CONTENT_JSON: doq->errno_ = XMQ_ERROR_EXPECTED_JSON; break;
                default: break;
                }
                ok = false;
                goto exit;
            }
        }
    }

    doq->original_content_type_ = detected_ct;
    doq->original_size_ = stop-start;

    switch (ct)
    {
    case XMQ_CONTENT_XMQ: ok = xmqParseBuffer(doq, start, stop, implicit_root, flags); break;
    case XMQ_CONTENT_HTMQ: ok = xmqParseBuffer(doq, start, stop, implicit_root, flags); break;
    case XMQ_CONTENT_XML: ok = xmq_parse_buffer_xml(doq, start, stop, flags); break;
    case XMQ_CONTENT_HTML: ok = xmq_parse_buffer_html(doq, start, stop, flags); break;
    case XMQ_CONTENT_JSON: ok = xmq_parse_buffer_json(doq, start, stop, implicit_root); break;
    case XMQ_CONTENT_IXML: ok = xmq_parse_buffer_ixml(doq, start, stop, flags); break;
    case XMQ_CONTENT_TEXT: ok = xmq_parse_buffer_text(doq, start, stop, implicit_root); break;
    case XMQ_CONTENT_CLINES: ok = xmq_parse_buffer_clines(doq, start, stop); break;
    default: break;
    }

exit:

    if (ok)
    {
        bool should_trim = false;

        if ((flags & XMQ_FLAG_TRIM_HEURISTIC) ||
            (flags & XMQ_FLAG_TRIM_EXACT)) should_trim = true;

        if (!(flags & XMQ_FLAG_TRIM_NONE) &&
            (ct == XMQ_CONTENT_XML ||
             ct == XMQ_CONTENT_HTML))
        {
            should_trim = true;
        }

        if (should_trim) xmqTrimWhitespace(doq, flags);
    }

    return ok;
}

bool xmqParseFileWithType(XMQDoc *doq,
                          const char *file,
                          const char *implicit_root,
                          XMQContentType ct,
                          int flags)
{
    bool rc = true;
    size_t fsize;
    const char *buffer;

    if (file)
    {
        xmqSetDocSourceName(doq, file);
        rc = load_file(doq, file, &fsize, &buffer);
    }
    else
    {
        xmqSetDocSourceName(doq, "-");
        rc = load_stdin(doq, &fsize, &buffer);
    }
    if (!rc) return false;

    rc = xmqParseBufferWithType(doq, buffer, buffer+fsize, implicit_root, ct, flags);

    free((void*)buffer);

    return rc;
}


xmlDtdPtr parse_doctype_raw(XMQDoc *doq, const char *start, const char *stop)
{
    size_t n = stop-start;
    xmlParserCtxtPtr ctxt;
    xmlDocPtr doc;

    ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);
    if (ctxt == NULL) {
        return NULL;
    }

    xmlParseChunk(ctxt, start, n, 0);
    xmlParseChunk(ctxt, start, 0, 1);

    doc = ctxt->myDoc;
    int rc = ctxt->wellFormed;
    xmlFreeParserCtxt(ctxt);

    if (!rc) {
        return NULL;
    }

    xmlDtdPtr dtd = xmlCopyDtd(doc->intSubset);
    xmlFreeDoc(doc);

    return dtd;
}

bool xmq_parse_buffer_json(XMQDoc *doq,
                           const char *start,
                           const char *stop,
                           const char *implicit_root)
{
    bool rc = true;
    XMQOutputSettings *os = xmqNewOutputSettings();
    XMQParseCallbacks *parse = xmqNewParseCallbacks();

    xmq_setup_parse_callbacks(parse);

    XMQParseState *state = xmqNewParseState(parse, os);
    state->no_trim_quotes = true;
    state->doq = doq;
    xmqSetStateSourceName(state, doq->source_name_);

    if (implicit_root != NULL && implicit_root[0] == 0) implicit_root = NULL;

    state->implicit_root = implicit_root;

    stack_push(state->element_stack, doq->docptr_.xml);
    // Now state->element_stack->top->data points to doq->docptr_;
    state->element_last = NULL; // Will be set when the first node is created.
    // The doc root will be set when the first element node is created.

    // Time to tokenize the buffer and invoke the parse callbacks.
    xmq_tokenize_buffer_json(state, start, stop);

    if (xmqStateErrno(state))
    {
        rc = false;
        doq->errno_ = xmqStateErrno(state);
        doq->error_ = build_error_message("%s\n", xmqStateErrorMsg(state));
    }

    xmqFreeParseState(state);
    xmqFreeParseCallbacks(parse);
    xmqFreeOutputSettings(os);

    return rc;
}

bool xmq_parse_buffer_ixml(XMQDoc *ixml_grammar,
                           const char *start,
                           const char *stop,
                           int flags)
{
    assert(ixml_grammar->yaep_grammar_ == NULL);

    bool rc = true;
    if (!stop) stop = start+strlen(start);

    XMQOutputSettings *os = xmqNewOutputSettings();
    XMQParseCallbacks *parse = xmqNewParseCallbacks();
    parse->magic_cookie = MAGIC_COOKIE;

    XMQParseState *state = xmqNewParseState(parse, os);
    xmqSetStateSourceName(state, xmqGetDocSourceName(ixml_grammar));

    state->doq = ixml_grammar;
    state->build_xml_of_ixml = false;
    YaepGrammar *grammar = yaepNewGrammar();
    YaepParseRun *run = yaepNewParseRun(grammar);
    ixml_grammar->yaep_grammar_ = grammar;
    ixml_grammar->yaep_parse_run_ = run;
    ixml_grammar->yaep_parse_state_ = state;
    if (xmqVerbose()) run->verbose = true;
    if (xmqDebugging()) run->debug = run->verbose = true;
    if (xmqTracing()) run->trace = run->debug = run->verbose = true;

    // Lets parse the ixml source to construct a yaep grammar.
    // This yaep grammar is cached in ixml_grammar->yaep_grammar_.
    ixml_build_yaep_grammar((YaepParseRun*)ixml_grammar->yaep_parse_run_,
                            (YaepGrammar*)ixml_grammar->yaep_grammar_,
                            state,
                            start,
                            stop,
                            NULL,
                            NULL);

    if (xmqStateErrno(state))
    {
        rc = false;
        ixml_grammar->errno_ = xmqStateErrno(state);
        ixml_grammar->error_ = build_error_message("%s\n", xmqStateErrorMsg(state));
    }

    // Do not free the state, it must be kept alive with the doc
    xmqFreeParseCallbacks(parse);
    xmqFreeOutputSettings(os);

    return rc;
}

void xmq_set_yaep_grammar(XMQDoc *doc, YaepGrammar *g)
{
    doc->yaep_grammar_ = g;
}

YaepGrammar *xmq_get_yaep_grammar(XMQDoc *doc)
{
    return doc->yaep_grammar_;
}

YaepParseRun *xmq_get_yaep_parse_run(XMQDoc *doc)
{
    return doc->yaep_parse_run_;
}

XMQParseState *xmq_get_yaep_parse_state(XMQDoc *doc)
{
    return doc->yaep_parse_state_;
}

void handle_yaep_syntax_error(YaepParseRun *pr,
                              int err_tok_num,
                              void *err_tok_attr,
                              int start_ignored_tok_num,
                              void *start_ignored_tok_attr,
                              int start_recovered_tok_num,
                              void *start_recovered_tok_attr)
{
    int line = 0, col = 0;
    find_line_col(pr->buffer_start, pr->buffer_stop, err_tok_num, &line, &col);

    // source.foo:2:26: syntax error
    printf("ixml:%d:%d: syntax error\n", line, col);
    int start = err_tok_num - 20;
    if (start < 0) start = 0;
    int stop = err_tok_num + 20;

    for (int i = start; i < stop && pr->buffer_start[i] != 0; ++i)
    {
        printf("%c", pr->buffer_start[i]);
    }
    printf("\n");
    for (int i = start; i < err_tok_num; ++i) printf (" ");
    printf("^\n");
}

const char *node_yaep_type_to_string(YaepTreeNodeType t)
{
    switch (t)
    {
    case YAEP_NIL: return "NIL";
    case YAEP_ERROR: return "ERROR";
    case YAEP_TERM: return "TERM";
    case YAEP_ANODE: return "ANODE";
    case YAEP_ALT: return "ALT";
    default:
        return "?";
    }
    return "?";
}

void collect_text(YaepTreeNode *n, MemBuffer *mb);

void collect_text(YaepTreeNode *n, MemBuffer *mb)
{
    if (n == NULL) return;
    if (n->type == YAEP_ANODE)
    {
        YaepAbstractNode *an = &n->val.anode;
        for (int i=0; an->children[i] != NULL; ++i)
        {
            YaepTreeNode *nn = an->children[i];
            collect_text(nn, mb);
        }
    }
    else
    if (n->type == YAEP_TERM)
    {
        YaepTerminalNode *at = &n->val.terminal;
        if (at->mark != '-')
        {
            UTF8Char utf8;
            size_t len = encode_utf8(at->code, &utf8);
            utf8.bytes[len] = 0;
            membuffer_append(mb, utf8.bytes);
        }
    }
    else
    {
        assert(false);
    }
}

void generate_dom_from_yaep_node(xmlDocPtr doc, xmlNodePtr node, YaepTreeNode *n, YaepTreeNode *parent, int depth, int index)
{
    if (n == NULL) return;
    if (n->type == YAEP_TERM && n->val.terminal.code == -1) return;
    if (n->type == YAEP_ANODE)
    {
        YaepAbstractNode *an = &n->val.anode;

        if (an != NULL && an->name != NULL)
        {
            if (an->name[0] == '|' && an->name[1] == '!')
            {
                // The content is a not lookahead node. Discard it.
            }
            else if (an->name[0] == '|' && an->name[1] == '+')
            {
                // The content to be inserted has been encoded in the rule name.
                // A hack yes. Does it work? Yes!
                xmlNodePtr new_node = NULL;

                if(an->name[2] == '/' && an->name[3] == '/')
                {
                    new_node = xmlNewDocComment(doc, (xmlChar*)an->name+4);
                }
                if(an->name[2] == '#')
                {
                    int value = (int)strtol(an->name+3, NULL, 16);
                    UTF8Char c;
                    encode_utf8(value, &c);
                    new_node = xmlNewDocText(doc, (xmlChar*)c.bytes);
                }
                else
                {
                    new_node = xmlNewDocText(doc, (xmlChar*)an->name+2);
                }
                if (node == NULL)
                {
                    xmlDocSetRootElement(doc, new_node);
                }
                else
                {
                    xmlAddChild(node, new_node);
                }
            }
            else if (an->mark != '-')
            {
                if (an->mark == '@')
                {
                    // This should become an attribute.
                    MemBuffer *mb = new_membuffer();
                    collect_text(n, mb);
                    membuffer_append_null(mb);
                    xmlNewProp(node, (xmlChar*)an->name, (xmlChar*)mb->buffer_);
                    free_membuffer_and_free_content(mb);
                }
                else
                {
                    // Normal node that should be generated.
                    xmlNodePtr new_node = xmlNewDocNode(doc, NULL, (xmlChar*)an->name, NULL);

                    if (node == NULL)
                    {
                        xmlDocSetRootElement(doc, new_node);
                    }
                    else
                    {
                        xmlAddChild(node, new_node);
                    }

                    for (int i=0; an->children[i] != NULL; ++i)
                    {
                        YaepTreeNode *nn = an->children[i];
                        generate_dom_from_yaep_node(doc, new_node, nn, n, depth+1, i);
                    }
                }
            }
            else
            {
                // Skip anonymous node whose name starts with / and deleted nodes with mark=-
                for (int i=0; an->children[i] != NULL; ++i)
                {
                    YaepTreeNode *nn = an->children[i];
                    generate_dom_from_yaep_node(doc, node, nn, n, depth+1, i);
                }
            }
        }
    }
    else if (n->type == YAEP_ALT)
    {
        xmlNodePtr new_node = NULL;
        if (node == NULL)
        {
            new_node = xmlNewDocNode(doc, NULL, (xmlChar*)"AMBIGUOUS", NULL);
            xmlDocSetRootElement(doc, new_node);
        }
        else
        {
            YaepAbstractNode *an = NULL;

            if (parent) an = &parent->val.anode;

            if (!an || an->mark != '*')
            {
                new_node = xmlNewDocNode(doc, NULL, (xmlChar*)"AMBIGUOUS", NULL);
                xmlAddChild(node, new_node);
            }
            else
            {
                new_node = node;
            }
        }

        YaepTreeNode *alt = n;

        generate_dom_from_yaep_node(doc, new_node, alt->val.alt.node, n, depth+1, 0);

        alt = alt->val.alt.next;

        while (alt && alt->type == YAEP_ALT)
        {
            generate_dom_from_yaep_node(doc, new_node, alt->val.alt.node, n, depth+1, 0);
            alt = alt->val.alt.next;
        }
    }
    else
    if (n->type == YAEP_TERM)
    {
        YaepTerminalNode *at = &n->val.terminal;
        if (at->mark != '-')
        {
            UTF8Char utf8;
            size_t len = encode_utf8(at->code, &utf8);
            utf8.bytes[len] = 0;

            xmlNodePtr new_node = xmlNewDocText(doc, (xmlChar*)utf8.bytes);

            if (node == NULL)
            {
                xmlDocSetRootElement(doc, new_node);
            }
            else
            {
                xmlAddChild(node, new_node);
            }
        }
    }
    else
    {
        for (int i=0; i<depth; ++i) printf("    ");
        printf("[%d] ", index);
        printf("WOOT %s\n", node_yaep_type_to_string(n->type));
    }
}

bool xmqParseBufferWithIXML(XMQDoc *doc, const char *start, const char *stop, XMQDoc *ixml_grammar, int flags)
{
    if (!doc || !start || !ixml_grammar) return false;
    if (!stop) stop = start+strlen(start);

    XMQParseState *state = xmq_get_yaep_parse_state(ixml_grammar);

    // Now add all character terminals in the content (not yet added) to the grammar.
    // And rewrite charsets into rules with multiple choice shrunk to the actual usage of characters in the input.
    scan_content_fixup_charsets(state, start, stop);

    ixml_print_grammar(state);

    state->yaep_i_ = hashmap_iterate(state->ixml_terminals_map);
    state->yaep_j_ = 0;
    int rc = yaep_read_grammar(xmq_get_yaep_parse_run(ixml_grammar),
                               xmq_get_yaep_grammar(ixml_grammar),
                               0,
                               ixml_to_yaep_read_terminal,
                               ixml_to_yaep_read_rule);
    hashmap_free_iterator(state->yaep_i_);

    if (rc != 0)
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "internal error, yaep did not accept generated yaep grammar";
        printf("xmq: internal error generating yaep grammar from ixml grammar %s\n", yaep_error_message(xmq_get_yaep_grammar(ixml_grammar)));
        longjmp(state->error_handler, 1);
    }

    yaep_set_one_parse_flag(xmq_get_yaep_grammar(ixml_grammar),
                            (flags & XMQ_FLAG_IXML_ALL_PARSES)?0:1);

    yaep_set_error_recovery_flag(xmq_get_yaep_grammar(ixml_grammar),
                                 (flags & XMQ_FLAG_IXML_TRY_TO_RECOVER)?1:0);

    if (state->ixml_costs_enabled)
    {
        verbose("xmq=", "ixml rule costs are used to resolve ambigiuity");
        // We have found =-in the grammar, ie that a rule has a certain cost. We assume this
        // means that there can be ambiguous results.
        // Keep parsing, finding all parses....
        yaep_set_one_parse_flag(xmq_get_yaep_grammar(ixml_grammar), 0);
        // Enable cost calculations...
        yaep_set_cost_flag(xmq_get_yaep_grammar(ixml_grammar), true);
    }

    if (state->ixml_controlled_ambiguity_enabled)
    {
        verbose("xmq=", "ixml controlled ambiguity enabled");
        // We have found *rule = ... the grammar, ie that a rule expects ambiguity.
        // Keep parsing, finding all parses....
        yaep_set_one_parse_flag(xmq_get_yaep_grammar(ixml_grammar), 0);
    }

    YaepParseRun *run = xmq_get_yaep_parse_run(ixml_grammar);
    run->buffer_start = start;
    run->buffer_stop = stop;
    run->buffer_i = start;

    YaepGrammar *grammar = xmq_get_yaep_grammar(ixml_grammar);
    run->syntax_error = handle_yaep_syntax_error;

    // Parse source content using the yaep grammar, previously generated from the ixml source.
    rc = yaepParse(run, grammar);

    if (rc)
    {
        // Syntax error has already been printed.
        return false;
    }

    generate_dom_from_yaep_node(doc->docptr_.xml, NULL, run->root, NULL, 0, 0);

    if (run->ambiguous_p)
    {
        xmlNodePtr element = xmlDocGetRootElement(doc->docptr_.xml);
        xmlNsPtr ns = xmlNewNs(element,
                               (const xmlChar *)"http://invisiblexml.org/NS",
                               (const xmlChar *)"ixml");

        xmlSetNsProp(element, ns, (xmlChar*)"state", (xmlChar*)"ambiguous");
    }


    if (run->root) yaepFreeTree(run->root, NULL, NULL);

    return true;
}

bool xmqParseFileWithIXML(XMQDoc *doc, const char *file_name, XMQDoc *ixml_grammar, int flags)
{
    const char *buffer;
    size_t buffer_len = 0;
    bool ok = load_file(doc, file_name, &buffer_len, &buffer);

    if (!ok) return false;

    ok = xmqParseBufferWithIXML(doc, buffer, buffer+buffer_len, ixml_grammar, flags);

    free((char*)buffer);

    return ok;
}

char *xmqCompactQuote(const char *content)
{
    MemBuffer *mb = new_membuffer();

    XMQOutputSettings os;
    memset(&os, 0, sizeof(os));
    os.compact = true;
    os.escape_newlines = true;
    os.output_buffer = mb;
    os.content.writer_state = os.output_buffer;
    os.content.write = (XMQWrite)(void*)membuffer_append_region;
    os.error.writer_state = os.output_buffer;
    os.error.write = (XMQWrite)(void*)membuffer_append_region;
    os.explicit_space = " ";
    os.explicit_tab = "\t";

    XMQPrintState ps;
    memset(&ps, 0, sizeof(ps));
    ps.output_settings = &os;

    xmlNode node;
    memset(&node, 0, sizeof(node));
    node.content = (xmlChar*)content;
    print_value(&ps , &node, LEVEL_ATTR_VALUE);

    membuffer_append_null(mb);

    return free_membuffer_but_return_trimmed_content(mb);
}


XMQLineConfig *xmqNewLineConfig()
{
    XMQLineConfig *lc = (XMQLineConfig*)malloc(sizeof(XMQLineConfig));
    memset(lc, 0, sizeof(XMQLineConfig));
    return lc;
}

void xmqFreeLineConfig(XMQLineConfig *lc)
{
    free(lc);
}

void xmqSetLineHumanReadable(XMQLineConfig *lc, bool enabled)
{
    lc->human_readable_ = enabled;
}

char *xmqLineDoc(XMQLineConfig *lc, XMQDoc *doc)
{
    XMQOutputSettings *settings = xmqNewOutputSettings();
    xmqSetCompact(settings, true);
    xmqSetEscapeNewlines(settings, true);
    xmqSetUseColor(settings, false);
    xmqSetOutputFormat(settings, XMQ_CONTENT_XMQ);
    xmqSetRenderFormat(settings, XMQ_RENDER_PLAIN);

    char *start, *stop;
    xmqSetupPrintMemory(settings, &start, &stop);
    xmqPrint(doc, settings);

    xmqFreeOutputSettings(settings);

    return start;
}

char *xmq_line_vprintf_hr(XMQLineConfig *lc, const char *element_name, va_list ap);
char *xmq_line_vprintf_hr(XMQLineConfig *lc, const char *element_name, va_list ap)
{
    // Lets check that last character. We can have:
    // 1) A complete xmq object.
    //    debug("xmq{", "alfa=", "%d", 42, "}");
    //    logs as: (xmq) alfa=42
    // 2) A single string.
    //    debug("xmq=", "size=%zu name=%s", the_size, the_name);
    //    logs as: (xmq) size=42 name=info
    // 3) Forgot to indicate { or =.
    //    debug("Fix me!");
    //    logs as: (log) Fix me!

    char c = element_name[strlen(element_name)-1];

    char module[256];
    snprintf(module, 256, "(%.*s) ", (int)strlen(element_name)-1, element_name);

    if (c != '{')
    {
        MemBuffer *mb = new_membuffer();
        const char *format;
        if ( c != '=')
        {
            // User forgot to indicate either { or =.
            format = element_name;
            membuffer_append(mb, "(log) ");
        }
        else
        {
            format = va_arg(ap, const char *);
        }
        char *buf = buf_vsnprintf(format, ap);
        membuffer_append(mb, buf);
        free(buf);
        membuffer_prefix_lines(mb, module);
        membuffer_append_null(mb);
        return free_membuffer_but_return_trimmed_content(mb);
    }

    MemBuffer *mb = new_membuffer();

    char *buf = (char*)malloc(1024);
    size_t buf_size = 1024;
    memset(buf, 0, buf_size);

    bool first = true;

    for (;;)
    {
        const char *key = va_arg(ap, const char*);
        if (!key) break;
        if (*key == '}') break;

        size_t kl = strlen(key);

        if (!first)
        {
            membuffer_append(mb, " ");
            first = false;
        }

        if (key[kl-1] != '=')
        {
            warning("xmq.warn=", "Warning erroneous api usage! Line printf key \"%s\" does not end with = sign!", key);
            break;
        }

        membuffer_append_region(mb, key, key+kl-1);
        membuffer_append(mb, ":");

        const char *format = va_arg(ap, const char *);
        for (;;)
        {
            size_t n = vsnprintf(buf, buf_size, format, ap);

            if (n < buf_size)
            {
                // The generated output fitted in the allocated buffer.
                break;
            }

            buf_size *= 2;
            free(buf);
            buf = (char*)malloc(buf_size);
            memset(buf, 0, buf_size);
        }
        membuffer_append(mb, buf);
    }

    free(buf);

    membuffer_prefix_lines(mb, module);
    membuffer_append_null(mb);

    return free_membuffer_but_return_trimmed_content(mb);
}

char *xmq_line_vprintf_xmq(XMQLineConfig *lc, const char *element_name, va_list ap);
char *xmq_line_vprintf_xmq(XMQLineConfig *lc, const char *element_name, va_list ap)
{
    // Lets check that last character. We can have:
    // 1) A complete xmq object.
    //    debug("xmq.cli{", "alfa=", "%d", 42, "}");
    //    logs as: xmq.cli{alfa=42}
    // 2) A single string.
    //    debug("xmq.cli=", "size=%zu name=%s", the_size, the_name);
    //    logs as: xmq.cli='size=42 name=info'
    // 3) Forgot to indicate { or =.
    //    debug("Fix me!");
    //    logs as: log='Fix me!'

    char c = element_name[strlen(element_name)-1];

    if (c != '{')
    {
        MemBuffer *mb = new_membuffer();
        const char *format;
        if ( c != '=')
        {
            // User forgot to indicate either { or =.
            format = element_name;
            membuffer_append(mb, "log=");
        }
        else
        {
            format = va_arg(ap, const char *);
            membuffer_append(mb, element_name);
        }
        char *buf = buf_vsnprintf(format, ap);
        char *q = xmqCompactQuote(buf);
        free(buf);
        membuffer_append(mb, q);
        membuffer_append_null(mb);
        return free_membuffer_but_return_trimmed_content(mb);
    }

    MemBuffer *mb = new_membuffer();
    membuffer_append(mb, element_name);

    char *buf = (char*)malloc(1024);
    size_t buf_size = 1024;
    memset(buf, 0, buf_size);

    for (;;)
    {
        const char *key = va_arg(ap, const char*);
        if (!key) break;
        if (*key == '}') break;

        size_t kl = strlen(key);
        char last = membuffer_back(mb);
        if (last != '"' && last != '\'' && last != '(' && last != ')'  && last != '{' && last != '}')
        {
            membuffer_append(mb, " ");
        }

        if (key[kl-1] != '=')
        {
            warning("xmq.warn=", "Warning erroneous api usage! Line printf key \"%s\" does not end with = sign!", key);
            break;
        }

        // The key has an = sign at the end. E.g. size=
        membuffer_append(mb, key);
        const char *format = va_arg(ap, const char *);
        for (;;)
        {
            size_t n = vsnprintf(buf, buf_size, format, ap);

            if (n < buf_size)
            {
                // The generated output fitted in the allocated buffer.
                break;
            }

            buf_size *= 2;
            free(buf);
            buf = (char*)malloc(buf_size);
            memset(buf, 0, buf_size);
        }

        char *quote = xmqCompactQuote(buf);
        membuffer_append(mb, quote);
        free(quote);
    }

    free(buf);
    membuffer_append(mb, "}");
    membuffer_append_null(mb);

    return free_membuffer_but_return_trimmed_content(mb);
}

char *xmqLineVPrintf(XMQLineConfig *lc, const char *element_name, va_list ap)
{
    if (lc->human_readable_)
    {
        return xmq_line_vprintf_hr(lc, element_name, ap);
    }
    return xmq_line_vprintf_xmq(lc, element_name, ap);
}

char *xmqLinePrintf(XMQLineConfig *lc, const char *element_name, ...)
{
    va_list ap;
    va_start(ap, element_name);

    char *v = xmqLineVPrintf(lc, element_name, ap);

    va_end(ap);

    return v;
}

#ifdef BUILDING_DIST_XMQ

// PART C ALWAYS_C ////////////////////////////////////////

#ifdef ALWAYS_MODULE

STATIC void check_malloc(void *a)
{
    if (!a)
    {
        PRINT_ERROR("libxmq: Out of memory!\n");
        exit(1);
    }
}

XMQLineConfig xmq_log_line_config_;

STATIC void error__(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    char *line = xmqLineVPrintf(&xmq_log_line_config_, fmt, args);
    fprintf(stderr, "%s\n", line);
    free(line);
    va_end(args);
}

STATIC void warning__(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    char *line = xmqLineVPrintf(&xmq_log_line_config_, fmt, args);
    fprintf(stderr, "%s\n", line);
    free(line);
    va_end(args);
}

bool xmq_verbose_enabled_ = false;

STATIC void verbose__(const char* fmt, ...)
{
    if (xmq_verbose_enabled_)
    {
        if (!xmq_log_filter_ || !strncmp(fmt, xmq_log_filter_, strlen(xmq_log_filter_)))
        {
            va_list args;
            va_start(args, fmt);
            char *line = xmqLineVPrintf(&xmq_log_line_config_, fmt, args);
            fprintf(stderr, "%s\n", line);
            free(line);
            va_end(args);
        }
    }
}

bool xmq_debug_enabled_ = false;

STATIC void debug__(const char* fmt, ...)
{
    assert(fmt);

    if (xmq_debug_enabled_)
    {
        if (!xmq_log_filter_ || !strncmp(fmt, xmq_log_filter_, strlen(xmq_log_filter_)))
        {
            va_list args;
            va_start(args, fmt);
            char *line = xmqLineVPrintf(&xmq_log_line_config_, fmt, args);
            fprintf(stderr, "%s\n", line);
            free(line);
            va_end(args);
        }
    }
}

STATIC void debug_mb__(const char* module, MemBuffer *mb)
{
    if (xmq_debug_enabled_)
    {
        if (!xmq_log_filter_ || !strncmp(module, xmq_log_filter_, strlen(xmq_log_filter_)))
        {
            debug__(module, "%.*s", mb->used_, mb->buffer_);
        }
    }
}

bool xmq_trace_enabled_ = false;
const char *xmq_log_filter_ = NULL;

STATIC void trace__(const char* fmt, ...)
{
    if (xmq_trace_enabled_)
    {
        if (!xmq_log_filter_ || !strncmp(fmt, xmq_log_filter_, strlen(xmq_log_filter_)))
        {
            va_list args;
            va_start(args, fmt);
            char *line = xmqLineVPrintf(&xmq_log_line_config_, fmt, args);
            fprintf(stderr, "%s\n", line);
            free(line);
            va_end(args);
        }
    }
}

STATIC void trace_mb__(const char* module, MemBuffer *mb)
{
    if (xmq_trace_enabled_)
    {
        if (!xmq_log_filter_ || !strncmp(module, xmq_log_filter_, strlen(xmq_log_filter_)))
        {
            trace__(module, "%.*s", mb->used_, mb->buffer_);
        }
    }
}

char *buf_vsnprintf(const char *format, va_list ap)
{
    size_t buf_size = 1024;
    char *buf = (char*)malloc(buf_size);

    for (;;)
    {
        va_list tmp;
        va_copy(tmp, ap);
        size_t n = vsnprintf(buf, buf_size, format, tmp);
        va_end(tmp);

        if (n < buf_size) break;
        buf_size = n+32;
        free(buf);
        // Why not realloc? Well, we are goint to redo the printf anyway overwriting
        // the buffer again. So why allow realloc to spend time copying the content before
        // it is overwritten?
        buf = (char*)malloc(buf_size);
    }
    return buf;
}

#ifdef PLATFORM_WINAPI
char *strndup(const char *s, size_t l)
{
    char *buf = (char*)malloc(l+1);
    size_t i = 0;
    for (; i < l; ++i)
    {
        buf[i] = s[i];
        if (!s[i]) break;
    }
    if (i < l)
    {
        // The string s was shorter than l. We have already copied the null terminator.
        // Just resize.
        buf = (char*)realloc(buf, i+1);
    }
    else
    {
        // We copied l bytes. Store a null-terminator in the position i (== l).
        buf[i] = 0;
    }
    return buf;
}
#endif

static char *helper(size_t scale, size_t s, const char *suffix)
{
    size_t o = s;
    s /= scale;
    size_t diff = o-(s*scale);
    if (diff == 0) {
        char *buf = (char*)malloc(64);
        snprintf(buf, 64, "%zu.00%s", s, suffix);
        return buf;
    }
    size_t dec = (int)(100*(diff+1) / scale);
    char *buf = (char*)malloc(64);
    snprintf(buf, 64, "%zu.%02zu%s", s, dec, suffix);
    return buf;
}

#define KB 1024

STATIC char *humanReadableTwoDecimals(size_t s)
{
    if (s < KB)
    {
        return helper(1, s, " B");
    }
    if (s < KB * KB)
    {
        return helper(KB, s, " KiB");
    }
    if (s < KB * KB * KB)
    {
        return helper(KB*KB, s, " MiB");
    }
#if SIZEOF_SIZE_T == 8
    if (s < KB * KB * KB * KB)
    {
     	return helper(KB*KB*KB, s, " GiB");
    }
    if (s < KB * KB * KB * KB * KB)
    {
        return helper(KB*KB*KB*KB, s, " TiB");
    }
    return helper(KB*KB*KB*KB*KB, s, " PiB");
#else
    return helper(KB*KB*KB, s, " GiB");
#endif
}


#endif // ALWAYS_MODULE

// PART C COLORS_C ////////////////////////////////////////

#ifdef COLORS_MODULE

bool hex_to_number(char c, char cc, int *v);

/**
   get_color: Lookup the color strings
   coloring: The table of colors.
   c: The color to use from the table.
   pre: Store a pointer to the start color string here.
   post: Store a pointer to the end color string here.
*/
void getThemeStrings(XMQOutputSettings *os, XMQColor color, const char **pre, const char **post)
{
    XMQTheme *theme = os->theme;

    if (!theme)
    {
        *pre = NULL;
        *post = NULL;
        return;
    }

    switch(color)
    {

#define X(TYPE) case COLOR_##TYPE: *pre = theme->TYPE.pre; *post = theme->TYPE.post; return;
LIST_OF_XMQ_TOKENS
#undef X

    case COLOR_unicode_whitespace: *pre = theme->unicode_whitespace.pre; *post = theme->unicode_whitespace.post; return;
    case COLOR_indentation_whitespace: *pre = theme->indentation_whitespace.pre; *post = theme->indentation_whitespace.post; return;
    case COLOR_ns_override_xsl: *pre = theme->ns_override_xsl.pre; *post = theme->ns_override_xsl.post; return;
    default:
        *pre = NULL;
        *post = NULL;
        return;
    }
    assert(false);
    *pre = NULL;
    *post = NULL;
}

// Set background color: echo -ne "\033]11;#53186f\007"

//#define DEFAULT_COLOR "\033]11;#aa186f\007"
//#echo -ne '\e]10;#123456\e\\'  # set default foreground to #123456
//#echo -ne '\e]11;#abcdef\e\\'  # set default background to #abcdef
//printf "\x1b[38;2;40;177;249mTRUECOLOR\x1b[0m\n"

bool string_to_color_def(const char *s, XMQColorDef *def)
{
    // #aabbcc
    // #aabbcc_B
    // #aabbcc_U
    // #aabbcc_B_U

    def->r = -1;
    def->g = -1;
    def->b = -1;
    def->bold = false;
    def->underline = false;

    int r, g, b;
    bool bold, underline;

    r = g = b = 0;
    bold = false;
    underline = false;

    if (strlen(s) < 7) return false;
    if (*s != '#') return false;
    s++;

    bool ok = hex_to_number(*(s+0), *(s+1), &r);
    ok &= hex_to_number(*(s+2), *(s+3), &g);
    ok &= hex_to_number(*(s+4), *(s+5), &b);

    if (!ok) return false;

    s+=6;
    if (*s == '_')
    {
        if (*(s+1) == 'B') bold = true;
        if (*(s+1) == 'U') underline = true;
        s += 2;
    }
    if (*s == '_')
    {
        if (*(s+1) == 'B') bold = true;
        if (*(s+1) == 'U') underline = true;
        s += 2;
    }
    if (*s != 0) return false;

    def->r = r;
    def->g = g;
    def->b = b;
    def->bold = bold;
    def->underline = underline;

    return true;
}

bool hex_to_number(char c, char cc, int *v)
{
    int hi = 0;
    if (c >= '0' && c <= '9') hi = (int)c-'0';
    else if (c >= 'a' && c <= 'f') hi = 10+(int)c-'a';
    else if (c >= 'A' && c <= 'F') hi = 10+(int)c-'A';
    else return false;

    int lo = 0;
    if (cc >= '0' && cc <= '9') lo = (int)cc-'0';
    else if (cc >= 'a' && cc <= 'f') lo = 10+(int)cc-'a';
    else if (cc >= 'A' && cc <= 'F') lo = 10+(int)cc-'A';
    else return false;

    *v = hi*16+lo;
    return true;
}

bool generate_ansi_color(char *buf, size_t buf_size, XMQColorDef *def)
{
    // Example: \x1b[38;2;40;177;249mTRUECOLOR\x1b[0m
    if (buf_size < 32) return false;

    char *i = buf;

    *i++ = 27;
    *i++ = '[';
    *i++ = '0';
    *i++ = ';';
    if (def->bold)
    {
        *i++ = '1';
        *i++ = ';';
    }
    if (def->underline) {
        *i++ = '4';
        *i++ = ';';
    }
    *i++ = '3';
    *i++ = '8';
    *i++ = ';';
    *i++ = '2';
    *i++ = ';';

    char tmp[16];
    snprintf(tmp, sizeof(tmp), "%d", def->r);
    strcpy(i, tmp);
    i += strlen(tmp);
    *i++ = ';';

    snprintf(tmp, sizeof(tmp), "%d", def->g);
    strcpy(i, tmp);
    i += strlen(tmp);
    *i++ = ';';

    snprintf(tmp, sizeof(tmp), "%d", def->b);
    strcpy(i, tmp);
    i += strlen(tmp);
    *i++ = 'm';
    *i++ = 0;

    return true;
}

bool generate_html_color(char *buf, size_t buf_size, XMQColorDef *def, const char *name)
{
    // xmqQ{color:#26a269;font-weight:600;}

    if (buf_size < 64) return false;

    char *i = buf;
    i += snprintf(i, buf_size, "%s{color:#%02x%02x%02x", name, def->r, def->g, def->b);
    *i++ = ';';

    if (def->bold)
    {
        const char *tmp = "font-weight:600;";
        strcpy(i, tmp);
        i += strlen(tmp);
    }

    if (def->underline)
    {
        const char *tmp = "text-decoration:underline;";
        strcpy(i, tmp);
        i += strlen(tmp);
    }

    *i++ = '}';
    *i++ = 0;
    return false;
}

bool generate_tex_color(char *buf, size_t buf_size, XMQColorDef *def, const char *name)
{
    // \definecolor{mypink2}{RGB}{219, 48, 122}

    if (buf_size < 128) return false;

    snprintf(buf, buf_size, "\\definecolor{%s}{RGB}{%d,%d,%d}", name, def->r, def->g, def->b);
    return true;
}

const char *color_names[13] = {
    "xmqC", // Comment
    "xmqQ", // Quote
    "xmqE", // Entity
    "xmqNS", // Name Space (both for element and attribute)
    "xmqEN", // Element Name
    "xmqEK", // Element Key
    "xmqEKV", // Element Key Value
    "xmqAK", // Attribute Key
    "xmqAKV", // Attribute Key Value
    "xmqCP", // Compound Parentheses
    "xmqNSD", // Name Space declaration xmlns
    "xmqUW", // Unicode whitespace
    "xmqXSL", // Element color for xsl transform elements.
};

const char* colorName(int i)
{
    return color_names[i];
}

void setColorDef(XMQColorDef *cd, int r, int g, int b, bool bold, bool underline);

void setColorDef(XMQColorDef *cd, int r, int g, int b, bool bold, bool underline)
{
    cd->r = r;
    cd->g = g;
    cd->b = b;
    cd->bold = bold;
    cd->underline = underline;
}

#endif // COLORS_MODULE

// PART C CORE_C ////////////////////////////////////////

#ifdef CORE_MODULE

bool internal_parser_number(const char *s, int64_t *out, int num_digits);

bool internal_parser_number(const char *s, int64_t *out, int num_digits)
{
    if (s == NULL || *s == 0) return false;

    char *err = NULL;
    int64_t tmp = strtoll(s, &err, 0);

    if (err == NULL || *err != 0) return false;
    *out = tmp;

    return true;
}

bool coreParseI8(const char *s, int8_t *out)
{
    int64_t tmp = 0;
    bool ok = internal_parser_number(s, &tmp, 3);
    if (!ok) return false;
    if (tmp > 127) return false;
    if (tmp < -128) return false;
    *out = (int8_t)tmp;

    return true;
}

bool coreParseI16(const char *s, int16_t *out)
{
    return false;
}

bool coreParseI32(const char *s, int32_t *out)
{
    return false;
}

bool coreParseI64(const char *s, int64_t *out)
{
    return false;
}

/*bool coreParseI128(const char *s, __int128 *out)
{
    return false;
}*/

#endif // CORE_MODULE

// PART C DEFAULT_THEMES_C ////////////////////////////////////////

#ifdef DEFAULT_THEMES_MODULE

const char *defaultColor(int i, const char *theme_name);

const char *default_darkbg_colors[NUM_XMQ_COLOR_NAMES] = {
    "#2aa1b3", // XMQ_COLOR_C
    "#26a269_B", // XMQ_COLOR_Q
    "#c061cb", // XMQ_COLOR_E
    "#a9a9a9", // XMQ_COLOR_NS
    "#ff8c00", // XMQ_COLOR_EN
    "#88b4f7", // XMQ_COLOR_EK
    "#26a269_B", // XMQ_COLOR_EKV
    "#88b4f7", // XMQ_COLOR_AK
    "#6196ec", // XMQ_COLOR_AKV
    "#c061cb", // XMQ_COLOR_CP
    "#2aa1b3", // XMQ_COLOR_NSD
    "#880000_U", // XMQ_COLOR_UW
    "#c061cb" // XMQ_COLOR_XSL
};

const char *win_darkbg_ansi[NUM_XMQ_COLOR_NAMES] = {
    "\033[96m\033[24m", // XMQ_COLOR_C --- CYAN
    "\033[92m\033[24m", // XMQ_COLOR_Q --- GREEN
    "\033[95m\033[24m", // XMQ_COLOR_E --- MAGENTA
    "\033[37m\033[24m", // XMQ_COLOR_NS --- GRAY
    "\033[93m\033[24m", // XMQ_COLOR_EN --- ORANGE
    "\033[36m\033[24m", // XMQ_COLOR_EK --- LIGHT BLUE
    "\033[92m\033[24m", // XMQ_COLOR_EKV --- GREEN
    "\033[36m\033[24m", // XMQ_COLOR_AK --- LIGHT BLUE
    "\033[94m\033[24m", // XMQ_COLOR_AKV --- BLUE
    "\033[95m\033[24m", // XMQ_COLOR_CP --- MAGENTA
    "\033[36m\033[24m", // XMQ_COLOR_NSD --- LIGHT BLUE
    "\033[91m\033[4m", // XMQ_COLOR_UW --- RED UNDERLINE
    "\033[95m\033[24m", // XMQ_COLOR_XSL -- MAGENTA
};

const char *default_lightbg_colors[NUM_XMQ_COLOR_NAMES] = {
    "#2aa1b3", // XMQ_COLOR_C
    "#26a269_B", // XMQ_COLOR_Q
    "#c061cb", // XMQ_COLOR_E
    "#696969", // XMQ_COLOR_NS
    "#a86c00", // XMQ_COLOR_EN
    "#0060fd", // XMQ_COLOR_EK
    "#26a269_B", // XMQ_COLOR_EKV
    "#0060fd", // XMQ_COLOR_AK
    "#12488c", // XMQ_COLOR_AKV
    "#c061cb", // XMQ_COLOR_CP
    "#1a91a3", // XMQ_COLOR_NSD
    "#880000_U", // XMQ_COLOR_UW
    "#c061cb" // XMQ_COLOR_XSL
};

const char *ansiWin(int i)
{
    return win_darkbg_ansi[i];
}

const char *defaultColor(int i, const char *theme_name)
{
    if (!strcmp(theme_name, "lightbg")) return default_lightbg_colors[i];
    return default_darkbg_colors[i];
}

void installDefaultThemeColors(XMQTheme *theme)
{
    XMQColorDef *colors = theme->colors_darkbg;
    for (int i = 0; i < NUM_XMQ_COLOR_NAMES; ++i)
    {
        const char *color = defaultColor(i, "darkbg");
        string_to_color_def(color, &colors[i]);
    }

    colors = theme->colors_lightbg;
    for (int i = 0; i < NUM_XMQ_COLOR_NAMES; ++i)
    {
        const char *color = defaultColor(i, "lightbg");
        string_to_color_def(color, &colors[i]);
    }
}

#endif // DEFAULT_THEMES_MODULE

// PART C ENTITIES_C ////////////////////////////////////////

#ifdef ENTITIES_MODULE

// This is not complete yet...

#define HTML_GREEK \
X(913,Alpha,"Α","Alpha") \
X(914,Beta,"Β","Beta") \
X(915,Gamma,"Γ","Gamma") \
X(916,Delta,"Δ","Delta") \
X(917,Epsilon,"Ε","Epsilon") \
X(918,Zeta,"Ζ","Zeta") \
X(919,Eta,"Η","Eta") \
X(920,Theta,"Θ","Theta") \
X(921,Iota,"Ι","Iota") \
X(922,Kappa,"Κ","Kappa") \
X(923,Lambda,"Λ","Lambda") \
X(924,Mu,"Μ","Mu") \
X(925,Nu,"Ν","Nu") \
X(926,Xi,"Ξ","Xi") \
X(927,Omicron,"Ο","Omicron") \
X(928,Pi,"Π","Pi") \
X(929,Rho,"Ρ","Rho") \
X(931,Sigma,"Σ","Sigma") \
X(932,Tau,"Τ","Tau") \
X(933,Upsilon,"Υ","Upsilon") \
X(934,Phi,"Φ","Phi") \
X(935,Chi,"Χ","Chi") \
X(936,Psi,"Ψ","Psi") \
X(937,Omega,"Ω","Omega") \
X(945,alpha,"α","alpha") \
X(946,beta,"β","beta") \
X(947,gamma,"γ","gamma") \
X(948,delta,"δ","delta") \
X(949,epsilon,"ε","epsilon") \
X(950,zeta,"ζ","zeta") \
X(951,eta,"η","eta") \
X(952,theta,"θ","theta") \
X(953,iota,"ι","iota") \
X(954,kappa,"κ","kappa") \
X(955,lambda,"λ","lambda") \
X(956,mu,"μ","mu") \
X(957,nu,"ν","nu") \
X(958,xi,"ξ","xi") \
X(959,omicron,"ο","omicron") \
X(960,pi,"π","pi") \
X(961,rho,"ρ","rho") \
X(962,sigmaf,"ς","sigmaf") \
X(963,sigma,"σ","sigma") \
X(964,tau,"τ","tau") \
X(965,upsilon,"υ","upsilon") \
X(966,phi,"φ","phi") \
X(967,chi,"χ","chi") \
X(968,psi,"ψ","psi") \
X(969,omega,"ω","omega") \
X(977,thetasym,"ϑ","Theta") \
X(978,upsih,"ϒ","Upsilon") \
X(982,piv,"ϖ","Pi") \

#define HTML_MATH        \
X(8704,forall,"∀","For") \
X(8706,part,"∂","Part") \
X(8707,exist,"∃","Exist") \
X(8709,empty,"∅","Empty") \
X(8711,nabla,"∇","Nabla") \
X(8712,isin,"∈","Is") \
X(8713,notin,"∉","Not") \
X(8715,ni,"∋","Ni") \
X(8719,prod,"∏","Product") \
X(8721,sum,"∑","Sum") \
X(8722,minus,"−","Minus") \
X(8727,lowast,"∗","Asterisk") \
X(8730,radic,"√","Square") \
X(8733,prop,"∝","Proportional") \
X(8734,infin,"∞","Infinity") \
X(8736,ang,"∠","Angle") \
X(8743,and,"∧","And") \
X(8744,or,"∨","Or") \
X(8745,cap,"∩","Cap") \
X(8746,cup,"∪","Cup") \
X(8747,int,"∫","Integral") \
X(8756,there4,"∴","Therefore") \
X(8764,sim,"∼","Similar") \
X(8773,cong,"≅","Congurent") \
X(8776,asymp,"≈","Almost") \
X(8800,ne,"≠","Not") \
X(8801,equiv,"≡","Equivalent") \
X(8804,le,"≤","Less") \
X(8805,ge,"≥","Greater") \
X(8834,sub,"⊂","Subset") \
X(8835,sup,"⊃","Superset") \
X(8836,nsub,"⊄","Not") \
X(8838,sube,"⊆","Subset") \
X(8839,supe,"⊇","Superset") \
X(8853,oplus,"⊕","Circled") \
X(8855,otimes,"⊗","Circled") \
X(8869,perp,"⊥","Perpendicular") \
X(8901,sdot,"⋅","Dot") \

#define HTML_MISC \
X(338,OElig,"Œ","Uppercase") \
X(339,oelig,"œ","Lowercase") \
X(352,Scaron,"Š","Uppercase") \
X(353,scaron,"š","Lowercase") \
X(376,Yuml,"Ÿ","Capital") \
X(402,fnof,"ƒ","Lowercase") \
X(710,circ,"ˆ","Circumflex") \
X(732,tilde,"˜","Tilde") \
X(8194,ensp," ","En") \
X(8195,emsp," ","Em") \
X(8201,thinsp," ","Thin") \
X(8204,zwnj,"‌","Zero") \
X(8205,zwj,"‍","Zero") \
X(8206,lrm,"‎","Left-to-right") \
X(8207,rlm,"‏","Right-to-left") \
X(8211,ndash,"–","En") \
X(8212,mdash,"—","Em") \
X(8216,lsquo,"‘","Left") \
X(8217,rsquo,"’","Right") \
X(8218,sbquo,"‚","Single") \
X(8220,ldquo,"“","Left") \
X(8221,rdquo,"”","Right") \
X(8222,bdquo,"„","Double") \
X(8224,dagger,"†","Dagger") \
X(8225,Dagger,"‡","Double") \
X(8226,bull,"•","Bullet") \
X(8230,hellip,"…","Horizontal") \
X(8240,permil,"‰","Per") \
X(8242,prime,"′","Minutes") \
X(8243,Prime,"″","Seconds") \
X(8249,lsaquo,"‹","Single") \
X(8250,rsaquo,"›","Single") \
X(8254,oline,"‾","Overline") \
X(8364,euro,"€","Euro") \
X(8482,trade,"™","Trademark") \
X(8592,larr,"←","Left") \
X(8593,uarr,"↑","Up") \
X(8594,rarr,"→","Right") \
X(8595,darr,"↓","Down") \
X(8596,harr,"↔","Left") \
X(8629,crarr,"↵","Carriage") \
X(8968,lceil,"⌈","Left") \
X(8969,rceil,"⌉","Right") \
X(8970,lfloor,"⌊","Left") \
X(8971,rfloor,"⌋","Right") \
X(9674,loz,"◊","Lozenge") \
X(9824,spades,"♠","Spade") \
X(9827,clubs,"♣","Club") \
X(9829,hearts,"♥","Heart") \
X(9830,diams,"♦","Diamond") \

const char *toHtmlEntity(int uc)
{
    switch(uc)
    {
#define X(uc,name,is,about) case uc: return #name;
HTML_GREEK
HTML_MATH
HTML_MISC
#undef X
    }
    return NULL;
}

#endif // ENTITIES_MODULE

// PART C HASHMAP_C ////////////////////////////////////////

#ifdef HASHMAP_MODULE

struct HashMapNode;
typedef struct HashMapNode HashMapNode;

struct HashMapNode
{
    char *key_;
    void *val_;
    struct HashMapNode *next_;
};

struct HashMap
{
    int size_;
    int max_size_;
    HashMapNode** nodes_;
};

struct HashMapIterator
{
    HashMap *hm_;
    int offset_;
    struct HashMapNode *node_;
};

// FUNCTION DECLARATIONS //////////////////////////////////////////////////

size_t hash_code(const char *str);
HashMapNode* hashmap_create_node(const char *key);

///////////////////////////////////////////////////////////////////////////

size_t hash_code(const char *str)
{
    size_t hash = 0;
    size_t c;

    while (true)
    {
        c = *str++;
        if (!c) break;
        hash = c + (hash << 6) + (hash << 16) - hash; // sdbm
        // hash = hash * 33 ^ c; // djb2a
    }

    return hash;
}

HashMapNode* hashmap_create_node(const char *key)
{
    HashMapNode* new_node = (HashMapNode*) malloc(sizeof(HashMapNode));
    memset(new_node, 0, sizeof(*new_node));
    new_node->key_ = strdup(key);

    return new_node;
}

HashMap* hashmap_create(size_t max_size)
{
    HashMap* new_table = (HashMap*) malloc(sizeof(HashMap));
    memset(new_table, 0, sizeof(*new_table));

    new_table->nodes_ = (HashMapNode**)calloc(max_size, sizeof(HashMapNode*));
    new_table->max_size_ = max_size;
    new_table->size_ = 0;

    return new_table;
}

void hashmap_free_and_values(HashMap *map, FreeFuncPtr freefunc)
{
    for (int i=0; i < map->max_size_; ++i)
    {
        HashMapNode *node = map->nodes_[i];
        map->nodes_[i] = NULL;
        while (node)
        {
            if (node->key_) free((char*)node->key_);
            node->key_ = NULL;
            if (node->val_) freefunc(node->val_);
            node->val_ = NULL;
            HashMapNode *next = node->next_;
            free(node);
            node = next;
        }
    }
    map->size_ = 0;
    map->max_size_ = 0;
    free(map->nodes_);
    free(map);
}


void hashmap_put(HashMap* table, const char* key, void *val)
{
    assert(val != NULL);

    size_t index = hash_code(key) % table->max_size_;
    HashMapNode* slot = table->nodes_[index];
    HashMapNode* prev_slot = NULL;

    if (!slot)
    {
        // No hash in this position, create new node and fill slot.
        HashMapNode* new_node = hashmap_create_node(key);
        new_node->val_ = val;
        table->nodes_[index] = new_node;
        table->size_++;
        return;
    }

    // Look for a match...
    while (slot)
    {
        if (strcmp(slot->key_, key) == 0)
        {
            slot->val_ = val;
            return;
        }
        prev_slot = slot;
        slot = slot->next_;
    }

    // No matching node found, append to prev_slot
    HashMapNode* new_node = hashmap_create_node(key);
    new_node->val_ = val;
    prev_slot->next_ = new_node;
    table->size_++;
    return;
}

void *hashmap_get(HashMap* table, const char* key)
{
    int index = hash_code(key) % table->max_size_;
    HashMapNode* slot = table->nodes_[index];

    while (slot)
    {
        if (!strcmp(slot->key_, key))
        {
            return slot->val_;
        }
        slot = slot->next_;
    }

    return NULL;
}

size_t hashmap_size(HashMap* table)
{
    return table->size_;
}

void hashmap_free(HashMap* table)
{
    for (int i=0; i < table->max_size_; i++)
    {
        HashMapNode* slot = table->nodes_[i];
        while (slot != NULL)
        {
            HashMapNode* tmp = slot;
            if (tmp->key_) free((char*)tmp->key_);
            tmp->key_ = NULL;
            slot = slot ->next_;
            free(tmp);
        }
    }

    free(table->nodes_);
    free(table);
}

HashMapIterator *hashmap_iterate(HashMap *map)
{
    HashMapIterator* new_iterator = (HashMapIterator*) malloc(sizeof(HashMapIterator));
    memset(new_iterator, 0, sizeof(*new_iterator));
    new_iterator->hm_ = map;
    new_iterator->offset_ = -1;

    return new_iterator;
}

bool hashmap_next_key_value(HashMapIterator *i, const char **key, void **val)
{
    if (i->node_)
    {
        i->node_ = i->node_->next_;
    }
    if (i->node_)
    {
        *key = i->node_->key_;
        *val = i->node_->val_;
        return true;
    }
    do
    {
        i->offset_++;
    }
    while (i->offset_ < i->hm_->max_size_ && i->hm_->nodes_[i->offset_] == NULL);

    if (i->offset_ >= i->hm_->max_size_) return false;

    i->node_ = i->hm_->nodes_[i->offset_];
    *key = i->node_->key_;
    *val = i->node_->val_;
    return true;
}

void hashmap_free_iterator(HashMapIterator *i)
{
    free(i);
}

#endif // HASHMAP_MODULE

// PART C STACK_C ////////////////////////////////////////

#ifdef STACK_MODULE

StackElement *find_element_above(Stack *s, StackElement *b);

Stack *stack_create()
{
    Stack *s = (Stack*)malloc(sizeof(Stack));
    memset(s, 0, sizeof(Stack));
    return s;
}

void stack_free(Stack *stack)
{
    if (!stack) return;

    StackElement *e = stack->top;
    while (e)
    {
        StackElement *next = e->below;
        free(e);
        e = next;
    }

    free(stack);
}

void stack_push(Stack *stack, void *data)
{
    assert(stack);
    StackElement *element = (StackElement*)malloc(sizeof(StackElement));
    memset(element, 0, sizeof(StackElement));
    element->data = data;

    if (stack->top == NULL) {
        stack->top = stack->bottom = element;
    }
    else
    {
        element->below = stack->top;
        stack->top = element;
    }
    stack->size++;
}

void *stack_pop(Stack *stack)
{
    assert(stack);
    assert(stack->top);

    void *data = stack->top->data;
    StackElement *element = stack->top;
    stack->top = element->below;
    free(element);
    stack->size--;
    return data;
}

StackElement *find_element_above(Stack *s, StackElement *b)
{
    StackElement *e = s->top;

    for (;;)
    {
        if (!e) return NULL;
        if (e->below == b) return e;
        e = e->below;
    }

    return NULL;
}

void *stack_rock(Stack *stack)
{
    assert(stack);
    assert(stack->bottom);

    assert(stack->size > 0);

    if (stack->size == 1) return stack_pop(stack);

    StackElement *element = stack->bottom;
    void *data = element->data;
    StackElement *above = find_element_above(stack, element);
    assert(above);
    stack->bottom = above;
    above->below = NULL;
    free(element);
    stack->size--;

    return data;
}

#endif // STACK_MODULE

// PART C MEMBUFFER_C ////////////////////////////////////////

#ifdef MEMBUFFER_MODULE

/** Allocate a automatically growing membuffer. */
MemBuffer *new_membuffer()
{
    MemBuffer *mb = (MemBuffer*)malloc(sizeof(MemBuffer));
    check_malloc(mb);
    memset(mb, 0, sizeof(*mb));
    return mb;
}

/** Free the MemBuffer support struct but return the actual contents. */
char *free_membuffer_but_return_trimmed_content(MemBuffer *mb)
{
    char *b = mb->buffer_;
    b = (char*)realloc(b, mb->used_);
    free(mb);
    return b;
}

void free_membuffer_and_free_content(MemBuffer *mb)
{
    if (mb->buffer_) free(mb->buffer_);
    mb->buffer_ = NULL;
    free(mb);
}

void membuffer_reuse(MemBuffer *mb, char *start, size_t len)
{
    if (mb->buffer_) free(mb->buffer_);
    mb->buffer_ = start;
    mb->used_ = mb->max_ = len;
}

size_t pick_buffer_new_size(size_t max, size_t used, size_t add)
{
    assert(used <= max);
    if (used + add > max)
    {
        // Increment buffer with 1KiB.
        max = max + 1024;
    }
    if (used + add > max)
    {
        // Still did not fit? The add was more than 1 Kib. Lets add that.
        max = max + add;
    }
    assert(used + add <= max);

    return max;
}

void membuffer_append_region(MemBuffer *mb, const char *start, const char *stop)
{
    if (!start) return;
    if (!stop) stop = start + strlen(start);
    size_t add = stop-start;
    size_t max = pick_buffer_new_size(mb->max_, mb->used_, add);
    if (max > mb->max_)
    {
        mb->buffer_ = (char*)realloc(mb->buffer_, max);
        mb->max_ = max;
    }
    memcpy(mb->buffer_+mb->used_, start, add);
    mb->used_ += add;
}

void membuffer_append(MemBuffer *mb, const char *start)
{
    const char *i = start;
    char *to = mb->buffer_+mb->used_;
    const char *stop = mb->buffer_+mb->max_;

    while (*i)
    {
        if (to >= stop)
        {
            mb->used_ = to - mb->buffer_;
            size_t max = pick_buffer_new_size(mb->max_, mb->used_, 1);
            assert(max >= mb->max_);
            mb->buffer_ = (char*)realloc(mb->buffer_, max);
            mb->max_ = max;
            stop = mb->buffer_+mb->max_;
            to = mb->buffer_+mb->used_;
        }
        *to = *i;
        i++;
        to++;
    }
    mb->used_ = to-mb->buffer_;
}

void membuffer_append_char(MemBuffer *mb, char c)
{
    size_t max = pick_buffer_new_size(mb->max_, mb->used_, 1);
    if (max > mb->max_)
    {
        mb->buffer_ = (char*)realloc(mb->buffer_, max);
        mb->max_ = max;
    }
    memcpy(mb->buffer_+mb->used_, &c, 1);
    mb->used_++;
}

void membuffer_append_int(MemBuffer *mb, int i)
{
    size_t add = sizeof(i);
    size_t max = pick_buffer_new_size(mb->max_, mb->used_, add);
    if (max > mb->max_)
    {
        mb->buffer_ = (char*)realloc(mb->buffer_, max);
        mb->max_ = max;
    }
    memcpy(mb->buffer_+mb->used_, &i, sizeof(i));
    mb->used_ += add;
}

void membuffer_append_null(MemBuffer *mb)
{
    membuffer_append_char(mb, 0);
}

void membuffer_append_nl(MemBuffer *mb)
{
    membuffer_append_char(mb, '\n');
}

void membuffer_drop_last_null(MemBuffer *mb)
{
    char *buf = mb->buffer_;
    size_t used = mb->used_;
    if (used > 0 && buf[used-1] == 0)
    {
        mb->used_--;
    }
}

void membuffer_append_entity(MemBuffer *mb, char c)
{
    if (c == ' ') membuffer_append(mb, "&#32;");
    else if (c == '\n') membuffer_append(mb, "&#10;");
    else if (c == '\t') membuffer_append(mb, "&#9;");
    else if (c == '\r') membuffer_append(mb, "&#13;");
    else
    {
        assert(false);
    }
}

void membuffer_append_pointer(MemBuffer *mb, void *ptr)
{
    size_t add = sizeof(ptr);
    size_t max = pick_buffer_new_size(mb->max_, mb->used_, add);
    if (max > mb->max_)
    {
        mb->buffer_ = (char*)realloc(mb->buffer_, max);
        mb->max_ = max;
    }
    memcpy(mb->buffer_+mb->used_, &ptr, sizeof(ptr));
    mb->used_ += add;
}

void membuffer_logf(MemBuffer *mb, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    char *line = xmqLineVPrintf(&xmq_log_line_config_, fmt, args);
    membuffer_append(mb, line);
    free(line);
    va_end(args);
}

void membuffer_printf(MemBuffer *mb, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    char *buf = buf_vsnprintf(fmt, args);
    membuffer_append(mb, buf);
    free(buf);

    va_end(args);
}

size_t membuffer_used(MemBuffer *mb)
{
    return mb->used_;
}

char membuffer_back(MemBuffer *mb)
{
    if (mb->used_ == 0) return 0;
    return mb->buffer_[mb->used_-1];
}

void membuffer_prefix_lines(MemBuffer *mb, const char *prefix)
{
    MemBuffer *nb = new_membuffer();
    const char *last = mb->buffer_;
    const char *stop = mb->buffer_ + mb->used_;
    const char *i = mb->buffer_;

    while (i < stop)
    {
        if (*i == '\n')
        {
            membuffer_append(nb, prefix);
            membuffer_append_region(nb, last, i+1);
            last = i+1;
        }
        i++;
    }
    if (i > last)
    {
        membuffer_append(nb, prefix);
        membuffer_append_region(nb, last, i);
    }

    free(mb->buffer_);
    mb->max_ = nb->max_;
    mb->used_ = nb->used_;
    mb->buffer_ = nb->buffer_;
    nb->buffer_ = NULL;
    nb->used_ = 0;
    nb->max_ = 0;
    free(nb);
}

#endif // MEMBUFFER_MODULE

// PART C IXML_C ////////////////////////////////////////

#ifdef IXML_MODULE

#define DEBUG_IXML_GRAMMAR

#ifdef DEBUG_IXML_GRAMMAR
#define IXML_STEP(name,state) {                 \
    if (false && xmq_trace_enabled_) {                                \
        char *tmp = xmq_quote_as_c(state->i, state->i+10, false);           \
        for (int i=0; i<state->depth; ++i) trace("    "); \
        trace("dbg " #name " >%s...\n", tmp);       \
        for (int i=0; i<state->depth; ++i) trace("    "); \
        trace("{\n");      \
        state->depth++; \
        free(tmp); \
    } \
}
#define IXML_DONE(name,state) {                 \
    if (false && xmq_trace_enabled_) {                                \
        char *tmp = xmq_quote_as_c(state->i, state->i+10, false);           \
        state->depth--; \
        for (int i=0; i<state->depth; ++i) trace("    "); \
        trace("}\n"); \
        free(tmp); \
    } \
}

#define EAT(name, num) { \
    if (false && xmq_trace_enabled_) { \
        char *tmp = xmq_quote_as_c(state->i, state->i+num, false);        \
        for (int i=0; i<state->depth; ++i) fprintf(stderr, "    "); \
        fprintf(stderr, "eat %s %s\n", #name, tmp);       \
        free(tmp); \
    } \
    increment(0, num, &state->i, &state->line, &state->col); \
}
#define ASSERT(x) assert(x)
#else
#define IXML_STEP(name,state) {}
#define EAT(name, num) increment(0, num, &state->i, &state->line, &state->col);
#define ASSERT(x) {}
#endif

void add_insertion_rule(XMQParseState *state, const char *content);
void add_not_rule_string(XMQParseState *state, const char *content);
void add_not_rule_charset(XMQParseState *state, IXMLNonTerminal *nt);
void add_single_char_rule(XMQParseState *state, IXMLNonTerminal *nt, int uc, char mark, char tmark);

bool is_ixml_eob(XMQParseState *state);
bool is_ixml_alias_start(XMQParseState *state);
bool is_ixml_alt_start(XMQParseState *state);
bool is_ixml_alt_end(char c);
bool is_ixml_charset_start(XMQParseState *state);
int  is_ixml_code_start(XMQParseState *state);
bool is_ixml_comment_start(XMQParseState *state);
bool is_ixml_encoded_start(XMQParseState *state);
bool is_ixml_factor_start(XMQParseState *state);
bool is_ixml_group_start(XMQParseState *state);
bool is_ixml_group_end(XMQParseState *state);
bool is_ixml_hex_char(char c);
bool is_ixml_hex_start(XMQParseState *state);
bool is_ixml_insertion_start(XMQParseState *state);
bool is_ixml_not_start(XMQParseState *state);
bool is_ixml_literal_start(XMQParseState *state);
bool is_ixml_mark_char(char c);
bool is_ixml_name_follower(char c);
bool is_ixml_name_start(char c);
bool is_ixml_naming_char(char c);
bool is_ixml_naming_start(XMQParseState *state);
bool is_ixml_nonterminal_start(XMQParseState *state);
bool is_ixml_prolog_start(XMQParseState *state);
bool is_ixml_range_start(XMQParseState *state);
bool is_ixml_string_char(char c);
bool is_ixml_string_start(XMQParseState *state);
bool is_ixml_term_start(XMQParseState *state);
bool is_ixml_tmark_char(char c);
bool is_ixml_tmark_start(XMQParseState *state);
bool is_ixml_quote_start(char c);
bool is_ixml_quoted_start(XMQParseState *state);
bool is_ixml_rule_start(XMQParseState *state);
bool is_ixml_rule_end(char c);
bool is_ixml_terminal_start(XMQParseState *state);
bool is_ixml_whitespace_char(char c);
bool is_ixml_whitespace_start(XMQParseState *state);

void parse_ixml(XMQParseState *state);
void parse_ixml_alias(XMQParseState *state, const char **alias_start, const char **alias_stop);
void parse_ixml_alt(XMQParseState *state);
void parse_ixml_alts(XMQParseState *state);
IXMLNonTerminal *parse_ixml_charset(XMQParseState *state, int *tmark);
void parse_ixml_comment(XMQParseState *state);
void parse_ixml_encoded(XMQParseState *state, bool add_terminal);
void parse_ixml_factor(XMQParseState *state);
void parse_ixml_group(XMQParseState *state);
void parse_ixml_hex(XMQParseState *state, int *value);
void parse_ixml_insertion(XMQParseState *state);
void parse_ixml_not(XMQParseState *state);
void parse_ixml_literal(XMQParseState *state);
void parse_ixml_name(XMQParseState *state, const char **content_start, const char **content_stop);
void parse_ixml_naming(XMQParseState *state,
                       char *mark,
                       char **name,
                       char **alias);
void parse_ixml_term_nonterminal(XMQParseState *state);
void parse_ixml_prolog(XMQParseState *state);
void parse_ixml_range(XMQParseState *state);
void parse_ixml_quoted(XMQParseState *state);
void parse_ixml_rule(XMQParseState *state);
void parse_ixml_string(XMQParseState *state, int **content);
void parse_ixml_term(XMQParseState *state);
void parse_ixml_terminal(XMQParseState *state);
void parse_ixml_whitespace(XMQParseState *state);

void skip_comment(const char **i);
void skip_encoded(const char **i);
void skip_mark(const char **i);
void skip_string(const char **i);
void skip_tmark(const char **i);
void skip_whitespace(const char **i);

void allocate_yaep_tmp_terminals(XMQParseState *state);
void free_yaep_tmp_terminals(XMQParseState *state);
void free_yaep_tmp_terminals_and_content(XMQParseState *state);

bool has_ixml_tmp_terminals(XMQParseState *state);
bool has_more_than_one_ixml_tmp_terminals(XMQParseState *state);
void add_yaep_term_to_rule(XMQParseState *state, char mark, IXMLTerminal *t, IXMLNonTerminal *nt);
void add_yaep_terminal(XMQParseState *state, IXMLTerminal *terminal);
IXMLNonTerminal *lookup_yaep_nonterminal_already(XMQParseState *state, const char *name);
void add_yaep_nonterminal(XMQParseState *state, IXMLNonTerminal *nonterminal);
void add_yaep_tmp_term_terminal(XMQParseState *state, char *name, int code);
// Store all state->ixml_tmp_terminals on the rule rhs.
void add_yaep_tmp_terminals_to_rule(XMQParseState *state, IXMLRule *rule, char mark);

void do_ixml(XMQParseState *state);
void do_ixml_comment(XMQParseState *state, const char *start, const char *stop);
void do_ixml_rule(XMQParseState *state, const char *name_start, const char *name_stop);
void do_ixml_alt(XMQParseState *state);
void do_ixml_nonterminal(XMQParseState *state, const char *name_start, char *name_stop);
void do_ixml_option(XMQParseState *state);

IXMLRule *new_ixml_rule();
IXMLCharset *new_ixml_charset(bool exclude);
void new_ixml_charset_part(IXMLCharset *cs, int from, int to, const char *category);
void free_ixml_charset(IXMLCharset *cs);
IXMLNonTerminal *copy_ixml_nonterminal(IXMLNonTerminal *nt);
void free_ixml_term(IXMLTerm *t);

char *generate_rule_name(XMQParseState *state);
char *generate_charset_rule_name(IXMLCharset *cs, char mark);
void make_last_term_optional(XMQParseState *state);
void make_last_term_repeat(XMQParseState *state, char factor_mark);
void make_last_term_repeat_zero(XMQParseState *state, char factor_mark);
void make_last_term_repeat_infix(XMQParseState *state, char factor_mark, char infix_mark);
void make_last_term_repeat_zero_infix(XMQParseState *state, char factor_mark, char infix_mark);

//////////////////////////////////////////////////////////////////////////////////////

bool is_ixml_eob(XMQParseState *state)
{
    return state->i >= state->buffer_stop || *(state->i) == 0;
}

bool is_ixml_alias_start(XMQParseState *state)
{
    return *(state->i) == '>';
}

bool is_ixml_alt_start(XMQParseState *state)
{
    char c = *(state->i);
    return
        c == '+' || // Insertion +"hej" or +#a
        c == '!' || // Not lookahead !"hejsan" ![L]
        c == '#' || // encoded literal
        c == '(' || // Group ( "svej" | "hojt" )
        c == '"' || // "string"
        c == '\'' || // 'string'
        c == '[' || // Charset
        c == '~' || // Negative charset
        is_ixml_mark_char(c) || // @^-
        is_ixml_name_start(c);
}

bool is_ixml_alt_end(char c)
{
    return
        c == ';' || // rule : "a", "b" ; "c", "d" .
        c == '|'; // rule : "a", "b" | "c", "d" .
}

bool is_ixml_charset_start(XMQParseState *state)
{
    const char *i = state->i;

    skip_tmark(&i);

    if (*i == '~') i++;
    while (is_ixml_whitespace_char(*i)) i++;

    if (*i == '[') return true;

    return false;
}

int is_ixml_code_start(XMQParseState *state)
{
    char a = *(state->i);
    char b = *(state->i+1);
    bool capital = (a >= 'A' && a <= 'Z');
    if (!capital) return 0;
    // Most unicode category classes (codes) are two characters Nd, Zs, Lu etc.
    // Some aggregated classes are a single uppercase L,N
    // A single silly is LC.
    bool letter = ((b >= 'a' && b <= 'z') || b == 'C');
    if (!letter) return 1;
    return 2;
}

bool is_ixml_comment_start(XMQParseState *state)
{
    return *(state->i) == '{';
}

bool is_ixml_encoded_start(XMQParseState *state)
{
    // -encoded: (tmark, s)?, -"#", hex, s.

    const char *i = state->i;
    skip_tmark(&i);

    char c = *i;
    if (c == '#') return true;
    return false;
}

bool is_ixml_factor_start(XMQParseState *state)
{
    return
        is_ixml_terminal_start(state) ||
        is_ixml_nonterminal_start(state) ||
        is_ixml_insertion_start(state) ||
        is_ixml_not_start(state) ||
        is_ixml_group_start(state);
}

bool is_ixml_group_start(XMQParseState *state)
{
    return *(state->i) == '(';
}

bool is_ixml_group_end(XMQParseState *state)
{
    return *(state->i) == ')';
}

bool is_ixml_insertion_start(XMQParseState *state)
{
    return *(state->i) == '+';
}

bool is_ixml_not_start(XMQParseState *state)
{
    return *(state->i) == '!';
}

bool is_ixml_hex_char(char c)
{
    return
        (c >= 'A' && c <= 'F') ||
        (c >= 'a' && c <= 'f') ||
        (c >= '0' && c <= '9');
}

bool is_ixml_hex_start(XMQParseState *state)
{
    return is_ixml_hex_char(*(state->i));
}

bool is_ixml_literal_start(XMQParseState *state)
{
    const char *i = state->i;
    skip_tmark(&i);

    char c = *i;
    return c == '"' || c == '\'' || c == '#';
}

bool is_ixml_mark_char(char c)
{
    return
        c == '@' || // Add as attribute.
        c == '^' || // Add as element (default but can be used to override attribute).
        c == '-' || // Do not generate node.
        c == '*';   // Controlled ambiguity test.
}

bool is_ixml_name_follower(char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ||
        c == '-' || (c >= '0' && c <= '9') ;
}

bool is_ixml_name_start(char c)
{
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}

bool is_ixml_naming_char(char c)
{
    return is_ixml_name_start(c) || is_ixml_mark_char(c);
}

bool is_ixml_naming_start(XMQParseState *state)
{
    return is_ixml_naming_char(*(state->i));
}

bool is_ixml_nonterminal_start(XMQParseState *state)
{
    return
        is_ixml_naming_start(state);
}

bool is_ixml_prolog_start(XMQParseState *state)
{
    // Detect "ixml ", "ixml\n", "ixml{}"
    return !strncmp(state->i, "ixml", 4) && is_ixml_whitespace_char(*(state->i+4));
}

bool is_ixml_range_start(XMQParseState *state)
{
    // -range: from, s, -"-", s, to.
    // @from: character.
    // @to: character.
    // -character: -'"', dchar, -'"';
    //             -"'", schar, -"'";
    //             "#", hex.

    const char *j = state->i;
    if (is_ixml_string_start(state))
    {
        skip_string(&j);
        skip_whitespace(&j);
        if (*j == '-') return true;
        return false;
    }
    else if (is_ixml_encoded_start(state))
    {
        skip_encoded(&j);
        skip_whitespace(&j);
        if (*j == '-') return true;
        return false;
    }

    return false;
}

bool is_ixml_string_char(char c)
{
    // Detect "howdy "" there" or 'howdy '' there'
    return c == '"' || c== '\'';
}

bool is_ixml_string_start(XMQParseState *state)
{
    return is_ixml_string_char(*(state->i));
}

bool is_ixml_term_start(XMQParseState *state)
{
    return is_ixml_factor_start(state);
}

bool is_ixml_quote_start(char c)
{
    return c == '"' || c == '\'';
}

bool is_ixml_quoted_start(XMQParseState *state)
{
    //  -quoted: (tmark, s)?, string, s.

    const char *i = state->i;
    skip_tmark(&i);
    char c = *i;
    return c == '"' || c  == '\'';
}

bool is_ixml_rule_start(XMQParseState *state)
{
    //  rule: (mark, s)?, name,
    if (is_ixml_naming_start(state)) return true;

    return false;
}

bool is_ixml_rule_end(char c)
{
    return c == '.'; // rule : "a", "b" ; "c", "d" .
}

bool is_ixml_terminal_start(XMQParseState *state)
{
    return
        is_ixml_encoded_start(state) ||
        is_ixml_literal_start(state) ||
        is_ixml_charset_start(state);
}

bool is_ixml_tmark_char(char c)
{
    return
        c == '^' || // Add as element (default but can be used to override attribute).
        c == '-';   // Do not generate node.
}

bool is_ixml_tmark_start(XMQParseState *state)
{
    return is_ixml_tmark_char(*(state->i));
}

bool is_ixml_whitespace_char(char c)
{
    return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '{' || c == '}';
}

bool is_ixml_whitespace_start(XMQParseState *state)
{
    return is_ixml_whitespace_char(*(state->i));
}

/////////////////////////////////////////////////////////////////////////////

void parse_ixml(XMQParseState *state)
{
    xmlNodePtr root = xmlNewDocNode(state->doq->docptr_.xml, NULL, (const xmlChar *)"ixml", NULL);
    state->element_last = root;
    xmlDocSetRootElement(state->doq->docptr_.xml, root);
    state->doq->root_.node = root;
    stack_push(state->element_stack, state->element_last);

    // ixml: s, prolog?, rule++RS, s.
    parse_ixml_whitespace(state);

    if (is_ixml_prolog_start(state))
    {
        parse_ixml_prolog(state);
        parse_ixml_whitespace(state);
    }

    if (!is_ixml_rule_start(state))
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected rule here";
        longjmp(state->error_handler, 1);
    }

    while (is_ixml_rule_start(state))
    {
        parse_ixml_rule(state);
    }

    parse_ixml_whitespace(state);
}

void parse_ixml_alias(XMQParseState *state, const char **alias_start, const char **alias_stop)
{
    IXML_STEP(alias, state);

    ASSERT(is_ixml_alias_start(state));
    EAT(alias_start, 1);

    parse_ixml_whitespace(state);

    parse_ixml_name(state, alias_start, alias_stop);

    parse_ixml_whitespace(state);

    IXML_DONE(alias, state);
}

void parse_ixml_alt(XMQParseState *state)
{
    // alt: term**(-",", s).
    IXML_STEP(alt, state);

    for (;;)
    {
        if (!is_ixml_alt_start(state))
        {
            state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR; // TODO
            state->error_info = "expected term here";
            longjmp(state->error_handler, 1);
        }
        parse_ixml_term(state);

        parse_ixml_whitespace(state);

        char c = *(state->i);
        if (is_ixml_alt_end(c) ||
            is_ixml_group_end(state) ||
            is_ixml_rule_end(c)) break;

        if (c != ',')
        {
            state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
            state->error_info = "expected , or . here";
            longjmp(state->error_handler, 1);
        }
        EAT(comma, 1);

        parse_ixml_whitespace(state);
    }


    IXML_DONE(alt, state);
}

void parse_ixml_alts(XMQParseState *state)
{
    IXML_STEP(alts, state);

    // alts: alt++(-[";|"], s).
    for (;;)
    {
        parse_ixml_whitespace(state);

        if (is_ixml_eob(state) ||
            is_ixml_rule_end(*(state->i)) ||
            is_ixml_group_end(state)) break;

        char c = *(state->i);
        if (c != '|')
        {
            if (!is_ixml_alt_start(state))
            {
                state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR; // TODO
                state->error_info = "expected alt here";
                longjmp(state->error_handler, 1);
            }
            parse_ixml_alt(state);

            parse_ixml_whitespace(state);
        }

        c = *(state->i);
        if (is_ixml_rule_end(c) || is_ixml_group_end(state)) break;
        if (c != '|' && c != ';')
        {
            state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
            state->error_info = "expected ; or | here";
            longjmp(state->error_handler, 1);
        }
        EAT(choice, 1);

        // The yaep grammar performs | by having mutiple rule entries
        // with the same name. We reached an alternative version of this
        // rule, create a new rule with the same name.
        parse_ixml_whitespace(state);
        IXMLNonTerminal *name = state->ixml_rule->rule_name;
        char mark = state->ixml_rule->mark;
        state->ixml_rule = new_ixml_rule();
        state->ixml_rule->rule_name->name = strdup(name->name);
        if (name->alias)
        {
            state->ixml_rule->rule_name->alias = strdup(name->alias);
        }
        state->ixml_rule->mark = mark;
        vector_push_back(state->ixml_rules, state->ixml_rule);
    }

    IXML_DONE(alts, state);
}

IXMLNonTerminal *parse_ixml_charset(XMQParseState *state, int *out_tmark)
{
    IXML_STEP(charset, state);
    ASSERT(is_ixml_charset_start(state));

    char tmark = 0;
    if (is_ixml_tmark_char(*(state->i)))
    {
        tmark = *(state->i);
        EAT(tmark, 1);
        parse_ixml_whitespace(state);
    }

    char category[3];
    category[0] = 0;
    bool exclude = false;
    if (*(state->i) == '~')
    {
        exclude = true;
        EAT(negate, 1);
        parse_ixml_whitespace(state);
    }

    ASSERT(*(state->i) == '[');

    EAT(left_bracket, 1);
    parse_ixml_whitespace(state);

    state->ixml_charset = new_ixml_charset(exclude);

    for (;;)
    {
        if (is_ixml_eob(state))
        {
            state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
            state->error_info = "charset is not closed";
            longjmp(state->error_handler, 1);
        }
        else if (is_ixml_range_start(state))
        {
            parse_ixml_range(state);
            new_ixml_charset_part(state->ixml_charset,
                                  state->ixml_charset_from,
                                  state->ixml_charset_to,
                                  "");
        }
        else if (is_ixml_encoded_start(state))
        {
            parse_ixml_encoded(state, false);
            new_ixml_charset_part(state->ixml_charset,
                                  state->ixml_encoded,
                                  state->ixml_encoded,
                                  "");
        }
        else if (is_ixml_code_start(state))
        {
            // Category can be Lc or just L.
            int n = is_ixml_code_start(state);
            category[0] = *state->i;
            category[1] = 0;
            if (n > 1) category[1] = *(state->i+1);
            category[2] = 0;
            EAT(unicode_category, n);
            const char **check = unicode_lookup_category_parts(category);
            if (!check)
            {
                state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
                state->error_info = "unknown unicode category";
                longjmp(state->error_handler, 1);
            }
            parse_ixml_whitespace(state);
            new_ixml_charset_part(state->ixml_charset,
                                  0,
                                  0,
                                  category);
        }
        else if (is_ixml_string_start(state))
        {
            int *content = NULL;
            parse_ixml_string(state, &content);
            int *i = content;
            while (*i)
            {
                new_ixml_charset_part(state->ixml_charset,
                                      *i,
                                      *i,
                                      "");
                i++;
            }
            free(content);
            parse_ixml_whitespace(state);
        }

        char c = *(state->i);
        if (c == ']') break;
        if (c != ';' && c != '|')
        {
            state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
            state->error_info = "expected ; or |";
            longjmp(state->error_handler, 1);
        }

        EAT(next_charset_part, 1);
        parse_ixml_whitespace(state);
    }

    EAT(right_bracket, 1);
    IXML_DONE(charset, state);

    char *cs_name = generate_charset_rule_name(state->ixml_charset, tmark);
    IXMLNonTerminal *nt = lookup_yaep_nonterminal_already(state, cs_name);
    if (!nt)
    {
        nt = new_ixml_nonterminal();
        nt->name = cs_name;
        nt->charset = state->ixml_charset;
        add_yaep_nonterminal(state, nt);
    }
    else
    {
        free_ixml_charset(state->ixml_charset);
        state->ixml_charset = NULL;
        free(cs_name);
    }
    *out_tmark = tmark;
    return nt;
}

void parse_ixml_comment(XMQParseState *state)
{
    IXML_STEP(comment, state);
    assert (*(state->i) == '{');

    EAT(comment_start, 1);

    for (;;)
    {
        if (is_ixml_eob(state))
        {
            state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
            state->error_info = "comment is not closed";
            longjmp(state->error_handler, 1);
        }
        if (*(state->i) == '{')
        {
            parse_ixml_comment(state);
        }
        if (*(state->i) == '}') break;
        EAT(comment_inside, 1);
    }
    EAT(comment_stop, 1);

    IXML_DONE(comment, state);
}

void parse_ixml_encoded(XMQParseState *state, bool add_terminal)
{
    IXML_STEP(encoded, state);
    ASSERT(is_ixml_encoded_start(state));

    int tmark = 0;
    if (is_ixml_tmark_start(state))
    {
        tmark = *(state->i);
        EAT(encoded_tmark, 1);
    }

    parse_ixml_whitespace(state);

    char c = *(state->i);
    ASSERT(c == '#');
    EAT(hash, 1);

    if (!is_ixml_hex_start(state))
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected hex after #";
        longjmp(state->error_handler, 1);
    }

    int value;
    parse_ixml_hex(state, &value);
    state->ixml_mark = tmark;
    state->ixml_encoded = value;

    parse_ixml_whitespace(state);

    if (add_terminal)
    {
        char buffer[16];
        snprintf(buffer, 15, "#%x", value);

        add_yaep_tmp_term_terminal(state, strdup(buffer), value);
    }

    IXML_DONE(encoded, state);
}

void parse_ixml_factor(XMQParseState *state)
{
    IXML_STEP(factor, state);
    ASSERT(is_ixml_factor_start(state));

    if (is_ixml_terminal_start(state))
    {
        allocate_yaep_tmp_terminals(state);

        parse_ixml_terminal(state);

        if (has_ixml_tmp_terminals(state))
        {
            if (has_more_than_one_ixml_tmp_terminals(state))
            {
                // Create a group.
                IXMLNonTerminal *nt = new_ixml_nonterminal();
                nt->name = generate_rule_name(state);
                add_yaep_nonterminal(state, nt);
                add_yaep_term_to_rule(state, '-', NULL, nt);
                stack_push(state->ixml_rule_stack, state->ixml_rule);
                state->ixml_rule = new_ixml_rule();
                state->ixml_rule->mark = '-';
                vector_push_back(state->ixml_rules, state->ixml_rule);

                free_ixml_nonterminal(state->ixml_rule->rule_name);
                state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

                // Add the terminals to the inner group/rule.
                add_yaep_tmp_terminals_to_rule(state, state->ixml_rule, state->ixml_mark);

                // Pop back out.
                state->ixml_rule = (IXMLRule*)stack_pop(state->ixml_rule_stack);
            }
            else
            {
                add_yaep_tmp_terminals_to_rule(state, state->ixml_rule, state->ixml_mark);
            }
        }

        free_yaep_tmp_terminals(state);
    }
    else if (is_ixml_nonterminal_start(state))
    {
        state->ixml_nonterminal = new_ixml_nonterminal();

        parse_ixml_term_nonterminal(state);

        add_yaep_nonterminal(state, state->ixml_nonterminal);
        add_yaep_term_to_rule(state, state->ixml_mark, NULL, state->ixml_nonterminal);
    }
    else  if (is_ixml_insertion_start(state))
    {
        parse_ixml_insertion(state);
    }
    else  if (is_ixml_not_start(state))
    {
        parse_ixml_not(state);
    }
    else if (is_ixml_group_start(state))
    {
        parse_ixml_group(state);
    }

    parse_ixml_whitespace(state);

    IXML_DONE(factor, state);
}

void parse_ixml_group(XMQParseState *state)
{
    IXML_STEP(group, state);
    ASSERT(is_ixml_group_start(state));

    EAT(left_par, 1);

    parse_ixml_whitespace(state);

    if (is_ixml_alt_start(state))
    {
        IXMLNonTerminal *nt = new_ixml_nonterminal();
        nt->name = generate_rule_name(state);

        add_yaep_nonterminal(state, nt);
        add_yaep_term_to_rule(state, '-', NULL, nt);

        stack_push(state->ixml_rule_stack, state->ixml_rule);
        state->ixml_rule = new_ixml_rule();
        state->ixml_rule->mark = '-';
        vector_push_back(state->ixml_rules, state->ixml_rule);

        free_ixml_nonterminal(state->ixml_rule->rule_name);
        state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

        parse_ixml_alts(state);

        state->ixml_rule = (IXMLRule*)stack_pop(state->ixml_rule_stack);
    }
    else if (*(state->i) != ')')
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected alts in group";
        longjmp(state->error_handler, 1);
    }

    if (*(state->i) != ')')
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected ) to close group";
        longjmp(state->error_handler, 1);
    }
    EAT(right_par, 1);

    parse_ixml_whitespace(state);

    IXML_DONE(factor, state);
}

void parse_ixml_hex(XMQParseState *state, int *value)
{
    IXML_STEP(hex,state);

    const char *start = state->i;
    while (is_ixml_hex_start(state))
    {
        EAT(hex_inside, 1);
    }
    const char *stop = state->i;
    char *hex = strndup(start, stop-start);
    *value = (int)strtol(hex, NULL, 16);
    free(hex);

    IXML_DONE(hex, state);
}

void parse_ixml_insertion(XMQParseState *state)
{
    IXML_STEP(insertion,state);

    ASSERT(is_ixml_insertion_start(state));

    EAT(insertion_plus, 1);

    parse_ixml_whitespace(state);

    if (is_ixml_string_start(state))
    {
        int *content = NULL;
        parse_ixml_string(state, &content);
        size_t len;
        for (len = 0; content[len]; ++len);
        // If all require 4 bytes of utf8, then this is the max length.
        char *buf = (char*)malloc(len*4 + 1);
        size_t offset = 0;
        for (size_t i = 0; i < len; ++i)
        {
            UTF8Char c;
            size_t l = encode_utf8(content[i], &c);
            strncpy(buf+offset, c.bytes, l);
            offset += l;
        }
        buf[offset] = 0;
        add_insertion_rule(state, buf);
        free(buf);
        free(content);
    }
    else if (is_ixml_encoded_start(state))
    {
        allocate_yaep_tmp_terminals(state);
        parse_ixml_encoded(state, true);
        UTF8Char c;
        encode_utf8(state->ixml_encoded, &c);
        char buf[16];
        snprintf(buf, 16, "#%x", state->ixml_encoded),
        add_insertion_rule(state, buf);
        free_yaep_tmp_terminals_and_content(state);
    }
    else
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected string or encoded character after insertion +";
        longjmp(state->error_handler, 1);
    }

    IXML_DONE(insertion, state);
}

void parse_ixml_not(XMQParseState *state)
{
    IXML_STEP(insertion,state);

    ASSERT(is_ixml_not_start(state));

    EAT(not_bang, 1);

    parse_ixml_whitespace(state);

    if (is_ixml_string_start(state))
    {
        int *content = NULL;
        parse_ixml_string(state, &content);
        size_t len;
        for (len = 0; content[len]; ++len);
        // If all require 4 bytes of utf8, then this is the max length.
        char *buf = (char*)malloc(len*4 + 1);
        size_t offset = 0;
        for (size_t i = 0; i < len; ++i)
        {
            UTF8Char c;
            size_t l = encode_utf8(content[i], &c);
            strncpy(buf+offset, c.bytes, l);
            offset += l;
        }
        buf[offset] = 0;
        add_not_rule_string(state, buf);
        free(buf);
        free(content);
    }
    else if (is_ixml_encoded_start(state))
    {
        allocate_yaep_tmp_terminals(state);
        parse_ixml_encoded(state, true);
        UTF8Char c;
        encode_utf8(state->ixml_encoded, &c);
        add_not_rule_string(state, c.bytes);
        free_yaep_tmp_terminals_and_content(state);
    }
    else if (is_ixml_charset_start(state))
    {
        int tmark;
        IXMLNonTerminal *nt = parse_ixml_charset(state, &tmark);
        add_not_rule_charset(state, nt);
    }
    else
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected string or encoded character after insertion +";
        longjmp(state->error_handler, 1);
    }

    IXML_DONE(insertion, state);
}

void parse_ixml_literal(XMQParseState *state)
{
    IXML_STEP(literal,state);

    ASSERT(is_ixml_literal_start(state));

    if (is_ixml_quoted_start(state))
    {
        parse_ixml_quoted(state);
    }
    else
    {
        parse_ixml_encoded(state, true);
    }

    IXML_DONE(literal, state);
}

void parse_ixml_name(XMQParseState *state, const char **name_start, const char **name_stop)
{
    IXML_STEP(name,state);

    assert(is_ixml_name_start(*(state->i)));
    *name_start = state->i;
    EAT(name_start, 1);

    while (is_ixml_name_follower(*(state->i)))
    {
        EAT(name_inside, 1);
    }
    *name_stop = state->i;

    IXML_DONE(name, state);
}

void parse_ixml_naming(XMQParseState *state,
                       char *mark,
                       char **name,
                       char **alias)
{
    IXML_STEP(naming,state);

    ASSERT(is_ixml_naming_start(state));

    if (is_ixml_mark_char(*(state->i)))
    {
        *mark = (*(state->i));
        EAT(naming_mark, 1);

        if (*mark == '*')
        {
            state->ixml_controlled_ambiguity_enabled = true;
        }
    }
    else
    {
        *mark = 0;
    }
    parse_ixml_whitespace(state);

    if (!is_ixml_name_start(*(state->i)))
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected a name";
        longjmp(state->error_handler, 1);
    }
    const char *name_start, *name_stop;
    parse_ixml_name(state, &name_start, &name_stop);
    *name = strndup(name_start, (name_stop-name_start));

    parse_ixml_whitespace(state);

    if (is_ixml_alias_start(state))
    {
        const char *alias_start, *alias_stop;
        parse_ixml_alias(state, &alias_start, &alias_stop);
        *alias = strndup(alias_start, alias_stop-alias_start);
    }
    else
    {
        *alias = NULL;
    }

    IXML_DONE(naming, state);
}

void parse_ixml_term_nonterminal(XMQParseState *state)
{
    IXML_STEP(nonterminal, state);
    ASSERT(is_ixml_naming_start(state));

    IXMLNonTerminal *nt = state->ixml_nonterminal;
    parse_ixml_naming(state, &state->ixml_mark, &nt->name, &nt->alias);

    IXML_DONE(nonterminal, state);
}

void parse_ixml_prolog(XMQParseState *state)
{
    IXML_STEP(prolog, state);
    // version: -"ixml", RS, -"version", RS, string, s, -'.' .
    // Example: ixml version "1.2.3-gurka" .

    assert(is_ixml_prolog_start(state));
    EAT(prolog_ixml, 4);

    parse_ixml_whitespace(state);

    if (strncmp(state->i, "version", 7))
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected \"version\" ";
        longjmp(state->error_handler, 1);
    }

    EAT(prolog_version, 7);

    if (!is_ixml_whitespace_start(state))
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected whitespace";
        longjmp(state->error_handler, 1);
    }

    parse_ixml_whitespace(state);

    if (!is_ixml_string_start(state))
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected string";
        longjmp(state->error_handler, 1);
    }

    int *content;
    parse_ixml_string(state, &content);
    free(content);

    parse_ixml_whitespace(state);

    char c = *(state->i);
    if (c != '.')
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = ": ixml version must end with a dot";
        longjmp(state->error_handler, 1);
    }
    EAT(prolog_stop, 1);

    IXML_DONE(prolog, state);
}

void parse_ixml_range(XMQParseState *state)
{
    IXML_STEP(range, state);
    ASSERT(is_ixml_range_start(state));

    if (is_ixml_string_start(state))
    {
        int *content;
        parse_ixml_string(state, &content);
        state->ixml_charset_from = content[0];
        free(content);
    }
    else
    {
        parse_ixml_encoded(state, false);
        state->ixml_charset_from = state->ixml_encoded;
    }
    parse_ixml_whitespace(state);

    // This is guaranteed by the is range test in the assert.
    ASSERT(*(state->i) == '-');
    EAT(range_minus, 1);

    parse_ixml_whitespace(state);

    if (is_ixml_string_start(state))
    {
        int *content;
        parse_ixml_string(state, &content);
        state->ixml_charset_to = content[0];
        free(content);
    }
    else if (is_ixml_encoded_start(state))
    {
        parse_ixml_encoded(state, false);
        state->ixml_charset_to = state->ixml_encoded;
    }
    else
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected range ending with string or hex char";
        longjmp(state->error_handler, 1);
    }
    parse_ixml_whitespace(state);

    IXML_DONE(range, state);
}

void parse_ixml_quoted(XMQParseState *state)
{
    IXML_STEP(quoted, state);
    // -quoted: (tmark, s)?, string, s.

    ASSERT(is_ixml_quoted_start(state));

    char tmark = 0;
    if (is_ixml_tmark_start(state))
    {
        tmark = *(state->i);
        EAT(quoted_tmark, 1);
        parse_ixml_whitespace(state);
    }

    state->ixml_mark = tmark;

    int *content = NULL;
    parse_ixml_string(state, &content);

    for (int *i = content; *i; ++i)
    {
        char buf[16];
        snprintf(buf, 15, "#%x", *i);
        add_yaep_tmp_term_terminal(state, strdup(buf), *i);
    }
    free(content);

    parse_ixml_whitespace(state);

    IXML_DONE(quoted, state);
}

void parse_ixml_rule(XMQParseState *state)
{
    // rule: naming, -["=:"], s, -alts, -".".
    IXML_STEP(rule, state);
    ASSERT(is_ixml_naming_start(state));

    stack_push(state->ixml_rule_stack, state->ixml_rule);

    state->ixml_rule = new_ixml_rule();
    vector_push_back(state->ixml_rules, state->ixml_rule);

    parse_ixml_naming(state,
                      &state->ixml_rule->mark,
                      &state->ixml_rule->rule_name->name,
                      &state->ixml_rule->rule_name->alias);

    parse_ixml_whitespace(state);

    char c = *(state->i);
    if (c != '=' && c != ':')
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected equal or colon here";
        longjmp(state->error_handler, 1);
    }
    EAT(rule_equal, 1);
    while (*(state->i) == '<')
    {
        state->ixml_rule->cost++;
        state->i++;
        state->ixml_costs_enabled = true;
    }

    parse_ixml_whitespace(state);

    parse_ixml_alts(state);

    c = *(state->i);
    if (c != '.')
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected dot here";
        longjmp(state->error_handler, 1);
    }
    EAT(rule_stop, 1);

    parse_ixml_whitespace(state);

    state->ixml_rule = (IXMLRule*)stack_pop(state->ixml_rule_stack);

    IXML_DONE(rule, state);
}

void parse_ixml_string(XMQParseState *state, int **content)
{
    IXML_STEP(string, state);

    MemBuffer *buf = new_membuffer();

    ASSERT(is_ixml_string_start(state));

    char q = *(state->i);
    EAT(string_start, 1);

    for (;;)
    {
        if (is_ixml_eob(state))
        {
            state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
            state->error_info = "string not terminated";
            longjmp(state->error_handler, 1);
        }

        if (*(state->i) == q)
        {
            if (*(state->i+1) == q)
            {
                // A double '' or "" means a single ' or " inside the string.
                EAT(string_quote, 1);
            }
            else
            {
                EAT(string_stop, 1);
                break;
            }
        }

        int uc = 0;
        size_t len = 0;
        bool ok = decode_utf8(state->i, state->buffer_stop, &uc, &len);
        if (!ok)
        {
            fprintf(stderr, "xmq: broken utf8\n");
            exit(1);
        }
        membuffer_append_int(buf, uc);
        EAT(string_inside, len);
    }
    // Add a zero termination to the integer array.
    membuffer_append_int(buf, 0);

    char *quote = free_membuffer_but_return_trimmed_content(buf);
    *content = (int*)quote;

    IXML_DONE(string, state);
}

void parse_ixml_term(XMQParseState *state)
{
    IXML_STEP(term, state);
    ASSERT(is_ixml_factor_start(state));

    if (is_ixml_factor_start(state))
    {
        parse_ixml_factor(state);
    }
    else
    {
        state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
        state->error_info = "expected factor";
        longjmp(state->error_handler, 1);
    }

    char factor_mark = state->ixml_mark;
    char c = *(state->i);
    if (c == '?')
    {
        EAT(option, 1);
        parse_ixml_whitespace(state);

        make_last_term_optional(state);
    }
    else if (c == '*' || c == '+')
    {
        // Remember the mark for the member.
        if (c == '*')
        {
            EAT(star, 1);
        }
        else
        {
            EAT(plus, 1);
        }

        char cc = *(state->i);
        if (c == cc)
        {
            // We have value++infix
            if (c == '*')
            {
                EAT(star, 1);
            }
            else
            {
                EAT(plus, 1);
            }
            parse_ixml_whitespace(state);

            // infix separator
            parse_ixml_factor(state);
            char infix_mark = state->ixml_mark;

            // We have value++infix value**infix
            if (c == '+') make_last_term_repeat_infix(state, factor_mark, infix_mark);
            else  make_last_term_repeat_zero_infix(state, factor_mark, infix_mark);

        }
        else
        {
            // We have value+ or value*
            if (c == '+') make_last_term_repeat(state, factor_mark);
            else  make_last_term_repeat_zero(state, factor_mark);
        }
        parse_ixml_whitespace(state);
    }
    parse_ixml_whitespace(state);

    IXML_DONE(term, state);
}

void parse_ixml_terminal(XMQParseState *state)
{
    IXML_STEP(terminal, state);
    ASSERT(is_ixml_literal_start(state) || is_ixml_charset_start(state));

    if (is_ixml_literal_start(state))
    {
        parse_ixml_literal(state);
    }
    else
    {
        int tmark;
        IXMLNonTerminal *nt = parse_ixml_charset(state, &tmark);
        add_yaep_term_to_rule(state, tmark, NULL, nt);
    }

    IXML_DONE(terminal, state);
}

void parse_ixml_whitespace(XMQParseState *state)
{
    if (is_ixml_eob(state) || !is_ixml_whitespace_start(state)) return;

    IXML_STEP(ws, state);

    while (state->i < state->buffer_stop && is_ixml_whitespace_start(state))
    {
        if (is_ixml_comment_start(state))
        {
            parse_ixml_comment(state);
        }
        else
        {
            EAT(ws, 1);
        }
    }

    IXML_DONE(ws, state);
}


const char *ixml_to_yaep_read_terminal(YaepParseRun *pr,
                                       YaepGrammar *g,
                                       int *code)
{
    XMQParseState *state = (XMQParseState*)pr->user_data;
    const char *key;
    void *val;
    bool ok = hashmap_next_key_value(state->yaep_i_, &key, &val);

    if (ok)
    {
        IXMLTerminal *t = (IXMLTerminal*)val;
        *code = t->code;
        return t->name;
    }

    return NULL;
}

const char *ixml_to_yaep_read_rule(YaepParseRun *pr,
                                   YaepGrammar *g,
                                   const char ***rhs,
                                   const char **abs_node,
                                   int *cost,
                                   int **transl,
                                   char *mark,
                                   char **marks)
{
    XMQParseState *state = (XMQParseState*)pr->user_data;
    if (state->yaep_j_ >= state->ixml_rules->size) return NULL;
    IXMLRule *rule = (IXMLRule*)vector_element_at(state->ixml_rules, state->yaep_j_);

    // This is a valid rule.
    size_t num_rhs = rule->rhs_terms->size;

    // Add rhs rules as translations.
    if (state->yaep_tmp_rhs_) free(state->yaep_tmp_rhs_);
    state->yaep_tmp_rhs_ = (char**)calloc(num_rhs+1, sizeof(char*));
    if (state->yaep_tmp_marks_) free(state->yaep_tmp_marks_);
    state->yaep_tmp_marks_ = (char*)calloc(num_rhs+1, sizeof(char));
    memset(state->yaep_tmp_marks_, 0, num_rhs+1);

    for (size_t i = 0; i < num_rhs; ++i)
    {
        IXMLTerm *term = (IXMLTerm*)vector_element_at(rule->rhs_terms, i);
        state->yaep_tmp_marks_[i] = term->mark;
        if (term->type == IXML_TERMINAL)
        {
            state->yaep_tmp_rhs_[i] = term->t->name;
        }
        else if (term->type == IXML_NON_TERMINAL)
        {
            state->yaep_tmp_rhs_[i] = term->nt->name;
        }
        else
        {
            fprintf(stderr, "Internal error %d as term type does not exist!\n", term->type);
            assert(false);
        }
    }

    *abs_node = rule->rule_name->name;
    if (rule->rule_name->alias)
    {
        *abs_node = rule->rule_name->alias;
    }

    if (state->yaep_tmp_transl_) free(state->yaep_tmp_transl_);
    state->yaep_tmp_transl_ = (int*)calloc(num_rhs+1, sizeof(char*));
    for (size_t i = 0; i < num_rhs; ++i)
    {
        state->yaep_tmp_transl_[i] = (int)i;
    }
    state->yaep_tmp_transl_[num_rhs] = -1;

    state->yaep_tmp_rhs_[num_rhs] = NULL;
    *rhs = (const char **)state->yaep_tmp_rhs_;
    *marks = state->yaep_tmp_marks_;
    *transl = state->yaep_tmp_transl_;
    *cost = rule->cost;
    *mark = rule->mark;

    state->yaep_j_++;
    return rule->rule_name->name;
}

bool ixml_build_yaep_grammar(YaepParseRun *pr,
                             YaepGrammar *g,
                             XMQParseState *state,
                             const char *grammar_start,
                             const char *grammar_stop,
                             const char *content_start,
                             const char *content_stop)
{
    if (state->magic_cookie != MAGIC_COOKIE)
    {
        PRINT_ERROR("Parser state not initialized!\n");
        assert(0);
        exit(1);
    }

    pr->user_data = state;
    pr->grammar = g;
    state->ixml_rules = vector_create();
    state->ixml_terminals_map = hashmap_create(256);
    state->ixml_non_terminals_map = hashmap_create(256);
    state->ixml_non_terminals = vector_create();
    state->ixml_rule_stack = stack_create();

    state->buffer_start = grammar_start;
    state->buffer_stop = grammar_stop;
    state->i = grammar_start;
    state->line = 1;
    state->col = 1;
    state->error_nr = XMQ_ERROR_NONE;

    if (state->parse && state->parse->init) state->parse->init(state);

    if (!setjmp(state->error_handler))
    {
        // Parse the IXML grammar.
        parse_ixml(state);
        if (state->i < state->buffer_stop)
        {
            state->error_nr = XMQ_ERROR_IXML_SYNTAX_ERROR;
            state->error_info = "failed to parse whole buffer";
            longjmp(state->error_handler, 1);
        }
    }
    else
    {
        XMQParseError error_nr = state->error_nr;
        generate_state_error_message(state, error_nr, grammar_start, grammar_stop);
        return false;
    }

    if (state->parse && state->parse->done) state->parse->done(state);

    return true;
}

void skip_comment(const char **i)
{
    const char *j = *i;
    ASSERT(*j == '{');
    for (;;)
    {
        j++;
        if (*j == '{') skip_comment(&j);
        if (*j == '}') break;
        if (*j == 0) return;
    }
    j++;
    *i = j;
}

void skip_encoded(const char **i)
{
    const char *j = *i;
    if (*j != '#') return;
    j++;
    while (is_ixml_hex_char(*j)) j++;
    *i = j;
}

void skip_mark(const char **i)
{
    const char *j = *i;
    char c = *j;
    if (c == '^' || c == '-' || c == '@') // mark
    {
        j++;
        while (is_ixml_whitespace_char(*j)) j++;
    }
    *i = j;
}

void skip_string(const char **i)
{
    const char *j = *i;
    if (*j == '"') // mark
    {
        j++;
        while (*j != '"')
        {
            if (*j == 0) return; // Ouch string not closed.
            if (*j == '"' && *(j+1) == '"') j++;
            j++;
        }
        // Move past the last "
        j++;
    }
    else
    if (*j == '\'') // mark
    {
        j++;
        while (*j != '\'')
        {
            if (*j == 0) return; // Ouch string not closed.
            if (*j == '\'' && *(j+1) == '\'') j++;
            j++;
        }
        j++;
    }
    *i = j;
}

void skip_tmark(const char **i)
{
    const char *j = *i;
    char c = *j;
    if (c == '^' || c == '-') // tmark
    {
        j++;
        while (is_ixml_whitespace_char(*j)) j++;
    }
    *i = j;
}

void skip_whitespace(const char **i)
{
    const char *j = *i;
    while (is_ixml_whitespace_char(*j))
    {
        if (*j == '{') skip_comment(&j);
        else j++;
    }
    *i = j;
}

void add_yaep_term_to_rule(XMQParseState *state, char mark, IXMLTerminal *t, IXMLNonTerminal *nt)
{
    IXMLTerm *term = (IXMLTerm*)calloc(1, sizeof(IXMLTerm));
    assert( (t || nt) && !(t && nt) );
    term->type = t ? IXML_TERMINAL :  IXML_NON_TERMINAL;
    term->mark = mark;
    term->t = t;
    term->nt = nt;
    vector_push_back(state->ixml_rule->rhs_terms, term);
}

void add_yaep_terminal(XMQParseState *state, IXMLTerminal *terminal)
{
    hashmap_put(state->ixml_terminals_map, terminal->name, terminal);
}

IXMLNonTerminal *lookup_yaep_nonterminal_already(XMQParseState *state, const char *name)
{
    // FIXME. Replace this with a set or map for faster lookup if needed.
    for (size_t i = 0; i < state->ixml_non_terminals->size; ++i)
    {
        IXMLNonTerminal *nt = (IXMLNonTerminal*)vector_element_at(state->ixml_non_terminals, i);
        if (!strcmp(name, nt->name)) return nt;
    }
    return NULL;
}

void add_yaep_nonterminal(XMQParseState *state, IXMLNonTerminal *nt)
{
    hashmap_put(state->ixml_non_terminals_map, nt->name, nt);
    vector_push_back(state->ixml_non_terminals, nt);
}

void add_yaep_tmp_term_terminal(XMQParseState *state, char *name, int code)
{
    IXMLTerminal *t = new_ixml_terminal();
    t->name = name;
    t->code = code;
    vector_push_back(state->ixml_tmp_terminals, t);
}

void add_single_char_rule(XMQParseState *state, IXMLNonTerminal *nt, int uc, char mark, char tmark)
{
    IXMLRule *rule = new_ixml_rule();
    rule->rule_name->name = strdup(nt->name);
    // No need to copy alias here since this rule is used for single characters for charsets.
    rule->mark = mark;
    vector_push_back(state->ixml_rules, rule);
    char buffer[16];
    snprintf(buffer, 15, "#%x", uc);
    allocate_yaep_tmp_terminals(state);
    add_yaep_tmp_term_terminal(state, strdup(buffer), uc);
    state->ixml_rule = rule; // FIXME this is a bit ugly.
    add_yaep_tmp_terminals_to_rule(state, rule, tmark);
    free_yaep_tmp_terminals(state);
}

void add_insertion_rule(XMQParseState *state, const char *content)
{
    // Generate a name like |+.......
    char *name = (char*)malloc(strlen(content)+3);
    name[0] = '|';
    name[1] = '+';
    strcpy(name+2, content);

    IXMLNonTerminal *nt = (IXMLNonTerminal*)hashmap_get(state->ixml_non_terminals_map, name);
    if (!nt)
    {
        IXMLRule *rule = new_ixml_rule();
        rule->rule_name->name = name;
        rule->mark = ' ';
        vector_push_back(state->ixml_rules, rule);

        nt = copy_ixml_nonterminal(rule->rule_name);
        add_yaep_nonterminal(state, nt);
    }
    else
    {
        // This insertion rule has already been added as a non terminal.
        free(name);
    }

    // Add nt to rule.
    add_yaep_term_to_rule(state, 0, NULL, nt);
}

void add_not_rule_string(XMQParseState *state, const char *content)
{
    // Generate a name like |!S.......
    char *name = (char*)malloc(strlen(content)+4);
    name[0] = '|';
    name[1] = '!';
    name[2] = 'S';
    strcpy(name+3, content);

    IXMLNonTerminal *nt = (IXMLNonTerminal*)hashmap_get(state->ixml_non_terminals_map, name);
    if (nt) {
        // This not rule has already been recorded. Do not add it again.
        free(name);
        return;
    }

    IXMLRule *rule = new_ixml_rule();
    rule->rule_name->name = name;
    rule->mark = ' ';
    vector_push_back(state->ixml_rules, rule);

    nt = copy_ixml_nonterminal(rule->rule_name);
    add_yaep_nonterminal(state, nt);
    add_yaep_term_to_rule(state, 0, NULL, nt);
    hashmap_put(state->ixml_non_terminals_map, name, nt);
}

void add_not_rule_charset(XMQParseState *state, IXMLNonTerminal *cs)
{
    // Generate a name like |![...]
    char *name = (char*)malloc(strlen(cs->name)+3);
    name[0] = '|';
    name[1] = '!';
    strcpy(name+2, cs->name);

    IXMLNonTerminal *nt = (IXMLNonTerminal*)hashmap_get(state->ixml_non_terminals_map, name);
    if (nt) {
        // This not rule has already been recorded. Do not add it again.
        free(name);
        return;
    }

    IXMLRule *rule = new_ixml_rule();

    rule->rule_name->name = name;
    rule->mark = ' ';
    vector_push_back(state->ixml_rules, rule);

    nt = copy_ixml_nonterminal(rule->rule_name);
    add_yaep_nonterminal(state, nt);
    add_yaep_term_to_rule(state, 0, NULL, nt);
    hashmap_put(state->ixml_non_terminals_map, name, nt);
}

void scan_content_fixup_charsets(XMQParseState *state, const char *start, const char *stop)
{
    const char *i = start;
    // We allocate a byte for every possible unicode.
    // 17*65536 = 0x11000 = 1 MB
    char *used = (char*)malloc(0x110000);
    memset(used, 0, 0x110000);

    if (state->used_unicodes == NULL)
    {
        state->used_unicodes = (char*)malloc(0x110000);
        memset(state->used_unicodes, 0, 0x110000);
    }

    char *already_used = state->used_unicodes;

    while (i < stop)
    {
        int uc = 0;
        size_t len = 0;
        bool ok = decode_utf8(i, stop, &uc, &len);
        if (!ok)
        {
            fprintf(stderr, "xmq: broken utf8\n");
            exit(1);
        }
        i += len;

        if (!already_used[uc])
        {
            already_used[uc] = 1;
            used[uc] = 1;
            char buf[16];
            snprintf(buf,16, "#%x", uc);
            IXMLTerminal *t = (IXMLTerminal*)hashmap_get(state->ixml_terminals_map, buf);
            if (t == NULL)
            {
                // Add terminal not in the grammar, but found inside the input content to be parsed.
                // This gives us better error messages from yaep. Instead of "No such token" we
                // get syntax error at line:col.
                t = new_ixml_terminal();
                t->name = strdup(buf);
                t->code = uc;
                add_yaep_terminal(state, t);
            }
        }
    }

    for (size_t i = 0; i < state->ixml_non_terminals->size; ++i)
    {
        IXMLNonTerminal *nt = (IXMLNonTerminal*)vector_element_at(state->ixml_non_terminals, i);
        if (nt->charset)
        {
            bool include = nt->name[0] != '~';
            char tmark = 0;
            if ((nt->name[0] == '[' && nt->name[1] == '-') ||
                (nt->name[1] == '[' && nt->name[2] == '-'))
            {
                tmark = '-';
            }

            if (include)
            {
                // Including...
                for (IXMLCharsetPart *p = nt->charset->first; p; p = p->next)
                {
                    if (!p->category[0])
                    {
                        // Not a category, test a range.
                        // All characters within the range that have been used
                        // should be tested for.
                        for (int c = p->from; c <= p->to; ++c)
                        {
                            if (c >= 0 && c <= 0x10ffff && used[c])
                            {
                                add_single_char_rule(state, nt, c, '-', tmark);
                            }
                        }
                    }
                    else
                    {
                        const char **cat_part = unicode_lookup_category_parts(p->category);
                        for (; *cat_part; ++cat_part)
                        {
                            int *cat = NULL;
                            size_t cat_len = 0;
                            bool ok = unicode_get_category_part(*cat_part, &cat, &cat_len);
                            if (!ok)
                            {
                                fprintf(stderr, "Invalid unicode category: %s\n", *cat_part);
                                exit(1);
                            }
                            // All characters in the category that have been used
                            // should be tested for.
                            for (size_t i = 0; i < cat_len; ++i)
                            {
                                int c = cat[i];
                                if (c >= 0 && c <= 0x10ffff && used[c])
                                {
                                    add_single_char_rule(state, nt, c, '-', tmark);
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                // Excluding....
                // Scan all actually used characters in the input content.
                for (int c = 0; c <= 0x10ffff; ++c)
                {
                    if (!used[c]) continue;
                    bool should_add = true;
                    for (IXMLCharsetPart *p = nt->charset->first; p; p = p->next)
                    {
                        if (p->category[0])
                        {
                            const char **cat_part = unicode_lookup_category_parts(p->category);
                            for (; *cat_part; ++cat_part)
                            {
                                int *cat = NULL;
                                size_t cat_len = 0;
                                bool ok = unicode_get_category_part(*cat_part, &cat, &cat_len);
                                if (!ok)
                                {
                                    fprintf(stderr, "Invalid unicode category: %s\n", *cat_part);
                                    exit(1);
                                }
                                // Is the used character in the category set?
                                if (category_has_code(c, cat, cat_len))
                                {
                                    // Yes it is.
                                    // Do not add the used character! We do not want to match against
                                    // it since it is excluded.
                                    should_add = false;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            // Is the used character in the range?
                            if (c >= p->from && c <= p->to)
                            {
                                // Yes it is.
                                // Do not add the used character! We do not want to match against
                                // it since it is excluded.
                                should_add= false;
                                break;
                            }
                        }
                    }
                    if (should_add)
                    {
                        add_single_char_rule(state, nt, c, '-', tmark);
                    }
                }
            }
        }
    }

    free(used);
}

void add_yaep_tmp_terminals_to_rule(XMQParseState *state, IXMLRule *rule, char mark)
{
    for (size_t i = 0; i < state->ixml_tmp_terminals->size; ++i)
    {
        IXMLTerminal *te = (IXMLTerminal*)state->ixml_tmp_terminals->elements[i];
        IXMLTerminal *t = (IXMLTerminal*)hashmap_get(state->ixml_terminals_map, te->name);
        if (t == NULL)
        {
            // This terminal was not already in the map. Add it.
            add_yaep_terminal(state, te);
            t = te;
        }
        else
        {
            // This terminal was already in the map. Free the terminal and add a
            // pointer the already stored termina to the rule.
            free_ixml_terminal(te);
        }
        add_yaep_term_to_rule(state, mark, t, NULL);
        state->ixml_tmp_terminals->elements[i] = NULL;
    }
}

void allocate_yaep_tmp_terminals(XMQParseState *state)
{
    assert(state->ixml_tmp_terminals == NULL);
    state->ixml_tmp_terminals = vector_create();
}

void free_yaep_tmp_terminals(XMQParseState *state)
{
    // Only free the vector, the content is copied to another array.
    vector_free(state->ixml_tmp_terminals);
    state->ixml_tmp_terminals = NULL;
}

void free_yaep_tmp_terminals_and_content(XMQParseState *state)
{
    // Free the tmp terminals themselves.
    vector_free_and_values(state->ixml_tmp_terminals, (FreeFuncPtr)free_ixml_terminal);
    state->ixml_tmp_terminals = NULL;
}

bool has_ixml_tmp_terminals(XMQParseState *state)
{
    assert(state->ixml_tmp_terminals);

    return state->ixml_tmp_terminals->size > 0;
}

bool has_more_than_one_ixml_tmp_terminals(XMQParseState *state)
{
    assert(state->ixml_tmp_terminals);

    return state->ixml_tmp_terminals->size > 1;
}

IXMLRule *new_ixml_rule()
{
    IXMLRule *r = (IXMLRule*)calloc(1, sizeof(IXMLRule));
    r->rule_name = new_ixml_nonterminal();
    r->rhs_terms = vector_create();
    return r;
}

void free_ixml_rule(IXMLRule *r)
{
    if (r->rule_name) free_ixml_nonterminal(r->rule_name);
    r->rule_name = NULL;
    vector_free_and_values(r->rhs_terms, (FreeFuncPtr)free_ixml_term);
    r->rhs_terms = NULL;
    free(r);
}

IXMLTerminal *new_ixml_terminal()
{
    IXMLTerminal *t = (IXMLTerminal*)calloc(1, sizeof(IXMLTerminal));
    return t;
}

void free_ixml_terminal(IXMLTerminal *t)
{
    if (t->name) free(t->name);
    t->name = NULL;
    free(t);
}

IXMLNonTerminal *new_ixml_nonterminal()
{
    IXMLNonTerminal *nt = (IXMLNonTerminal*)calloc(1, sizeof(IXMLNonTerminal));
    return nt;
}

IXMLNonTerminal *copy_ixml_nonterminal(IXMLNonTerminal *nt)
{
    IXMLNonTerminal *t = (IXMLNonTerminal*)calloc(1, sizeof(IXMLNonTerminal));
    t->name = strdup(nt->name);
    return t;
}

void free_ixml_nonterminal(IXMLNonTerminal *nt)
{
    if (nt->name) free(nt->name);
    nt->name = NULL;
    if (nt->alias) free(nt->alias);
    nt->alias = NULL;
    if (nt->charset) free_ixml_charset(nt->charset);
    nt->charset = NULL;
    free(nt);
}

void free_ixml_term(IXMLTerm *t)
{
    t->t = NULL;
    t->nt = NULL;
    free(t);
}

IXMLCharset *new_ixml_charset(bool exclude)
{
    IXMLCharset *cs = (IXMLCharset*)calloc(1, sizeof(IXMLCharset));
    cs->exclude = exclude;
    return cs;
}

void new_ixml_charset_part(IXMLCharset *cs, int from, int to, const char *category)
{
    IXMLCharsetPart *csp = (IXMLCharsetPart*)calloc(1, sizeof(IXMLCharsetPart));
    csp->from = from;
    csp->to = to;
    strncpy(csp->category, category, 2);
    if (cs->last == NULL)
    {
        cs->first = cs->last = csp;
    }
    else
    {
        cs->last->next = csp;
        cs->last = csp;
    }
}

void free_ixml_charset(IXMLCharset *cs)
{
    IXMLCharsetPart *i = cs->first;
    while (i)
    {
        IXMLCharsetPart *next = i->next;
        i->next = NULL;
        free(i);
        i = next;
    }
    cs->first = NULL;
    cs->last = NULL;
    free(cs);
}

char *generate_rule_name(XMQParseState *state)
{
    char buf[16];
    snprintf(buf, 15, "|%d", state->num_generated_rules);
    state->num_generated_rules++;
    return strdup(buf);
}

char *generate_charset_rule_name(IXMLCharset *cs, char mark)
{
    char *buf = (char*)malloc(1024);
    buf[0] = 0;

    if (cs->exclude) strcat(buf, "~");
    strcat(buf, "[");
    char m[2];
    m[0] = mark;
    m[1] = 0;
    if (mark != 0 && mark != ' ') strcat(buf, m);

    IXMLCharsetPart *i = cs->first;
    bool first = true;

    while (i)
    {
        if (!first) { strcat(buf, ";"); } else { first = false; }
        if (i->category[0]) strcat(buf, i->category);
        else
        {
            if (i->from != i->to)
            {
                char s[16];
                snprintf(s, 16, "#%x-#%x", i->from, i->to);
                strcat(buf, s);
            }
            else
            {
                char s[16];
                snprintf(s, 16, "#%x", i->from);
                strcat(buf, s);
            }
        }
        i = i->next;
    }

    strcat(buf, "]");
    return buf;
}

void make_last_term_optional(XMQParseState *state)
{
    // Compose an anonymous rule.
    // 'a'? is replaced with the nonterminal /17 (for example)
    // Then /17 can be either 'a' or the empty string.
    // /17 : 'a'.
    // /17 : .

    // Pop the last term.
    IXMLTerm *term = (IXMLTerm*)vector_pop_back(state->ixml_rule->rhs_terms);

    IXMLNonTerminal *nt = new_ixml_nonterminal();
    nt->name = generate_rule_name(state);

    add_yaep_nonterminal(state, nt);
    add_yaep_term_to_rule(state, '-', NULL, nt);

    stack_push(state->ixml_rule_stack, state->ixml_rule);

    state->ixml_rule = new_ixml_rule();
    state->ixml_rule->mark = '-';
    vector_push_back(state->ixml_rules, state->ixml_rule);

    free_ixml_nonterminal(state->ixml_rule->rule_name);
    state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

    // Single term in this rule alternative.
    add_yaep_term_to_rule(state, state->ixml_mark, term->t, term->nt);
    free(term);
    term = NULL;

    state->ixml_rule = new_ixml_rule();
    state->ixml_rule->mark = '-';

    vector_push_back(state->ixml_rules, state->ixml_rule);

    free_ixml_nonterminal(state->ixml_rule->rule_name);
    state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

    // No terms in this rule alternative!

    state->ixml_rule = (IXMLRule*)stack_pop(state->ixml_rule_stack);
}

void make_last_term_repeat(XMQParseState *state, char factor_mark)
{
    // Compose an anonymous rule.
    // 'a'+ is replaced with the nonterminal /17 (for example)
    // Then /17 can be either 'a' or the /17, 'a'
    // /17 = "a".
    // /17 = /17, "a".

    // Pop the last term.
    IXMLTerm *term = (IXMLTerm*)vector_pop_back(state->ixml_rule->rhs_terms);

    IXMLNonTerminal *nt = new_ixml_nonterminal();
    nt->name = generate_rule_name(state);

    add_yaep_nonterminal(state, nt);
    add_yaep_term_to_rule(state, '-', NULL, nt);

    stack_push(state->ixml_rule_stack, state->ixml_rule);

    // Build first alternative rule: /17 = 'a'.
    state->ixml_rule = new_ixml_rule();
    state->ixml_rule->mark = '-';
    vector_push_back(state->ixml_rules, state->ixml_rule);

    free_ixml_nonterminal(state->ixml_rule->rule_name);
    state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

    // 'a'
    add_yaep_term_to_rule(state, factor_mark, term->t, term->nt);

    // Build second alternative rule: /17 = /17, 'a'.
    state->ixml_rule = new_ixml_rule();
    state->ixml_rule->mark = '-';
    vector_push_back(state->ixml_rules, state->ixml_rule);

    free_ixml_nonterminal(state->ixml_rule->rule_name);
    state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

    // /17, 'a'
    add_yaep_term_to_rule(state, '-', NULL, nt);
    add_yaep_term_to_rule(state, factor_mark, term->t, term->nt);

    free(term);
    term = NULL;

    state->ixml_rule = (IXMLRule*)stack_pop(state->ixml_rule_stack);
}

void make_last_term_repeat_infix(XMQParseState *state, char factor_mark, char infix_mark)
{
    // Compose an anonymous rule.
    // 'a'++'b' is replaced with the nonterminal /17 (for example)
    // Then /17 can be either 'a' or the /17, 'a'
    // /17 = "a".
    // /17 = /17, "b", "a".

    // Pop the last two terms.
    IXMLTerm *infix = (IXMLTerm*)vector_pop_back(state->ixml_rule->rhs_terms);
    IXMLTerm *term = (IXMLTerm*)vector_pop_back(state->ixml_rule->rhs_terms);

    IXMLNonTerminal *nt = new_ixml_nonterminal();
    nt->name = generate_rule_name(state);

    add_yaep_nonterminal(state, nt);
    add_yaep_term_to_rule(state, '-', NULL, nt);

    stack_push(state->ixml_rule_stack, state->ixml_rule);

    // Build first alternative rule: /17 = 'a'.
    state->ixml_rule = new_ixml_rule();
    state->ixml_rule->mark = '-';
    vector_push_back(state->ixml_rules, state->ixml_rule);

    free_ixml_nonterminal(state->ixml_rule->rule_name);
    state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

    // 'a'
    add_yaep_term_to_rule(state, factor_mark, term->t, term->nt);

    // Build second alternative rule: /17 = /17, 'b', 'a'.
    state->ixml_rule = new_ixml_rule();
    state->ixml_rule->mark = '-';
    vector_push_back(state->ixml_rules, state->ixml_rule);

    free_ixml_nonterminal(state->ixml_rule->rule_name);
    state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

    // /17, 'b', 'a'
    add_yaep_term_to_rule(state, '-', NULL, nt);
    add_yaep_term_to_rule(state, infix_mark, infix->t, infix->nt);
    add_yaep_term_to_rule(state, factor_mark, term->t, term->nt);

    free(term);
    free(infix);
    term = NULL;
    infix = NULL;

    state->ixml_rule = (IXMLRule*)stack_pop(state->ixml_rule_stack);
}

void make_last_term_repeat_zero(XMQParseState *state, char factor_mark)
{
    // Compose an anonymous rule.
    // 'a'+ is replaced with the nonterminal /17 (for example)
    // Then /17 can be either 'a' or the /17, 'a'
    // /17 = .
    // /17 = /17 | "a".

    // Pop the last term.
    IXMLTerm *term = (IXMLTerm*)vector_pop_back(state->ixml_rule->rhs_terms);

    IXMLNonTerminal *nt = new_ixml_nonterminal();
    nt->name = generate_rule_name(state);

    add_yaep_nonterminal(state, nt);
    add_yaep_term_to_rule(state, '-', NULL, nt);

    stack_push(state->ixml_rule_stack, state->ixml_rule);

    // Build first alternative rule: /17 = .
    state->ixml_rule = new_ixml_rule();
    state->ixml_rule->mark = '-';
    vector_push_back(state->ixml_rules, state->ixml_rule);

    free_ixml_nonterminal(state->ixml_rule->rule_name);
    state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

    // Build second alternative rule: /17 = /17, 'a'.
    state->ixml_rule = new_ixml_rule();
    state->ixml_rule->mark = '-';
    vector_push_back(state->ixml_rules, state->ixml_rule);

    free_ixml_nonterminal(state->ixml_rule->rule_name);
    state->ixml_rule->rule_name = copy_ixml_nonterminal(nt);

    // /17, 'a'
    add_yaep_term_to_rule(state, '-', NULL, nt);
    add_yaep_term_to_rule(state, factor_mark, term->t, term->nt);

    free(term);
    term = NULL;

    state->ixml_rule = (IXMLRule*)stack_pop(state->ixml_rule_stack);
}

void make_last_term_repeat_zero_infix(XMQParseState *state, char factor_mark, char infix_mark)
{
    make_last_term_repeat_infix(state, factor_mark, infix_mark);
    state->ixml_mark = '-';
    make_last_term_optional(state);
}

void ixml_print_grammar(XMQParseState *state)
{
    if (false && xmq_trace_enabled_)
    {
        HashMapIterator *i = hashmap_iterate(state->ixml_terminals_map);

        const char *name;
        void *val;
        while (hashmap_next_key_value(i, &name, &val))
        {
            IXMLTerminal *t = (IXMLTerminal*)val;
            printf("TERMINAL %s %d\n", t->name, t->code);
        }

        hashmap_free_iterator(i);

        for (size_t i = 0; i < state->ixml_rules->size; ++i)
        {
            IXMLRule *r = (IXMLRule*)vector_element_at(state->ixml_rules, i);
            printf("RULE %c%s\n", r->mark?r->mark:' ', r->rule_name->name);

            for (size_t j = 0; j < r->rhs_terms->size; ++j)
            {
                IXMLTerm *term = (IXMLTerm*)vector_element_at(r->rhs_terms, j);
                if (term->type == IXML_TERMINAL)
                {
                    printf("   T %c%s %d\n", term->mark?term->mark:' ', term->t->name, term->t->code);
                }
                else
                {
                    printf("   R %c%s \n", term->mark?term->mark:' ', term->nt->name);
                }
            }
        }
    }
}

#else


#endif // IXML_MODULE

// PART C JSON_C ////////////////////////////////////////

#ifdef JSON_MODULE

void eat_json_boolean(XMQParseState *state);
void eat_json_null(XMQParseState *state);
void eat_json_number(XMQParseState *state);
void eat_json_quote(XMQParseState *state, char **content_start, char **content_stop);

void fixup_json(XMQDoc *doq, xmlNode *node);

void parse_json(XMQParseState *state, const char *key_start, const char *key_stop);
void parse_json_array(XMQParseState *state, const char *key_start, const char *key_stop);
void parse_json_boolean(XMQParseState *state, const char *key_start, const char *key_stop);
void parse_json_null(XMQParseState *state, const char *key_start, const char *key_stop);
void parse_json_number(XMQParseState *state, const char *key_start, const char *key_stop);
void parse_json_object(XMQParseState *state, const char *key_start, const char *key_stop);
void parse_json_quote(XMQParseState *state, const char *key_start, const char *key_stop);

bool has_number_ended(char c);
bool has_attr_other_than_AS_(xmlNode *node);
const char *is_jnumber(const char *start, const char *stop);
bool is_json_boolean(XMQParseState *state);
bool is_json_null(XMQParseState *state);
bool is_json_number(XMQParseState *state);
bool is_json_quote_start(char c);
bool is_json_whitespace(char c);
void json_print_namespace_declaration(XMQPrintState *ps, xmlNs *ns);
void json_print_attribute(XMQPrintState *ps, xmlAttrPtr a);
void json_print_attributes(XMQPrintState *ps, xmlNodePtr node);
void json_print_array_with_children(XMQPrintState *ps,
                                    xmlNode *container,
                                    xmlNode *node);
void json_print_comment_node(XMQPrintState *ps, xmlNodePtr node, bool prefix_ul, size_t total, size_t used);
void json_print_doctype_node(XMQPrintState *ps, xmlNodePtr node);
void json_print_entity_node(XMQPrintState *ps, xmlNodePtr node);
void json_print_standalone_quote(XMQPrintState *ps, xmlNode *container, xmlNodePtr node, size_t total, size_t used);
void json_print_node(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_t total, size_t used);
void json_print_value(XMQPrintState *ps, xmlNode *from, xmlNode *to, Level level, bool force_string);
void json_print_element_name(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_t total, size_t used);
void json_print_element_with_children(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_t total, size_t used);
void json_print_key_node(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_t total, size_t used, bool force_string);

void json_check_comma(XMQPrintState *ps);
void json_print_comma(XMQPrintState *ps);
bool json_is_number(const char *start);
bool json_is_keyword(const char *start);
void json_print_leaf_node(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_t total, size_t used);

void trim_index_suffix(const char *key_start, const char **stop);

char equals[] = "=";
char underline[] = "_";
char leftpar[] = "(";
char rightpar[] = ")";
char leftbrace[] = "{";
char rightbrace[] = "}";
char array[] = "A";
char string[] = "S";

bool is_json_whitespace(char c)
{
    return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}

bool is_json_quote_start(char c)
{
    return c == '"';
}

void eat_json_quote(XMQParseState *state, char **content_start, char **content_stop)
{
    const char *start = state->i;
    const char *stop = state->buffer_stop;

    MemBuffer *buf = new_membuffer();

    const char *i = start;
    size_t line = state->line;
    size_t col = state->col;

    increment('"', 1, &i, &line, &col);

    while (i < stop)
    {
        char c = *i;
        if (c == '"')
        {
            increment(c, 1, &i, &line, &col);
            break;
        }
        if (c == '\\')
        {
            increment(c, 1, &i, &line, &col);
            c = *i;
            if (c == '"' || c == '\\' || c == 'b' || c == 'f' || c == 'n' || c == 'r' || c == 't' || c == '/')
            {
                increment(c, 1, &i, &line, &col);
                switch(c)
                {
                case 'b': c = 8; break;
                case 'f': c = 12; break;
                case 'n': c = 10; break;
                case 'r': c = 13; break;
                case 't': c = 9; break;
                case '/': c = '/'; break; // Silly, but actually used sometimes in json....
                }
                membuffer_append_char(buf, c);
                continue;
            }
            else if (c == 'u')
            {
                increment(c, 1, &i, &line, &col);
                c = *i;
                if (i+3 < stop)
                {
                    // Woot? Json can only escape unicode up to 0xffff ? What about 10000 up to 10ffff?
                    if (is_hex(*(i+0)) && is_hex(*(i+1)) && is_hex(*(i+2)) && is_hex(*(i+3)))
                    {
                        unsigned char c1 = hex_value(*(i+0));
                        unsigned char c2 = hex_value(*(i+1));
                        unsigned char c3 = hex_value(*(i+2));
                        unsigned char c4 = hex_value(*(i+3));
                        increment(c, 1, &i, &line, &col);
                        increment(c, 1, &i, &line, &col);
                        increment(c, 1, &i, &line, &col);
                        increment(c, 1, &i, &line, &col);

                        int uc = (c1<<12)|(c2<<8)|(c3<<4)|c4;
                        UTF8Char utf8;
                        size_t n = encode_utf8(uc, &utf8);

                        for (size_t j = 0; j < n; ++j)
                        {
                            membuffer_append_char(buf, utf8.bytes[j]);
                        }
                        continue;
                    }
                }
            }
            state->error_nr = XMQ_ERROR_JSON_INVALID_ESCAPE;
            longjmp(state->error_handler, 1);
        }
        membuffer_append_char(buf, c);
        increment(c, 1, &i, &line, &col);
    }
    // Add a zero termination to the string which is not used except for
    // guaranteeing that there is at least one allocated byte for empty strings.
    membuffer_append_null(buf);
    state->i = i;
    state->line = line;
    state->col = col;

    // Calculate the real length which might be less than the original
    // since escapes have disappeared. Add 1 to have at least something to allocate.
    size_t len = membuffer_used(buf);
    char *quote = free_membuffer_but_return_trimmed_content(buf);
    *content_start = quote;
    *content_stop = quote+len-1; // Drop the zero byte.
}

void trim_index_suffix(const char *key_start, const char **stop)
{
    const char *key_stop = *stop;

    if (key_start && key_stop && key_start < key_stop && *(key_stop-1) == ']')
    {
        // This is an indexed element name "path[32]":"123" ie the 32:nd xml element
        // which has been indexed because json objects must have unique keys.
        // Well strictly speaking the json permits multiple keys, but no implementation does....
        //
        // Lets drop the suffix [32].
        const char *i = key_stop-2;
        // Skip the digits stop at [ or key_start
        while (i > key_start && *i >= '0' && *i <= '9' && *i != '[') i--;
        if (i > key_start && *i == '[')
        {
            // We found a [ which is not at key_start. Trim off suffix!
            *stop = i;
        }
    }
}

void set_node_namespace(XMQParseState *state, xmlNodePtr node, const char *node_name)
{
    if (state->element_namespace)
    {
        // Have a namespace before the element name, eg abc:work
        xmlNsPtr ns = xmlSearchNs(state->doq->docptr_.xml,
                                  node,
                                  (const xmlChar *)state->element_namespace);
        if (!ns)
        {
            // The namespaces does not yet exist. Lets hope it will be declared
            // inside the attributes of this node. Use a temporary href for now.
            ns = xmlNewNs(node,
                          NULL,
                          (const xmlChar *)state->element_namespace);
            debug("[XMQ] created namespace prefix=%s in element %s\n", state->element_namespace, node_name);
        }
        debug("[XMQ] setting namespace prefix=%s for element %s\n", state->element_namespace, node_name);
        xmlSetNs(node, ns);
        free(state->element_namespace);
        state->element_namespace = NULL;
    }
    else if (state->default_namespace)
    {
        // We have a default namespace.
        xmlNsPtr ns = (xmlNsPtr)state->default_namespace;
        assert(ns->prefix == NULL);
        debug("[XMQ] set default namespace with href=%s for element %s\n", ns->href, node_name);
        xmlSetNs(node, ns);
    }
}

void parse_json_quote(XMQParseState *state, const char *key_start, const char *key_stop)
{
    int start_line = state->line;
    int start_col = state->col;

    char *content_start = NULL;
    char *content_stop = NULL;

    // Decode and content_start points to newly allocated buffer where escapes have been removed.
    eat_json_quote(state, &content_start, &content_stop);
    size_t content_len = content_stop-content_start;

    trim_index_suffix(key_start, &key_stop);

    if (key_start && *key_start == '|' && key_stop == key_start+1)
    {
        // This is "|":"symbol" which means a pure text node in xml.
        DO_CALLBACK_SIM(quote, state, start_line, 1, content_start, content_stop, content_stop);
        free(content_start);
        return;
    }

    if (key_start && key_stop == key_start+2 && *key_start == '/' && *(key_start+1) == '/')
    {
        // This is "//":"symbol" which means a comment node in xml.
        DO_CALLBACK_SIM(comment, state, start_line, start_col, content_start, content_stop, content_stop);
        free(content_start);
        return;
    }

    if (key_start && key_stop == key_start+3 && *key_start == '_' && *(key_start+1) == '/' && *(key_start+2) == '/')
    {
        // This is "_//":"symbol" which means a comment node in xml prefixing the root xml node.
        if (!state->root_found) state->add_pre_node_before = (xmlNode*)state->element_stack->top->data;
        else                    state->add_post_node_after = (xmlNode*)state->element_stack->top->data;
        DO_CALLBACK_SIM(comment, state, start_line, start_col, content_start, content_stop, content_stop);
        if (!state->root_found) state->add_pre_node_before = NULL;
        else                    state->add_post_node_after = NULL;
        free(content_start);
        return;
    }

    if (key_start && key_stop == key_start+1 && *key_start == '_' )
    {
        // This is the element name "_":"symbol" stored inside the json object,
        // in situations where the name is not visible as a key. For example
        // the root json object and any object in arrays.
        xmlNodePtr container = (xmlNodePtr)state->element_stack->top->data;
        size_t len = content_stop - content_start;
        char *name = (char*)malloc(len+1);
        memcpy(name, content_start, len);
        name[len] = 0;
        const char *colon = NULL;
        bool do_return = true;
        if (is_xmq_element_name(name, name+len, &colon))
        {
            if (!colon)
            {
                xmlNodeSetName(container, (xmlChar*)name);
            }
            else
            {
                DO_CALLBACK_SIM(element_ns, state, state->line, state->col, name, colon, colon);
                xmlNodeSetName(container, (xmlChar*)colon+1);
                set_node_namespace(state, container, colon+1);
            }
        }
        else
        {
            // Oups! we cannot use this as an element name! What should we do?
            //xmlNodeSetName(container, "Baaad")
            PRINT_WARNING("xmq: Warning! \"_\":\"%s\" cannot be converted into an valid element name!\n", name);
            do_return = false;
        }
        free(name);
        if (do_return)
        {
            free(content_start);
            // This will be set many times.
            state->root_found = true;
            return;
        }
    }

    if (key_start && *key_start == '!' && !state->doctype_found)
    {
        size_t len = key_stop-key_start;
        if (len == 8 && !strncmp("!DOCTYPE", key_start, 8))
        {
            // This is the one and only !DOCTYPE element.
            DO_CALLBACK_SIM(element_key, state, state->line, state->col, key_start, key_stop, key_stop);
            state->parsing_doctype = true;
            state->add_pre_node_before = (xmlNode*)state->element_stack->top->data;
            DO_CALLBACK_SIM(element_value_quote, state, state->line, state->col, content_start, content_stop, content_stop);
            state->add_pre_node_before = NULL;
            free(content_start);
            return;
        }
    }

    const char *unsafe_key_start = NULL;
    const char *unsafe_key_stop = NULL;
    const char *colon = NULL;

    if (!key_start || key_start == key_stop)
    {
        // No key and the empty key translates into a _
        key_start = underline;
        key_stop = underline+1;
    }
    else if (!is_xmq_element_name(key_start, key_stop, &colon))
    {
        unsafe_key_start = key_start;
        unsafe_key_stop = key_stop;
        key_start = underline;
        key_stop = underline+1;
    }

    if (*key_start == '_' && key_stop > key_start+1)
    {
        // Check if this is an xmlns ns declaration.
        if (key_start+6 <= key_stop && !strncmp(key_start, "_xmlns", 6))
        {
            // Declaring the use of a namespace.
            if (colon)
            {
                // We have for example: "_xmlns:xls":"http://a.b.c."
                DO_CALLBACK_SIM(ns_declaration, state, state->line, state->col, key_start+1, colon?colon:key_stop, key_stop);
                assert (state->declaring_xmlns == true);
                DO_CALLBACK_SIM(attr_value_quote, state, start_line, start_col, content_start, content_stop, content_stop)
            }
            else
            {
                // The default namespace. "_xmlns":"http://a.b.c"
                DO_CALLBACK_SIM(ns_declaration, state, state->line, state->col, key_start+1, key_stop, key_stop);
                DO_CALLBACK_SIM(attr_value_quote, state, start_line, start_col, content_start, content_stop, content_stop)
            }
        }
        else
        {
            // This is a normal attribute that was stored as "_attr":"value"
            DO_CALLBACK_SIM(attr_key, state, state->line, state->col, key_start+1, key_stop, key_stop);
            DO_CALLBACK_SIM(attr_value_quote, state, start_line, start_col, content_start, content_stop, content_stop);
        }
        free(content_start);
        return;
    }

    if (!unsafe_key_start && colon)
    {
        DO_CALLBACK_SIM(element_ns, state, state->line, state->col, key_start, colon, colon);
        key_start = colon+1;
    }
    DO_CALLBACK_SIM(element_key, state, state->line, state->col, key_start, key_stop, key_stop);

    bool need_string_type =
        content_len > 0 && (
            (content_len == 4 && !strncmp(content_start, "true", 4)) ||
            (content_len == 5 && !strncmp(content_start, "false", 5)) ||
            (content_len == 4 && !strncmp(content_start, "null", 4)) ||
            content_stop == is_jnumber(content_start, content_stop));

    if (need_string_type || unsafe_key_start)
    {
        // Ah, this is the string "false" not the boolean false. Mark this with the attribute S to show that it is a string.
        DO_CALLBACK_SIM(apar_left, state, state->line, state->col, leftpar, leftpar+1, leftpar+1);
        if (unsafe_key_start)
        {
            DO_CALLBACK_SIM(attr_key, state, state->line, state->col, underline, underline+1, underline+1);
            DO_CALLBACK_SIM(attr_value_quote, state, state->line, state->col, unsafe_key_start, unsafe_key_stop, unsafe_key_stop);
        }
        if (need_string_type)
        {
            DO_CALLBACK_SIM(attr_key, state, state->line, state->col, string, string+1, string+1);
        }
        DO_CALLBACK_SIM(apar_right, state, state->line, state->col, rightpar, rightpar+1, rightpar+1);
    }

    DO_CALLBACK_SIM(element_value_text, state, start_line, start_col, content_start, content_stop, content_stop);
    free(content_start);
}

bool is_json_null(XMQParseState *state)
{
    const char *i = state->i;
    const char *stop = state->buffer_stop;

    if (i+4 <= stop && !strncmp("null", i, 4)) return true;
    return false;
}

void eat_json_null(XMQParseState *state)
{
    const char *i = state->i;
    size_t line = state->line;
    size_t col = state->col;

    increment('n', 1, &i, &line, &col);
    increment('u', 1, &i, &line, &col);
    increment('l', 1, &i, &line, &col);
    increment('l', 1, &i, &line, &col);

    state->i = i;
    state->line = line;
    state->col = col;
}

void parse_json_null(XMQParseState *state, const char *key_start, const char *key_stop)
{
    const char *start = state->i;
    int start_line = state->line;
    int start_col = state->col;

    eat_json_null(state);
    const char *stop = state->i;

    const char *unsafe_key_start = NULL;
    const char *unsafe_key_stop = NULL;
    const char *colon = NULL;

    trim_index_suffix(key_start, &key_stop);

    if (key_start && *key_start == '_' && key_stop > key_start+1)
    {
        // script:{"_async":null "_href":"abc"}
        // translates into scripts(async href=abc)
        // detect attribute before this null. Make into attribute without value.
        DO_CALLBACK_SIM(attr_key, state, state->line, state->col, key_start+1, key_stop, key_stop);
        return;
    }

    if (!key_start || key_start == key_stop)
    {
        // No key and the empty key translates into a _
        key_start = underline;
        key_stop = underline+1;
    }
    else if (!is_xmq_element_name(key_start, key_stop, &colon))
    {
        unsafe_key_start = key_start;
        unsafe_key_stop = key_stop;
        key_start = underline;
        key_stop = underline+1;
    }

    if (!unsafe_key_start && colon)
    {
        DO_CALLBACK_SIM(element_ns, state, state->line, state->col, key_start, colon, colon);
        key_start = colon+1;
    }
    DO_CALLBACK_SIM(element_key, state, state->line, state->col, key_start, key_stop, key_stop);
    if (unsafe_key_start)
    {
        DO_CALLBACK_SIM(apar_left, state, state->line, state->col, leftpar, leftpar+1, leftpar+1);
        if (unsafe_key_start)
        {
            DO_CALLBACK_SIM(attr_key, state, state->line, state->col, underline, underline+1, underline+1);
            DO_CALLBACK_SIM(attr_value_quote, state, state->line, state->col, unsafe_key_start, unsafe_key_stop, unsafe_key_stop);
        }
        DO_CALLBACK_SIM(apar_right, state, state->line, state->col, rightpar, rightpar+1, rightpar+1);
    }

    DO_CALLBACK(element_value_text, state, start_line, start_col, start, stop, stop);
}

bool has_number_ended(char c)
{
    return c == ' ' || c == '\n' || c == ',' || c == '}' || c == ']';
}

const char *is_jnumber(const char *start, const char *stop)
{
    if (stop == NULL) stop = start+strlen(start);
    if (start == stop) return NULL;

    bool found_e = false;
    bool found_e_sign = false;
    bool leading_zero = false;
    bool last_is_digit = false;
    bool found_dot = false;

    const char *i;
    for (i = start; i < stop; ++i)
    {
        char c = *i;

        last_is_digit = false;
        bool current_is_not_digit = (c < '0' || c > '9');

        if (i == start)
        {
            if (current_is_not_digit && c != '-' ) return NULL;
            if (c == '0') leading_zero = true;
            if (c != '-') last_is_digit = true;
            continue;
        }

        if (leading_zero)
        {
            leading_zero = false;
            if (has_number_ended(c)) return i;
            if (c != '.') return NULL;
            found_dot = true;
        }
        else if (c == '.')
        {
            if (found_dot) return NULL;
            found_dot = true;
        }
        else if (c == 'e' || c == 'E')
        {
            if (found_e) return NULL;
            found_e = true;
        }
        else if (found_e && !found_e_sign)
        {
            if (has_number_ended(c)) return i;
            if (current_is_not_digit && c != '-' && c != '+') return NULL;
            if (c == '+' || c == '-')
            {
                found_e_sign = true;
            }
            else
            {
                last_is_digit = true;
            }
        }
        else
        {
            found_e_sign = false;
            if (has_number_ended(c)) return i;
            if (current_is_not_digit) return NULL;
            last_is_digit = true;
        }
    }

    if (last_is_digit == false) return NULL;

    return i;
}

bool is_json_boolean(XMQParseState *state)
{
    const char *i = state->i;
    const char *stop = state->buffer_stop;

    if (i+4 <= stop && !strncmp("true", i, 4)) return true;
    if (i+5 <= stop && !strncmp("false", i, 5)) return true;
    return false;
}

void eat_json_boolean(XMQParseState *state)
{
    const char *i = state->i;
    //const char *stop = state->buffer_stop;
    size_t line = state->line;
    size_t col = state->col;

    if (*i == 't')
    {
        increment('t', 1, &i, &line, &col);
        increment('r', 1, &i, &line, &col);
        increment('u', 1, &i, &line, &col);
        increment('e', 1, &i, &line, &col);
    }
    else
    {
        increment('f', 1, &i, &line, &col);
        increment('a', 1, &i, &line, &col);
        increment('l', 1, &i, &line, &col);
        increment('s', 1, &i, &line, &col);
        increment('e', 1, &i, &line, &col);
    }

    state->i = i;
    state->line = line;
    state->col = col;
}

void parse_json_boolean(XMQParseState *state, const char *key_start, const char *key_stop)
{
    const char *start = state->i;
    int start_line = state->line;
    int start_col = state->col;

    eat_json_boolean(state);
    const char *stop = state->i;

    const char *unsafe_key_start = NULL;
    const char *unsafe_key_stop = NULL;
    const char *colon = NULL;

    trim_index_suffix(key_start, &key_stop);

    if (!key_start || key_start == key_stop)
    {
        // No key and the empty key translates into a _
        key_start = underline;
        key_stop = underline+1;
    }
    else if (!is_xmq_element_name(key_start, key_stop, &colon))
    {
        unsafe_key_start = key_start;
        unsafe_key_stop = key_stop;
        key_start = underline;
        key_stop = underline+1;
    }

    if (!unsafe_key_start && colon)
    {
        DO_CALLBACK_SIM(element_ns, state, state->line, state->col, key_start, colon, colon);
        key_start = colon+1;
    }
    DO_CALLBACK_SIM(element_key, state, state->line, state->col, key_start, key_stop, key_stop);
    if (unsafe_key_start)
    {
        DO_CALLBACK_SIM(apar_left, state, state->line, state->col, leftpar, leftpar+1, leftpar+1);
        if (unsafe_key_start)
        {
            DO_CALLBACK_SIM(attr_key, state, state->line, state->col, underline, underline+1, underline+1);
            DO_CALLBACK_SIM(attr_value_quote, state, state->line, state->col, unsafe_key_start, unsafe_key_stop, unsafe_key_stop);
        }
        DO_CALLBACK_SIM(apar_right, state, state->line, state->col, rightpar, rightpar+1, rightpar+1);
    }

    DO_CALLBACK(element_value_text, state, start_line, start_col, start, stop, stop);
}

bool is_json_number(XMQParseState *state)
{
    return NULL != is_jnumber(state->i, state->buffer_stop);
}

void eat_json_number(XMQParseState *state)
{
    const char *start = state->i;
    const char *stop = state->buffer_stop;
    const char *i = start;
    size_t line = state->line;
    size_t col = state->col;

    const char *end = is_jnumber(i, stop);
    assert(end); // Must not call eat_json_number without check for a number before...
    increment('?', end-start, &i, &line, &col);

    state->i = i;
    state->line = line;
    state->col = col;
}

void parse_json_number(XMQParseState *state, const char *key_start, const char *key_stop)
{
    const char *start = state->i;
    int start_line = state->line;
    int start_col = state->col;

    eat_json_number(state);
    const char *stop = state->i;

    const char *unsafe_key_start = NULL;
    const char *unsafe_key_stop = NULL;
    const char *colon = NULL;

    trim_index_suffix(key_start, &key_stop);

    if (!key_start || key_start == key_stop)
    {
        // No key and the empty key translates into a _
        key_start = underline;
        key_stop = underline+1;
    }
    else if (!is_xmq_element_name(key_start, key_stop, &colon))
    {
        unsafe_key_start = key_start;
        unsafe_key_stop = key_stop;
        key_start = underline;
        key_stop = underline+1;
    }

    if (!unsafe_key_start && colon)
    {
        DO_CALLBACK_SIM(element_ns, state, state->line, state->col, key_start, colon, colon);
        key_start = colon+1;
    }
    DO_CALLBACK_SIM(element_key, state, state->line, state->col, key_start, key_stop, key_stop);
    if (unsafe_key_start)
    {
        DO_CALLBACK_SIM(apar_left, state, state->line, state->col, leftpar, leftpar+1, leftpar+1);
        if (unsafe_key_start)
        {
            DO_CALLBACK_SIM(attr_key, state, state->line, state->col, underline, underline+1, underline+1);
            DO_CALLBACK_SIM(attr_value_quote, state, state->line, state->col, unsafe_key_start, unsafe_key_stop, unsafe_key_stop);
        }
        DO_CALLBACK_SIM(apar_right, state, state->line, state->col, rightpar, rightpar+1, rightpar+1);
    }

    DO_CALLBACK(element_value_text, state, start_line, start_col, start, stop, stop);
}

bool xmq_tokenize_buffer_json(XMQParseState *state, const char *start, const char *stop)
{
    if (state->magic_cookie != MAGIC_COOKIE)
    {
        PRINT_ERROR("Parser state not initialized!\n");
        assert(0);
        exit(1);
    }

    state->buffer_start = start;
    state->buffer_stop = stop;
    state->i = start;
    state->line = 1;
    state->col = 1;
    state->error_nr = XMQ_ERROR_NONE;

    if (state->parse->init) state->parse->init(state);

    if (!setjmp(state->error_handler))
    {
        parse_json(state, NULL, NULL);
        if (state->i < state->buffer_stop)
        {
            state->error_nr = XMQ_ERROR_UNEXPECTED_CLOSING_BRACE;
            longjmp(state->error_handler, 1);
        }
    }
    else
    {
        XMQParseError error_nr = state->error_nr;
        generate_state_error_message(state, error_nr, start, stop);
        return false;
    }

    if (state->parse->done) state->parse->done(state);
    return true;
}

void parse_json_array(XMQParseState *state, const char *key_start, const char *key_stop)
{
    char c = *state->i;
    assert(c == '[');
    increment(c, 1, &state->i, &state->line, &state->col);

    const char *unsafe_key_start = NULL;
    const char *unsafe_key_stop = NULL;
    const char *colon = NULL;

    trim_index_suffix(key_start, &key_stop);

    if (!key_start || key_start == key_stop)
    {
        // No key and the empty key translates into a _
        key_start = underline;
        key_stop = underline+1;
    }
    else if (!is_xmq_element_name(key_start, key_stop, &colon))
    {
        unsafe_key_start = key_start;
        unsafe_key_stop = key_stop;
        key_start = underline;
        key_stop = underline+1;
    }

    if (!unsafe_key_start && colon)
    {
        DO_CALLBACK_SIM(element_ns, state, state->line, state->col, key_start, colon, colon);
        key_start = colon+1;
    }
    DO_CALLBACK_SIM(element_key, state, state->line, state->col, key_start, key_stop, key_stop);
    DO_CALLBACK_SIM(apar_left, state, state->line, state->col, leftpar, leftpar+1, leftpar+1);
    if (unsafe_key_start)
    {
        DO_CALLBACK_SIM(attr_key, state, state->line, state->col, underline, underline+1, underline+1);
        DO_CALLBACK_SIM(attr_value_quote, state, state->line, state->col, unsafe_key_start, unsafe_key_stop, unsafe_key_stop);
    }
    DO_CALLBACK_SIM(attr_key, state, state->line, state->col, array, array+1, array+1);
    DO_CALLBACK_SIM(apar_right, state, state->line, state->col, rightpar, rightpar+1, rightpar+1);

    DO_CALLBACK_SIM(brace_left, state, state->line, state->col, leftbrace, leftbrace+1, leftbrace+1);

    const char *stop = state->buffer_stop;

    c = ',';

    while (state->i < stop && c == ',')
    {
        eat_xml_whitespace(state, NULL, NULL);
        c = *(state->i);
        if (c == ']') break;

        parse_json(state, NULL, NULL);
        c = *state->i;
        if (c == ',') increment(c, 1, &state->i, &state->line, &state->col);
    }

    assert(c == ']');
    increment(c, 1, &state->i, &state->line, &state->col);

    DO_CALLBACK_SIM(brace_right, state, state->line, state->col, rightbrace, rightbrace+1, rightbrace+1);
}

void parse_json(XMQParseState *state, const char *key_start, const char *key_stop)
{
    eat_xml_whitespace(state, NULL, NULL);

    char c = *(state->i);

    if (is_json_quote_start(c)) parse_json_quote(state, key_start, key_stop);
    else if (is_json_boolean(state)) parse_json_boolean(state, key_start, key_stop);
    else if (is_json_null(state)) parse_json_null(state, key_start, key_stop);
    else if (is_json_number(state)) parse_json_number(state, key_start, key_stop);
    else if (c == '{') parse_json_object(state, key_start, key_stop);
    else if (c == '[') parse_json_array(state, key_start, key_stop);
    else
    {
        state->error_nr = XMQ_ERROR_JSON_INVALID_CHAR;
        longjmp(state->error_handler, 1);
    }
    eat_xml_whitespace(state, NULL, NULL);
}

typedef struct
{
    size_t total;
    size_t used;
} Counter;

void json_print_object_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to)
{
    xmlNode *i = from;

    HashMap* map = hashmap_create(100);

    while (i)
    {
        const char *name = (const char*)i->name;
        if (name && strcmp(name, "_")) // We have a name and it is NOT a single _
        {
            Counter *c = (Counter*)hashmap_get(map, name);
            if (!c)
            {
                c = (Counter*)malloc(sizeof(Counter));
                memset(c, 0, sizeof(Counter));
                hashmap_put(map, name, c);
            }
            c->total++;
        }
        if (i == to) break;
        i = xml_next_sibling(i);
    }

    i = from;
    while (i)
    {
        const char *name = (const char*)i->name;
        if (name && strcmp(name, "_"))
        {
            Counter *c = (Counter*)hashmap_get(map, (const char*)i->name);
            json_print_node(ps, container, i, c->total, c->used);
            c->used++;
        }
        else
        {
            json_print_node(ps, container, i, 1, 0);
        }
        if (i == to) break;
        i = xml_next_sibling(i);
    }

    hashmap_free_and_values(map, free);
}

void json_print_array_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to)
{
    xmlNode *i = from;
    while (i)
    {
        json_check_comma(ps);
        bool force_string = xml_get_attribute(i, "S");
        bool is_number = xml_element_content(i) && json_is_number(xml_element_content(i));
        bool is_keyword = xml_element_content(i) && json_is_keyword(xml_element_content(i));

        if (force_string || is_number || is_keyword)
        {
            json_print_value(ps, xml_first_child(i), xml_last_child(i), LEVEL_ELEMENT_VALUE, force_string);
        }
        else
        {
            json_print_node(ps, NULL, i, 1, 0);
        }
        i = xml_next_sibling(i);
    }
}

bool has_attr_other_than_AS_(xmlNode *node)
{
    xmlAttr *a = xml_first_attribute(node);

    while (a)
    {
        if (strcmp((const char*)a->name, "A") &&
            strcmp((const char*)a->name, "S") &&
            strcmp((const char*)a->name, "_")) return true;
        a = a->next;
    }

    return false;
}

void json_print_node(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_t total, size_t used)
{
    // This is a comment translated into "//":"Comment text"
    if (is_comment_node(node))
    {
        json_print_comment_node(ps, node, false, total, used);
        return;
    }

    // Standalone quote must be quoted: 'word' 'some words'
    if (is_content_node(node))
    {
        json_print_standalone_quote(ps, container, node, total, used);
        return;
    }

    // This is an entity reference node. &something;
    if (is_entity_node(node))
    {
        json_print_entity_node(ps, node);
        return;
    }

    // This is a node with no children, but the only such valid json nodes are
    // the empty object _ ---> {} or _(A) ---> [].
    if (is_leaf_node(node) && container)
    {
        return json_print_leaf_node(ps, container, node, total, used);
    }

    // This is a key = value or key = 'value value' or key = ( 'value' &#10; )
    // Also! If the node has attributes, then we cannot print as key value in json.
    // It has to be an object.
    if (is_key_value_node(node) &&
        (!has_attributes(node) ||
         !has_attr_other_than_AS_(node)))
    {
        bool force_string = xml_get_attribute(node, "S");
        return json_print_key_node(ps, container, node, total, used, force_string);
    }

    // The node is marked foo(A) { } translate this into: "foo":[ ]
    if (xml_get_attribute(node, "A"))
    {
        const char *name = xml_element_name(node);
        bool is_underline = (name[0] == '_' && name[1] == 0);
        bool has_attr = has_attr_other_than_AS_(node);
        if (!is_underline && !container)
        {
            // The xmq "alfa(A) { _=hello _=there }" translates into
            // the json ["hello","there"] and there is no sensible
            // way of storing the alfa element name. Fixable?
            PRINT_WARNING("xmq: Warning! The element name \"%s\" is lost when converted to an unnamed json array!\n", name);
        }
        if (has_attr)
        {
            // The xmq "_(A beta=123) { _=hello _=there }" translates into
            // the json ["hello","there"] and there is no sensible
            // way of storing the beta attribute. Fixable?
            PRINT_WARNING("xmq: Warning! The element \"%s\" loses its attributes when converted to a json array!\n", name);
        }
        return json_print_array_with_children(ps, container, node);
    }

    // All other nodes are printed
    json_print_element_with_children(ps, container, node, total, used);
}

void parse_json_object(XMQParseState *state, const char *key_start, const char *key_stop)
{
    char c = *state->i;
    assert(c == '{');
    increment(c, 1, &state->i, &state->line, &state->col);

    const char *unsafe_key_start = NULL;
    const char *unsafe_key_stop = NULL;
    const char *colon = NULL;

    trim_index_suffix(key_start, &key_stop);

    if (!key_start || key_start == key_stop)
    {
        // No key and the empty key translates into a _
        key_start = underline;
        key_stop = underline+1;
    }
    else if (!is_xmq_element_name(key_start, key_stop, &colon))
    {
        unsafe_key_start = key_start;
        unsafe_key_stop = key_stop;
        key_start = underline;
        key_stop = underline+1;
    }

    if (!unsafe_key_start && colon)
    {
        DO_CALLBACK_SIM(element_ns, state, state->line, state->col, key_start, colon, colon);
        key_start = colon+1;
    }
    DO_CALLBACK_SIM(element_key, state, state->line, state->col, colon?colon+1:key_start, key_stop, key_stop);
    if (unsafe_key_start)
    {
        DO_CALLBACK_SIM(apar_left, state, state->line, state->col, leftpar, leftpar+1, leftpar+1);
        DO_CALLBACK_SIM(attr_key, state, state->line, state->col, underline, underline+1, underline+1);
        DO_CALLBACK_SIM(attr_value_quote, state, state->line, state->col, unsafe_key_start, unsafe_key_stop, unsafe_key_stop);
        DO_CALLBACK_SIM(apar_right, state, state->line, state->col, rightpar, rightpar+1, rightpar+1);
    }

    DO_CALLBACK_SIM(brace_left, state, state->line, state->col, leftbrace, leftbrace+1, leftbrace+1);

    const char *stop = state->buffer_stop;

    c = ',';

    while (state->i < stop && c == ',')
    {
        eat_xml_whitespace(state, NULL, NULL);
        c = *(state->i);
        if (c == '}') break;

        if (!is_json_quote_start(c))
        {
            state->error_nr = XMQ_ERROR_JSON_INVALID_CHAR;
            longjmp(state->error_handler, 1);
        }

        // Find the key string, ie speed in { "speed":123 }
        char *new_key_start = NULL;
        char *new_key_stop = NULL;
        eat_json_quote(state, &new_key_start, &new_key_stop);

        eat_xml_whitespace(state, NULL, NULL);
        c = *(state->i);

        if (c == ':')
        {
            increment(c, 1, &state->i, &state->line, &state->col);
        }
        else
        {
            state->error_nr = XMQ_ERROR_JSON_INVALID_CHAR;
            longjmp(state->error_handler, 1);
        }

        parse_json(state, new_key_start, new_key_stop);
        free(new_key_start);

        c = *state->i;
        if (c == ',') increment(c, 1, &state->i, &state->line, &state->col);
    }
    while (c == ',');

    assert(c == '}');
    increment(c, 1, &state->i, &state->line, &state->col);

    DO_CALLBACK_SIM(brace_right, state, state->line, state->col, rightbrace, rightbrace+1, rightbrace+1);
}

void json_print_value(XMQPrintState *ps, xmlNode *from, xmlNode *to, Level level, bool force_string)
{
    XMQOutputSettings *output_settings = ps->output_settings;
    XMQWrite write = output_settings->content.write;
    void *writer_state = output_settings->content.writer_state;

    xmlNode *node = from;
    const char *content = xml_element_content(node);

    if (!xml_next_sibling(node) &&
        !force_string &&
        (json_is_number(xml_element_content(node))
         || json_is_keyword(xml_element_content(node))))
    {
        // This is a number or a keyword. E.g. 123 true false null
        write(writer_state, content, NULL);
        ps->last_char = content[strlen(content)-1];
    }
    else if (!xml_next_sibling(node) && content[0] == 0)
    {
        write(writer_state, "\"\"", NULL);
        ps->last_char = '"';
    }
    else
    {
        print_utf8(ps, COLOR_none, 1, "\"", NULL);

        if (is_entity_node(node))
        {
            write(writer_state, "&", NULL);
            write(writer_state, (const char*)node->name, NULL);
            write(writer_state, ";", NULL);
        }
        else
        {
            do
            {
                if (is_entity_node(node))
                {
                    const char *name = xml_element_name(node);
                    print_utf8(ps, COLOR_none, 3, "&", NULL, name, NULL, ";", NULL);
                }
                else
                {
                    const char *value = xml_element_content(node);
                    if (value)
                    {
                        char *quoted_value = xmq_quote_as_c(value, value+strlen(value), false);
                        print_utf8(ps, COLOR_none, 1, quoted_value, NULL);
                        free(quoted_value);
                    }
                }
                if (node == to) break;
                node = xml_next_sibling(node);
            } while (node);
        }

        print_utf8(ps, COLOR_none, 1, "\"", NULL);
        ps->last_char = '"';
    }
}

void json_print_array_with_children(XMQPrintState *ps,
                                    xmlNode *container,
                                    xmlNode *node)
{
    json_check_comma(ps);

    if (container)
    {
        // We have a containing node, then we can print this using "name" : [ ... ]
        json_print_element_name(ps, container, node, 1, 0);
        print_utf8(ps, COLOR_none, 1, ":", NULL);
    }

    void *from = xml_first_child(node);
    void *to = xml_last_child(node);

    print_utf8(ps, COLOR_brace_left, 1, "[", NULL);
    ps->last_char = '[';

    ps->line_indent += ps->output_settings->add_indent;

    if (!container)
    {
        // Top level object or object inside array. [ {} {} ]
        // Dump the element name! It cannot be represented!
    }

    if (from)
    {
        while (xml_prev_sibling((xmlNode*)from)) from = xml_prev_sibling((xmlNode*)from);
        assert(from != NULL);
    }

    json_print_array_nodes(ps, NULL, (xmlNode*)from, (xmlNode*)to);

    ps->line_indent -= ps->output_settings->add_indent;

    print_utf8(ps, COLOR_brace_right, 1, "]", NULL);
    ps->last_char = ']';
}

void json_print_attribute(XMQPrintState *ps, xmlAttr *a)
{
    const char *key;
    const char *prefix;
    size_t total_u_len;
    attr_strlen_name_prefix(a, &key, &prefix, &total_u_len);

    // Do not print "_" attributes since they are the name of the element
    // when the element name is not valid xml.
    if (!strcmp(key, "_")) return;

    json_check_comma(ps);

    char *quoted_key = xmq_quote_as_c(key, key+strlen(key), false);
    print_utf8(ps, COLOR_none, 1, "\"_", NULL);
    if (prefix)
    {
        print_utf8(ps, COLOR_none, 1, prefix, NULL);
        print_utf8(ps, COLOR_none, 1, ":", NULL);
    }
    print_utf8(ps, COLOR_none, 2, quoted_key, NULL, "\":", NULL);
    free(quoted_key);

    if (a->children != NULL)
    {
        char *value = (char*)xmlNodeListGetString(a->doc, a->children, 1);
        char *quoted_value = xmq_quote_as_c(value, value+strlen(value), true);
        print_utf8(ps, COLOR_none, 1, quoted_value, NULL);
        free(quoted_value);
        xmlFree(value);
    }
    else
    {
        print_utf8(ps, COLOR_none, 1, "null", NULL);
    }
}

void json_print_namespace_declaration(XMQPrintState *ps, xmlNs *ns)
{
    const char *prefix;
    size_t total_u_len;

    namespace_strlen_prefix(ns, &prefix, &total_u_len);

    json_check_comma(ps);

    print_utf8(ps, COLOR_none, 1, "\"_xmlns", NULL);

    if (prefix)
    {
        print_utf8(ps, COLOR_none, 1, ":", NULL);
        print_utf8(ps, COLOR_none, 1, prefix, NULL);
    }
    print_utf8(ps, COLOR_none, 1, "\":", NULL);

    const char *v = xml_namespace_href(ns);

    if (v != NULL)
    {
        print_utf8(ps, COLOR_none, 3, "\"", NULL, v, NULL, "\"", NULL);
    }
    else
    {
        print_utf8(ps, COLOR_none, 1, "null", NULL);
    }

}

void json_print_attributes(XMQPrintState *ps,
                           xmlNodePtr node)
{
    xmlAttr *a = xml_first_attribute(node);
    xmlNs *ns = xml_first_namespace_def(node);

    while (a)
    {
        json_print_attribute(ps, a);
        a = xml_next_attribute(a);
    }

    while (ns)
    {
        json_print_namespace_declaration(ps, ns);
        ns = xml_next_namespace_def(ns);
    }
}

void json_print_element_with_children(XMQPrintState *ps,
                                      xmlNode *container,
                                      xmlNode *node,
                                      size_t total,
                                      size_t used)
{
    json_check_comma(ps);

    if (container)
    {
        // We have a containing node, then we can print this using "name" : { ... }
        json_print_element_name(ps, container, node, total, used);
        print_utf8(ps, COLOR_none, 1, ":", NULL);
    }

    void *from = xml_first_child(node);
    void *to = xml_last_child(node);

    print_utf8(ps, COLOR_brace_left, 1, "{", NULL);
    ps->last_char = '{';

    ps->line_indent += ps->output_settings->add_indent;

    while (!container && ps->pre_nodes && ps->pre_nodes->size > 0)
    {
        xmlNodePtr node = (xmlNodePtr)stack_rock(ps->pre_nodes);

        if (is_doctype_node(node))
        {
            json_print_doctype_node(ps, node);
        }
        else if (is_comment_node(node))
        {
            json_print_comment_node(ps, node, true, ps->pre_post_num_comments_total, ps->pre_post_num_comments_used++);
        }
        else
        {
            assert(false);
        }
    }

    const char *name = xml_element_name(node);
    bool is_underline = (name[0] == '_' && name[1] == 0);
    if (!container && name && !is_underline)
    {
        // Top level object or object inside array.
        // Hide the name of the object inside the json object with the key "_".
        // I.e. x { a=1 } -> { "_":"x", "a":1 }
        json_check_comma(ps);
        print_utf8(ps, COLOR_none, 1, "\"_\":", NULL);
        ps->last_char = ':';
        json_print_element_name(ps, container, node, total, used);
    }

    json_print_attributes(ps, node);

    if (from)
    {
        while (xml_prev_sibling((xmlNode*)from)) from = xml_prev_sibling((xmlNode*)from);
        assert(from != NULL);
    }

    json_print_object_nodes(ps, node, (xmlNode*)from, (xmlNode*)to);

    while (!container && ps->post_nodes && ps->post_nodes->size > 0)
    {
        xmlNodePtr node = (xmlNodePtr)stack_rock(ps->post_nodes);

        if (is_comment_node(node))
        {
            json_print_comment_node(ps, node, true, ps->pre_post_num_comments_total, ps->pre_post_num_comments_used++);
        }
        else
        {
            assert(false);
        }
    }

    ps->line_indent -= ps->output_settings->add_indent;

    print_utf8(ps, COLOR_brace_right, 1, "}", NULL);
    ps->last_char = '}';
}

void json_print_element_name(XMQPrintState *ps, xmlNode *container, xmlNode *node, size_t total, size_t used)
{
    const char *name = (const char*)node->name;
    const char *prefix = NULL;

    if (node->ns && node->ns->prefix)
    {
        prefix = (const char*)node->ns->prefix;
    }

    print_utf8(ps, COLOR_none, 1, "\"", NULL);

    if (prefix)
    {
        print_utf8(ps, COLOR_none, 1, prefix, NULL);
        print_utf8(ps, COLOR_none, 1, ":", NULL);
    }

    if (name[0] != '_' || name[1] != 0)
    {
        print_utf8(ps, COLOR_none, 1, name, NULL);
    }
    else
    {
        xmlAttr *a = xml_get_attribute(node, "_");
        if (a)
        {
            // The key was stored inside the attribute because it could not
            // be used as the element name.
            char *value = (char*)xmlNodeListGetString(node->doc, a->children, 1);
            char *quoted_value = xmq_quote_as_c(value, value+strlen(value), false);
            print_utf8(ps, COLOR_none, 1, quoted_value, NULL);
            free(quoted_value);
            xmlFree(value);
            ps->last_char = '"';
        }
    }

    if (total > 1)
    {
        char buf[32];
        buf[31] = 0;
        snprintf(buf, 32, "[%zu]", used);
        print_utf8(ps, COLOR_none, 1, buf, NULL);
    }
    print_utf8(ps, COLOR_none, 1, "\"", NULL);

    ps->last_char = '"';
}

void json_print_key_node(XMQPrintState *ps,
                         xmlNode *container,
                         xmlNode *node,
                         size_t total,
                         size_t used,
                         bool force_string)
{
    json_check_comma(ps);

    if (container)
    {
        json_print_element_name(ps, container, node, total, used);
        print_utf8(ps, COLOR_equals, 1, ":", NULL);
        ps->last_char = ':';
    }

    json_print_value(ps, xml_first_child(node), xml_last_child(node), LEVEL_ELEMENT_VALUE, force_string);
}

void json_check_comma(XMQPrintState *ps)
{
    char c = ps->last_char;
    if (c == 0) return;

    if (c != '{' && c != '[' && c != ',')
    {
        json_print_comma(ps);
    }
}

void json_print_comma(XMQPrintState *ps)
{
    XMQOutputSettings *output_settings = ps->output_settings;
    XMQWrite write = output_settings->content.write;
    void *writer_state = output_settings->content.writer_state;
    write(writer_state, ",", NULL);
    ps->last_char = ',';
    ps->current_indent ++;
}

void json_print_comment_node(XMQPrintState *ps,
                             xmlNode *node,
                             bool prefix_ul,
                             size_t total,
                             size_t used)
{
    json_check_comma(ps);

    if (prefix_ul) print_utf8(ps, COLOR_equals, 1, "\"_//", NULL);
    else print_utf8(ps, COLOR_equals, 1, "\"//", NULL);

    if (total > 1)
    {
        char buf[32];
        buf[31] = 0;
        snprintf(buf, 32, "[%zu]\":", used);
        print_utf8(ps, COLOR_equals, 1, buf, NULL);
    }
    else
    {
        print_utf8(ps, COLOR_equals, 1, "\":", NULL);
    }
    ps->last_char = ':';
    json_print_value(ps, node, node, LEVEL_XMQ, true);
    ps->last_char = '"';
}

void json_print_doctype_node(XMQPrintState *ps, xmlNodePtr node)
{
    json_check_comma(ps);

    // Print !DOCTYPE inside top level object.
    // I.e. !DOCTYPE=html html { body = a } -> { "!DOCTYPE":"html", "html":{ "body":"a"}}
    print_utf8(ps, COLOR_none, 1, "\"!DOCTYPE\":", NULL);
    ps->last_char = ':';
    xmlBuffer *buffer = xmlBufferCreate();
    xmlNodeDump(buffer, (xmlDocPtr)ps->doq->docptr_.xml, node, 0, 0);
    char *c = (char*)xmlBufferContent(buffer);
    char *quoted_value = xmq_quote_as_c(c+10, c+strlen(c)-1, true);
    print_utf8(ps, COLOR_none, 1, quoted_value, NULL);
    free(quoted_value);
    xmlBufferFree(buffer);
    ps->last_char = '"';
}

void json_print_entity_node(XMQPrintState *ps, xmlNodePtr node)
{
    json_check_comma(ps);

    const char *name = xml_element_name(node);

    print_utf8(ps, COLOR_none, 3, "\"&\":\"&", NULL, name, NULL, ";\"", NULL);
    ps->last_char = '"';
}

void json_print_standalone_quote(XMQPrintState *ps, xmlNodePtr container, xmlNodePtr node, size_t total, size_t used)
{
    json_check_comma(ps);
    const char *value = xml_element_content(node);
    char *quoted_value = xmq_quote_as_c(value, value+strlen(value), false);
    if (total == 1)
    {
        print_utf8(ps, COLOR_none, 3, "\"|\":\"", NULL, quoted_value, NULL, "\"", NULL);
    }
    else
    {
        char buf[32];
        buf[31] = 0;
        snprintf(buf, 32, "\"|[%zu]\":\"", used);
        print_utf8(ps, COLOR_none, 3, buf, NULL, quoted_value, NULL, "\"", NULL);
    }
    free(quoted_value);
    ps->last_char = '"';
}

bool json_is_number(const char *start)
{
    const char *stop = start+strlen(start);
    return stop == is_jnumber(start, stop);
}

bool json_is_keyword(const char *start)
{
    if (!strcmp(start, "true")) return true;
    if (!strcmp(start, "false")) return true;
    if (!strcmp(start, "null")) return true;
    return false;
}

void json_print_leaf_node(XMQPrintState *ps,
                          xmlNode *container,
                          xmlNode *node,
                          size_t total,
                          size_t used)
{
    XMQOutputSettings *output_settings = ps->output_settings;
    XMQWrite write = output_settings->content.write;
    void *writer_state = output_settings->content.writer_state;
    const char *name = xml_element_name(node);

    json_check_comma(ps);

    if (name)
    {
        if (!(name[0] == '_' && name[1] == 0))
        {
            json_print_element_name(ps, container, node, total, used);
            write(writer_state, ":", NULL);
        }
    }

    if (xml_get_attribute(node, "A"))
    {
        write(writer_state, "[]", NULL);
        ps->last_char = ']';
    }
    else
    {
        if (xml_first_attribute(node))
        {
            write(writer_state, "{", NULL);
            ps->last_char = '{';
            json_print_attributes(ps, node);
            write(writer_state, "}", NULL);
            ps->last_char = '}';
        }
        else
        {
            write(writer_state, "{}", NULL);
            ps->last_char = '}';
        }
    }
}

void fixup_json(XMQDoc *doq, xmlNode *node)
{
    if (is_element_node(node))
    {
        char *new_content = xml_collapse_text(node);
        if (new_content)
        {
            xmlNodePtr new_child = xmlNewDocText(doq->docptr_.xml, (const xmlChar*)new_content);
            xmlNode *i = node->children;
            while (i) {
                xmlNode *next = i->next;
                xmlUnlinkNode(i);
                xmlFreeNode(i);
                i = next;
            }
            assert(node);
            assert(new_child);
            xmlAddChild(node, new_child);
            free(new_content);
            return;
        }
    }

    xmlNode *i = xml_first_child(node);
    while (i)
    {
        xmlNode *next = xml_next_sibling(i); // i might be freed in trim.
        fixup_json(doq, i);
        i = next;
    }
}

void xmq_fixup_json_before_writeout(XMQDoc *doq)
{
    if (doq == NULL || doq->docptr_.xml == NULL) return;
    xmlNodePtr i = doq->docptr_.xml->children;
    if (!doq || !i) return;

    while (i)
    {
        xmlNode *next = xml_next_sibling(i); // i might be freed in fixup_json.
        fixup_json(doq, i);
        i = next;
    }
}

void collect_leading_ending_comments_doctype(XMQPrintState *ps, xmlNodePtr *first, xmlNodePtr *last)
{
    xmlNodePtr f = *first;
    xmlNodePtr l = *last;
    xmlNodePtr node;

    for (node = f; node && node != l; node = node->next)
    {
        if (is_doctype_node(node) || is_comment_node(node))
        {
            stack_push(ps->pre_nodes, node);
            if (is_comment_node(node)) ps->pre_post_num_comments_total++;
            continue;
        }
        break;
    }

    if (*first != node)
    {
        *first = node;
        f = node;
    }

    for (node = l; node && node != f; node = node->prev)
    {
        if (is_comment_node(node))
        {
            stack_push(ps->post_nodes, node);
            ps->pre_post_num_comments_total++;
            continue;
        }
        break;
    }

    if (*last != node)
    {
        *last = node;
    }
}

#else

// Empty function when XMQ_NO_JSON is defined.
void xmq_fixup_json_before_writeout(XMQDoc *doq)
{
}

// Empty function when XMQ_NO_JSON is defined.
bool xmq_parse_buffer_json(XMQDoc *doq, const char *start, const char *stop)
{
    return false;
}

// Empty function when XMQ_NO_JSON is defined.
void json_print_object_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to)
{
}

void collect_leading_ending_comments_doctype(XMQPrintState *ps, xmlNodePtr *first, xmlNodePtr *last)
{
}

void json_print_array_nodes(XMQPrintState *ps, xmlNode *container, xmlNode *from, xmlNode *to)
{
}

#endif // JSON_MODULE

// PART C TEXT_C ////////////////////////////////////////

#ifdef TEXT_MODULE

size_t count_whitespace(const char *i, const char *stop)
{
    unsigned char c = *i;
    if (c == ' ' || c == '\n' || c == '\t' || c == '\r')
    {
        return 1;
    }

    if (i+1 >= stop) return 0;

    // If not a unbreakable space U+00A0 (utf8 0xc2a0)
    // or the other unicode whitespaces (utf8 starts with 0xe2)
    // then we have not whitespaces here.
    if (c != 0xc2 && c != 0xe2) return 0;

    unsigned char cc = *(i+1);

    if (c == 0xC2 && cc == 0xA0)
    {
        // Unbreakable space. 0xA0
        return 2;
    }
    if (c == 0xE2 && cc == 0x80)
    {
        if (i+2 >= stop) return 0;

        unsigned char ccc = *(i+2);

        if (ccc == 0x80)
        {
            // EN quad. &#8192; U+2000 utf8 E2 80 80
            return 3;
        }
        if (ccc == 0x81)
        {
            // EM quad. &#8193; U+2001 utf8 E2 80 81
            return 3;
        }
        if (ccc == 0x82)
        {
            // EN space. &#8194; U+2002 utf8 E2 80 82
            return 3;
        }
        if (ccc == 0x83)
        {
            // EM space. &#8195; U+2003 utf8 E2 80 83
            return 3;
        }
    }
    return 0;
}

const char *has_leading_space_nl(const char *start, const char *stop, size_t *only_newlines)
{
    const char *i = start;
    bool found_nl = false;
    size_t only_nls = 0;

    if (only_newlines != NULL) *only_newlines = 0;

    // Look for leading newlines.
    while (i < stop && *i == '\n')
    {
        i++;
        only_nls++;
    }
    const char *middle = NULL;

    // Yep, we found some leading pure newlines.
    if (only_nls > 0)
    {
        found_nl = true;
        middle = i;
    }

    // Scan other leading whitespace, perhaps none.
    while (i < stop)
    {
        if (*i == '\n') found_nl = true;
        if (!is_xml_whitespace(*i)) break;
        i++;
    }

    // No newline found before content, so leading spaces/tabs will not be trimmed.
    if (!found_nl) return 0;

    if (middle == i)
    {
        // We have for example "\ncontent" this we can represent in xmq with a visible empty line, eg:
        // '
        //
        // content'
        if (only_newlines != NULL) *only_newlines = only_nls;
    }
    return i;
}

const char *has_ending_nl_space(const char *start, const char *stop, size_t *only_newlines)
{
    const char *i = stop;
    bool found_nl = false;
    size_t only_nls = 0;

    if (only_newlines != NULL) *only_newlines = 0;

    // Look for ending newlines.
    i--;
    while (i >= start && *i == '\n')
    {
        i--;
        only_nls++;
        found_nl = true;
    }
    const char *middle = i;

    while (i >= start)
    {
        if (*i == '\n') found_nl = true;
        if (!is_xml_whitespace(*i)) break;
        i--;
    }
    // No newline found after content, so ending spaces/tabs will not be trimmed.
    if (!found_nl) return 0;

    if (middle == i)
    {
        // We have for example "content\n" this we can represent in xmq with a visible empty line, eg:
        // 'content
        //
        // '
        if (only_newlines != NULL) *only_newlines = only_nls;
    }

    i++;
    return i;
}

bool has_leading_ending_different_quotes(const char *start, const char *stop)
{
    return start < stop && (
        ( *start == '\'' && *(stop-1) == '"')
        ||
        ( *start == '"' && *(stop-1) == '\''));
}

bool has_newlines(const char *start, const char *stop)
{
    for (const char *i = start; i < stop; ++i)
    {
        if (*i == '\n') return true;
    }
    return false;
}

bool has_must_escape_chars(const char *start, const char *stop)
{
    for (const char *i = start; i < stop; ++i)
    {
        if (*i == '\n') return true;
    }
    return false;
}

bool has_all_quotes(const char *start, const char *stop)
{
    if (start == stop) return false;
    bool all_sq = true;
    for (const char *i = start; i < stop; ++i)
    {
        if (*i != '\'')
        {
            all_sq = false;
            break;
        }
    }
    if (all_sq) return true;

    for (const char *i = start; i < stop; ++i)
    {
        if (*i != '"') return false;
    }

    return true;
}

bool has_all_whitespace(const char *start, const char *stop, bool *all_space, bool *only_newlines)
{
    *all_space = true;
    *only_newlines = true;
    for (const char *i = start; i < stop; ++i)
    {
        if (!is_xml_whitespace(*i))
        {
            *all_space = false;
            *only_newlines = false;
            return false;
        }
        if (*i != ' ' && *all_space == true)
        {
            *all_space = false;
        }
        if (*i != '\n' && *only_newlines == true)
        {
            *only_newlines = false;
        }
    }
    return true;
}

bool is_lowercase_hex(char c)
{
    return
        (c >= '0' && c <= '9') ||
        (c >= 'a' && c <= 'f');
}

size_t num_utf8_bytes(char c)
{
    if ((c & 0x80) == 0) return 1;
    if ((c & 0xe0) == 0xc0) return 2;
    if ((c & 0xf0) == 0xe0) return 3;
    if ((c & 0xf8) == 0xf0) return 4;
    return 0; // Error
}

/**
   peek_utf8_char: Peek 1 to 4 chars from s belonging to the next utf8 code point and store them in uc.
   @start: Read utf8 from this string.
   @stop: Points to byte after last byte in string. If NULL assume start is null terminated.
   @uc: Store the UTF8 char here.

   Return the number of bytes peek UTF8 char, use this number to skip ahead to next char in s.
*/
size_t peek_utf8_char(const char *start, const char *stop, UTF8Char *uc)
{
    char a = *start;
    size_t n = num_utf8_bytes(a);

    if (n == 1)
    {
        uc->bytes[0] = a;
        uc->bytes[1] = 0;
        uc->bytes[2] = 0;
        uc->bytes[3] = 0;
        return 1;
    }

    char b = *(start+1);
    if (n == 2)
    {
        uc->bytes[0] = a;
        uc->bytes[1] = b;
        uc->bytes[2] = 0;
        uc->bytes[3] = 0;
        return 2;
    }

    char c = *(start+2);
    if (n == 3)
    {
        uc->bytes[0] = a;
        uc->bytes[1] = b;
        uc->bytes[2] = c;
        uc->bytes[3] = 0;
        return 3;
    }

    char d = *(start+3);
    if (n == 4)
    {
        uc->bytes[0] = a;
        uc->bytes[1] = b;
        uc->bytes[2] = c;
        uc->bytes[3] = d;
        return 4;
    }

    return 0;
}

/**
   utf8_char_to_codepoint_string: Decode an utf8 char and store as a string "U+123"
   @uc: The utf8 char to decode.
   @buf: Store the codepoint string here must have space for 9 bytes, i.e. U+10FFFF and a NULL byte.
*/
bool utf8_char_to_codepoint_string(UTF8Char *uc, char *buf)
{
    int cp = 0;
    size_t len = 0;
    bool ok = decode_utf8(uc->bytes, uc->bytes+4, &cp, &len);
    if (!ok)
    {
        snprintf(buf, 16, "U+error");
        return false;
    }
    snprintf(buf, 16, "U+%X", cp);
    return true;
}

/**
   encode_utf8: Convert an integer unicode code point into utf8 bytes.
   @uc: The unicode code point to encode as utf8
   @out_char: Store the unicode code point here.
   @out_len: How many bytes the utf8 char used.

   Return true if valid utf8 char.
*/
size_t encode_utf8(int uc, UTF8Char *utf8)
{
    utf8->bytes[0] = 0;
    utf8->bytes[1] = 0;
    utf8->bytes[2] = 0;
    utf8->bytes[3] = 0;
    utf8->bytes[4] = 0;

    if (uc <= 0x7f)
    {
        utf8->bytes[0] = uc;
        return 1;
    }
    else if (uc <= 0x7ff)
    {
        utf8->bytes[0] = (0xc0 | ((uc >> 6) & 0x1f));
        utf8->bytes[1] = (0x80 | (uc & 0x3f));
        return 2;
    }
    else if (uc <= 0xffff)
    {
        utf8->bytes[0] = (0xe0 | ((uc >> 12) & 0x0f));
        utf8->bytes[1] = (0x80 | ((uc >> 6) & 0x3f));
        utf8->bytes[2] = (0x80 | (uc & 0x3f));
        return 3;
    }
    assert (uc <= 0x10ffff);
    utf8->bytes[0] = (0xf0 | ((uc >> 18) & 0x07));
    utf8->bytes[1] = (0x80 | ((uc >> 12) & 0x3f));
    utf8->bytes[2] = (0x80 | ((uc >> 6) & 0x3f));
    utf8->bytes[3] = (0x80 | (uc & 0x3f));
    return 4;
}

/**
   decode_utf8: Peek 1 to 4 chars from start and calculate unicode codepoint.
   @start: Read utf8 from this string.
   @stop: Points to byte after last byte in string. If NULL assume start is null terminated.
   @out_char: Store the unicode code point here.
   @out_len: How many bytes the utf8 char used.

   Return true if valid utf8 char.
*/
bool decode_utf8(const char *start, const char *stop, int *out_char, size_t *out_len)
{
    int c = (int)(unsigned char)(*start);

    if ((c & 0x80) == 0)
    {
        *out_char = c;
        *out_len = 1;
        return true;
    }

    if ((c & 0xe0) == 0xc0)
    {
        if (start+1 < stop)
        {
            unsigned char cc = *(start+1);
            if ((cc & 0xc0) == 0x80)
            {
                *out_char = ((c & 0x1f) << 6) | (cc & 0x3f);
                *out_len = 2;
                return true;
            }
        }
    }
    else if ((c & 0xf0) == 0xe0)
    {
        if (start+2 < stop)
        {
            unsigned char cc = *(start+1);
            unsigned char ccc = *(start+2);
            if (((cc & 0xc0) == 0x80) && ((ccc & 0xc0) == 0x80))
            {
                *out_char = ((c & 0x0f) << 12) | ((cc & 0x3f) << 6) | (ccc & 0x3f) ;
                *out_len = 3;
                return true;
            }
        }
    }
    else if ((c & 0xf8) == 0xf0)
    {
        if (start+3 < stop)
        {
            unsigned char cc = *(start+1);
            unsigned char ccc = *(start+2);
            unsigned char cccc = *(start+3);
            if (((cc & 0xc0) == 0x80) && ((ccc & 0xc0) == 0x80) && ((cccc & 0xc0) == 0x80))
            {
                *out_char = ((c & 0x07) << 18) | ((cc & 0x3f) << 12) | ((ccc & 0x3f) << 6) | (cccc & 0x3f);
                *out_len = 4;
                return true;
            }
        }
    }

    // Illegal utf8.
    *out_char = 1;
    *out_len = 1;
    return false;
}

/**
    str_b_u_len: Count bytes and unicode characters.
    @start:
    @stop
    @b_len:
    @u_len:

    Store the number of actual bytes. Which is stop-start, and strlen(start) if stop is NULL.
    Count actual unicode characters between start and stop (ie all bytes not having the two msb bits set to 0x10xxxxxx).
    This will have to be improved if we want to handle indentation with characters with combining diacritics.
*/
void str_b_u_len(const char *start, const char *stop, size_t *b_len, size_t *u_len)
{
    assert(start);
    if (stop)
    {
        *b_len = stop - start;
        size_t u = 0;
        for (const char *i = start; i < stop; ++i)
        {
            if ((*i & 0xc0) != 0x80) u++;
        }
        *u_len = u;
        return;
    }

    size_t b = 0;
    size_t u = 0;
    for (const char *i = start; *i != 0; ++i)
    {
        if ((*i & 0xc0) != 0x80) u++;
        b++;
    }
    *b_len = b;
    *u_len = u;
}

bool is_xmq_text_name(char c)
{
    if (c >= 'a' && c <= 'z') return true;
    if (c >= 'A' && c <= 'Z') return true;
    if (c >= '0' && c <= '9') return true;
    if (c == '-' || c == '_' || c == '.' || c == ':' || c == '#') return true;
    return false;
}

bool is_xmq_element_start(char c)
{
    if (c >= 'a' && c <= 'z') return true;
    if (c >= 'A' && c <= 'Z') return true;
    if (c == '_') return true;
    return false;
}

bool is_xmq_element_name(const char *start, const char *stop, const char **colon)
{
    const char *i = start;
    *colon = NULL;
    if (!is_xmq_element_start(*i)) return false;
    i++;

    for (; i < stop; ++i)
    {
        char c = *i;
        if (!is_xmq_text_name(c)) return false;
        if (c == ':') *colon = i;
    }

    return true;
}

bool is_xmq_token_whitespace(char c)
{
    if (c == ' ' || c == '\n' || c == '\r')
    {
        return true;
    }
    return false;
}

bool is_xml_whitespace(char c)
{
    if (c == ' ' || c == '\n' || c == '\t' || c == '\r')
    {
        return true;
    }
    return false;
}

bool is_all_xml_whitespace(const char *s)
{
    if (!s) return false;

    for (const char *i = s; *i; ++i)
    {
        if (!is_xml_whitespace(*i)) return false;
    }
    return true;
}

char to_hex(int c)
{
    if (c >= 0 && c <= 9) return '0'+c;
    return 'A'-10+c;
}

/**
    xmq_quote_as_c:

    Escape the in string using c/json quotes. I.e. Surround with " and newline becomes \n and " become \" etc.
*/
char *xmq_quote_as_c(const char *start, const char *stop, bool add_quotes)
{
    if (!stop) stop = start+strlen(start);
    if (stop == start)
    {
        if (add_quotes)
        {
            char *tmp = (char*)malloc(3);
            tmp[0] = '"';
            tmp[1] = '"';
            tmp[2] = 0;
            return tmp;
        }
        else
        {
            char *tmp = (char*)malloc(1);
            tmp[0] = 0;
            return tmp;
        }
    }
    assert(stop > start);
    size_t len = 1+(stop-start)*4+2; // Worst case expansion of all chars. +2 for qutes.
    char *buf = (char*)malloc(len);

    const char *i = start;
    char *o = buf;
    size_t real = 0;

    if (add_quotes)
    {
        real++;
        *o++ = '"';
    }

    for (; i < stop; ++i)
    {
        UTF8Char uc;
        size_t n = peek_utf8_char(i, stop, &uc);
        if (n > 1)
        {
            while (n) {
                *o++ = *i;
                real++;
                n--;
                i++;
            }
            i--;
            continue;
        }
        char c = *i;
        if (c >= ' ' && c <= 126 && c != '"' && c != '\\') { *o++ = *i; real++;}
        else if (c == '\\') { *o++ = '\\'; *o++ = '\\'; real+=2; }
        else if (c == '"') { *o++ = '\\'; *o++ = '"'; real+=2; }
        else if (c == '\a') { *o++ = '\\'; *o++ = 'a'; real+=2; }
        else if (c == '\b') { *o++ = '\\'; *o++ = 'b'; real+=2; }
        else if (c == '\t') { *o++ = '\\'; *o++ = 't'; real+=2; }
        else if (c == '\n') { *o++ = '\\'; *o++ = 'n'; real+=2; }
        else if (c == '\v') { *o++ = '\\'; *o++ = 'v'; real+=2; }
        else if (c == '\f') { *o++ = '\\'; *o++ = 'f'; real+=2; }
        else if (c == '\r') { *o++ = '\\'; *o++ = 'r'; real+=2; }
        else { *o++ = '\\'; *o++ = 'x'; *o++ = to_hex((c>>4)&0xf); *o++ = to_hex(c&0xf); real+=4; }
        if (c == 0) break;
    }
    if (add_quotes) {
        real++;
        *o++ = '"';
    }
    real++;
    *o = 0;
    buf = (char*)realloc(buf, real);
    return buf;
}

/**
    xmq_unquote_as_c:

    Unescape the in string using c/json quotes. I.e. Replace \" with ", \n with newline etc.
*/
char *xmq_unquote_as_c(const char *start, const char *stop, bool remove_quotes)
{
    if (stop == start)
    {
        char *tmp = (char*)malloc(1);
        tmp[0] = 0;
        return tmp;
    }
    assert(stop > start);
    size_t len = 1+stop-start; // It gets shorter when unescaping. Worst case no escape was found.
    char *buf = (char*)malloc(len);

    const char *i = start;
    char *o = buf;
    size_t real = 0;

    if (remove_quotes)
    {
        for (; i < stop && is_xml_whitespace(*i); ++i);
        if (*i != '"') return strdup("[Not a valid C escaped string]");
        i++;
    }

    for (; i < stop && (!remove_quotes || *i != '"'); ++i, real++)
    {
        char c = *i;
        if (c == '\\') {
            i++;
            if (i >= stop) break;
            c = *i;
            if (c == '"') *o++ = '"';
            else if (c == 'n') *o++ = '\n';
            else if (c == 'a') *o++ = '\a';
            else if (c == 'b') *o++ = '\b';
            else if (c == 't') *o++ = '\t';
            else if (c == 'v') *o++ = '\v';
            else if (c == 'f') *o++ = '\f';
            else if (c == 'r') *o++ = '\r';
            // Ignore or what?
        }
        else
        {
            *o++ = *i;
        }
    }
    if (remove_quotes)
    {
        if (*i != '"') return strdup("[Not a valid C escaped string]");
    }
    real++;
    *o = 0;
    buf = (char*)realloc(buf, real);
    return buf;
}

char *potentially_add_leading_ending_space(const char *start, const char *stop)
{
    char *content = NULL;
    int prefix = *start == '\'' ? 1 : 0;
    int postfix = *(stop-1) == '\'' ? 1 : 0;
    if (prefix || postfix)
    {
        size_t len = stop-start;
        len += prefix;
        len += postfix;
        content = (char*)malloc(len+1);
        if (prefix)
        {
            content[0] = ' ';
        }
        if (postfix)
        {
            content[len-1] = ' ';
        }
        memcpy(content+prefix, start, stop-start);
        content[len] = 0;
    }
    else
    {
        content = strndup(start, stop-start);
    }
    return content;
}

bool find_line_col(const char *start, const char *stop, size_t at, int *out_line, int *out_col)
{
    const char *i = start;
    int line = 1;
    int col = 1;
    while (i < stop && at > 0)
    {
        int oc;
        size_t ol;
        bool ok = decode_utf8(i, stop, &oc, &ol);
        assert(ok);
        i += ol;
        col++;
        if (oc == 10)
        {
            line++;
            col=0;
        }
        at--;
    }

    *out_line = line;
    *out_col = col;
    return true;
}

#define COMMA ,
#define UNICODE_CATEGORIES \
    X(Lu,Uppercase_Letter,"Lu",an uppercase letter) \
    X(Ll,Lowercase_Letter,"Ll",a lowercase letter)  \
    X(Lt,Titlecase_Letter,"Lt",a digraph encoded as a single character with first part uppercase) \
    X(LC,Cased_Letter,"Lu" COMMA "Ll" COMMA "Lt",cased letters Lu Ll Lt) \
    X(Lm,Modifier_Letter,"Lm",a modifier letter) \
    X(Lo,Other_Letter,"Lo",other letters including syllables and ideographs) \
    X(L,Letter,"Lu" COMMA "Ll" COMMA "Lt" COMMA "Lm" COMMA "Lo",letters Lu Ll Lt Lm Lo) \
    X(Mn,Nonspacing_Mark,"Mn",a nonspacing combining mark (zero advance width)) \
    X(Mc,Spacing_Mark,"Mc",a spacing combining mark (positive advance width)) \
    X(Me,Enclosing_Mark,"Me",an enclosing combining mark) \
    X(M,Mark,"Mn" COMMA "Mc" COMMA "Me",marks Mn | Mc | Me) \
    X(Nd,Decimal_Number,"Nd",a decimal digit) \
    X(Nl,Letter_Number,"Nl",a letterlike numeric character) \
    X(No,Other_Number,"No",a numeric character of other type) \
    X(N,Number,"Nd" COMMA "Nl" COMMA "No",numbers Nd | Nl | No) \
    X(Pc,Connector_Punctuation,"Pc",a connecting punctuation mark like a tie) \
    X(Pd,Dash_Punctuation,"Pd",a dash or hyphen punctuation mark) \
    X(Ps,Open_Punctuation,"Ps",an opening punctuation mark (of a pair)) \
    X(Pe,Close_Punctuation,"Pe",a closing punctuation mark (of a pair)) \
    X(Pi,Initial_Punctuation,"Pi",an initial quotation mark) \
    X(Pf,Final_Punctuation,"Pf",a final quotation mark) \
    X(Po,Other_Punctuation,"Po",a punctuation mark of other type) \
    X(P,Punctuation,"Pc" COMMA "Pd" COMMA "Ps" COMMA "Pe" COMMA "Pi" COMMA "Pf" COMMA "Po",punctuations Pc | Pd | Ps | Pe | Pi | Pf | Po) \
    X(Sm,Math_Symbol,"Sm",a symbol of mathematical use) \
    X(Sc,Currency_Symbol,"Sc",a currency sign) \
    X(Sk,Modifier_Symbol,"Sk",a non-letterlike modifier symbol) \
    X(So,Other_Symbol,"So",a symbol of other type) \
    X(S,Symbol,"Sm" COMMA "Sc" COMMA "Sk" COMMA "So",symbols Sm | Sc | Sk | So) \
    X(Zs,Space_Separator,"Zs",a space character (of various non-zero widths)) \
    X(Zl,Line_Separator,"Zl",U+2028 LINE SEPARATOR only) \
    X(Zp,Paragraph_Separator,"Zp",U+2029 PARAGRAPH SEPARATOR only) \
    X(Z,Separator,"Zs" COMMA "Zl" COMMA "Zp",separators Zs | Zl | Zp) \
    X(Cc,Control,"Cc",a C0 or C1 control code) \
    X(Cf,Format,"Cf",a format control character) \
    X(Cs,Surrogate,"Cs",a surrogate code point) \
    X(Co,Private_Use,"Co",a private-use character) \
    X(Cn,Unassigned,"Cn",a reserved unassigned code point or a noncharacter) \
    X(C,Other,"Cc" COMMA "Cf" COMMA "Cs" COMMA "Co" COMMA "Cn",others Cc | Cf | Cs | Co | Cn) \


const char *unicode_categories_[] = {
#define X(cat,name,strings,explanation) #cat,
UNICODE_CATEGORIES
};
#undef X

#define X(cat,name,strings,explanation) const char *unicode_category_##cat[] = { strings, 0 };
UNICODE_CATEGORIES
#undef X

int unicode_Ll[] = {0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006A,0x006B,0x006C,0x006D,0x006E,0x006F,0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007A,0x00B5,0x00DF,0x00E0,0x00E1,0x00E2,0x00E3,0x00E4,0x00E5,0x00E6,0x00E7,0x00E8,0x00E9,0x00EA,0x00EB,0x00EC,0x00ED,0x00EE,0x00EF,0x00F0,0x00F1,0x00F2,0x00F3,0x00F4,0x00F5,0x00F6,0x00F8,0x00F9,0x00FA,0x00FB,0x00FC,0x00FD,0x00FE,0x00FF,0x0101,0x0103,0x0105,0x0107,0x0109,0x010B,0x010D,0x010F,0x0111,0x0113,0x0115,0x0117,0x0119,0x011B,0x011D,0x011F,0x0121,0x0123,0x0125,0x0127,0x0129,0x012B,0x012D,0x012F,0x0131,0x0133,0x0135,0x0137,0x0138,0x013A,0x013C,0x013E,0x0140,0x0142,0x0144,0x0146,0x0148,0x0149,0x014B,0x014D,0x014F,0x0151,0x0153,0x0155,0x0157,0x0159,0x015B,0x015D,0x015F,0x0161,0x0163,0x0165,0x0167,0x0169,0x016B,0x016D,0x016F,0x0171,0x0173,0x0175,0x0177,0x017A,0x017C,0x017E,0x017F,0x0180,0x0183,0x0185,0x0188,0x018C,0x018D,0x0192,0x0195,0x0199,0x019A,0x019B,0x019E,0x01A1,0x01A3,0x01A5,0x01A8,0x01AA,0x01AB,0x01AD,0x01B0,0x01B4,0x01B6,0x01B9,0x01BA,0x01BD,0x01BE,0x01BF,0x01C6,0x01C9,0x01CC,0x01CE,0x01D0,0x01D2,0x01D4,0x01D6,0x01D8,0x01DA,0x01DC,0x01DD,0x01DF,0x01E1,0x01E3,0x01E5,0x01E7,0x01E9,0x01EB,0x01ED,0x01EF,0x01F0,0x01F3,0x01F5,0x01F9,0x01FB,0x01FD,0x01FF,0x0201,0x0203,0x0205,0x0207,0x0209,0x020B,0x020D,0x020F,0x0211,0x0213,0x0215,0x0217,0x0219,0x021B,0x021D,0x021F,0x0221,0x0223,0x0225,0x0227,0x0229,0x022B,0x022D,0x022F,0x0231,0x0233,0x0234,0x0235,0x0236,0x0237,0x0238,0x0239,0x023C,0x023F,0x0240,0x0242,0x0247,0x0249,0x024B,0x024D,0x024F,0x0250,0x0251,0x0252,0x0253,0x0254,0x0255,0x0256,0x0257,0x0258,0x0259,0x025A,0x025B,0x025C,0x025D,0x025E,0x025F,0x0260,0x0261,0x0262,0x0263,0x0264,0x0265,0x0266,0x0267,0x0268,0x0269,0x026A,0x026B,0x026C,0x026D,0x026E,0x026F,0x0270,0x0271,0x0272,0x0273,0x0274,0x0275,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x027D,0x027E,0x027F,0x0280,0x0281,0x0282,0x0283,0x0284,0x0285,0x0286,0x0287,0x0288,0x0289,0x028A,0x028B,0x028C,0x028D,0x028E,0x028F,0x0290,0x0291,0x0292,0x0293,0x0295,0x0296,0x0297,0x0298,0x0299,0x029A,0x029B,0x029C,0x029D,0x029E,0x029F,0x02A0,0x02A1,0x02A2,0x02A3,0x02A4,0x02A5,0x02A6,0x02A7,0x02A8,0x02A9,0x02AA,0x02AB,0x02AC,0x02AD,0x02AE,0x02AF,0x0371,0x0373,0x0377,0x037B,0x037C,0x037D,0x0390,0x03AC,0x03AD,0x03AE,0x03AF,0x03B0,0x03B1,0x03B2,0x03B3,0x03B4,0x03B5,0x03B6,0x03B7,0x03B8,0x03B9,0x03BA,0x03BB,0x03BC,0x03BD,0x03BE,0x03BF,0x03C0,0x03C1,0x03C2,0x03C3,0x03C4,0x03C5,0x03C6,0x03C7,0x03C8,0x03C9,0x03CA,0x03CB,0x03CC,0x03CD,0x03CE,0x03D0,0x03D1,0x03D5,0x03D6,0x03D7,0x03D9,0x03DB,0x03DD,0x03DF,0x03E1,0x03E3,0x03E5,0x03E7,0x03E9,0x03EB,0x03ED,0x03EF,0x03F0,0x03F1,0x03F2,0x03F3,0x03F5,0x03F8,0x03FB,0x03FC,0x0430,0x0431,0x0432,0x0433,0x0434,0x0435,0x0436,0x0437,0x0438,0x0439,0x043A,0x043B,0x043C,0x043D,0x043E,0x043F,0x0440,0x0441,0x0442,0x0443,0x0444,0x0445,0x0446,0x0447,0x0448,0x0449,0x044A,0x044B,0x044C,0x044D,0x044E,0x044F,0x0450,0x0451,0x0452,0x0453,0x0454,0x0455,0x0456,0x0457,0x0458,0x0459,0x045A,0x045B,0x045C,0x045D,0x045E,0x045F,0x0461,0x0463,0x0465,0x0467,0x0469,0x046B,0x046D,0x046F,0x0471,0x0473,0x0475,0x0477,0x0479,0x047B,0x047D,0x047F,0x0481,0x048B,0x048D,0x048F,0x0491,0x0493,0x0495,0x0497,0x0499,0x049B,0x049D,0x049F,0x04A1,0x04A3,0x04A5,0x04A7,0x04A9,0x04AB,0x04AD,0x04AF,0x04B1,0x04B3,0x04B5,0x04B7,0x04B9,0x04BB,0x04BD,0x04BF,0x04C2,0x04C4,0x04C6,0x04C8,0x04CA,0x04CC,0x04CE,0x04CF,0x04D1,0x04D3,0x04D5,0x04D7,0x04D9,0x04DB,0x04DD,0x04DF,0x04E1,0x04E3,0x04E5,0x04E7,0x04E9,0x04EB,0x04ED,0x04EF,0x04F1,0x04F3,0x04F5,0x04F7,0x04F9,0x04FB,0x04FD,0x04FF,0x0501,0x0503,0x0505,0x0507,0x0509,0x050B,0x050D,0x050F,0x0511,0x0513,0x0515,0x0517,0x0519,0x051B,0x051D,0x051F,0x0521,0x0523,0x0525,0x0527,0x0529,0x052B,0x052D,0x052F,0x0560,0x0561,0x0562,0x0563,0x0564,0x0565,0x0566,0x0567,0x0568,0x0569,0x056A,0x056B,0x056C,0x056D,0x056E,0x056F,0x0570,0x0571,0x0572,0x0573,0x0574,0x0575,0x0576,0x0577,0x0578,0x0579,0x057A,0x057B,0x057C,0x057D,0x057E,0x057F,0x0580,0x0581,0x0582,0x0583,0x0584,0x0585,0x0586,0x0587,0x0588,0x1042,0x1042,0x1042,0x1042,0x1042,0x1042,0x1042,0x1042,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1043,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x1044,0x104D,0x104D,0x104D,0x104D,0x104D,0x104D,0x104D,0x104D,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104E,0x104F,0x104F,0x104F,0x104F,0x104F,0x104F,0x104F,0x104F,0x104F,0x104F,0x104F,0x104F,0x1059,0x1059,0x1059,0x1059,0x1059,0x1059,0x1059,0x1059,0x1059,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105A,0x105B,0x105B,0x105B,0x105B,0x105B,0x105B,0x105B,0x105B,0x105B,0x105B,0x105B,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CC,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CD,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CE,0x10CF,0x10CF,0x10CF,0x10D0,0x10D1,0x10D2,0x10D3,0x10D4,0x10D5,0x10D6,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D7,0x10D8,0x10D8,0x10D8,0x10D8,0x10D8,0x10D8,0x10D8,0x10D9,0x10DA,0x10DB,0x10DC,0x10DD,0x10DE,0x10DF,0x10E0,0x10E1,0x10E2,0x10E3,0x10E4,0x10E5,0x10E6,0x10E7,0x10E8,0x10E9,0x10EA,0x10EB,0x10EC,0x10ED,0x10EE,0x10EF,0x10F0,0x10F1,0x10F2,0x10F3,0x10F4,0x10F5,0x10F6,0x10F7,0x10F8,0x10F9,0x10FA,0x10FD,0x10FE,0x10FF,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118C,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x118D,0x13F8,0x13F9,0x13FA,0x13FB,0x13FC,0x13FD,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E6,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x16E7,0x1C80,0x1C81,0x1C82,0x1C83,0x1C84,0x1C85,0x1C86,0x1C87,0x1C88,0x1C8A,0x1D00,0x1D01,0x1D02,0x1D03,0x1D04,0x1D05,0x1D06,0x1D07,0x1D08,0x1D09,0x1D0A,0x1D0B,0x1D0C,0x1D0D,0x1D0E,0x1D0F,0x1D10,0x1D11,0x1D12,0x1D13,0x1D14,0x1D15,0x1D16,0x1D17,0x1D18,0x1D19,0x1D1A,0x1D1B,0x1D1C,0x1D1D,0x1D1E,0x1D1F,0x1D20,0x1D21,0x1D22,0x1D23,0x1D24,0x1D25,0x1D26,0x1D27,0x1D28,0x1D29,0x1D2A,0x1D2B,0x1D41,0x1D41,0x1D41,0x1D41,0x1D41,0x1D41,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D42,0x1D43,0x1D43,0x1D43,0x1D43,0x1D44,0x1D44,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D45,0x1D46,0x1D46,0x1D46,0x1D46,0x1D46,0x1D46,0x1D46,0x1D46,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D48,0x1D49,0x1D49,0x1D49,0x1D49,0x1D49,0x1D49,0x1D49,0x1D49,0x1D49,0x1D49,0x1D49,0x1D49,0x1D4B,0x1D4B,0x1D4B,0x1D4B,0x1D4B,0x1D4B,0x1D4B,0x1D4B,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4C,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D4F,0x1D50,0x1D50,0x1D50,0x1D50,0x1D51,0x1D51,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D52,0x1D53,0x1D53,0x1D53,0x1D53,0x1D53,0x1D53,0x1D53,0x1D53,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D55,0x1D56,0x1D56,0x1D56,0x1D56,0x1D56,0x1D56,0x1D56,0x1D56,0x1D56,0x1D56,0x1D56,0x1D56,0x1D58,0x1D58,0x1D58,0x1D58,0x1D58,0x1D58,0x1D58,0x1D58,0x1D58,0x1D58,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D59,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5C,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5E,0x1D5E,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D5F,0x1D60,0x1D60,0x1D60,0x1D60,0x1D60,0x1D60,0x1D60,0x1D60,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D62,0x1D63,0x1D63,0x1D63,0x1D63,0x1D63,0x1D63,0x1D63,0x1D63,0x1D63,0x1D63,0x1D63,0x1D63,0x1D65,0x1D65,0x1D65,0x1D65,0x1D65,0x1D65,0x1D65,0x1D65,0x1D65,0x1D65,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D66,0x1D68,0x1D68,0x1D68,0x1D68,0x1D68,0x1D68,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D69,0x1D6A,0x1D6A,0x1D6A,0x1D6A,0x1D6A,0x1D6A,0x1D6B,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6C,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6D,0x1D6E,0x1D6E,0x1D6E,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D70,0x1D71,0x1D71,0x1D71,0x1D71,0x1D71,0x1D71,0x1D71,0x1D71,0x1D71,0x1D71,0x1D71,0x1D71,0x1D72,0x1D73,0x1D73,0x1D73,0x1D73,0x1D73,0x1D73,0x1D73,0x1D73,0x1D73,0x1D73,0x1D73,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D74,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D76,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D77,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D78,0x1D79,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7B,0x1D7C,0x1D7C,0x1D7C,0x1D7C,0x1D7C,0x1D7C,0x1D7C,0x1D7C,0x1D7C,0x1D7C,0x1D7C,0x1D7D,0x1D7E,0x1D7F,0x1D80,0x1D81,0x1D82,0x1D83,0x1D84,0x1D85,0x1D86,0x1D87,0x1D88,0x1D89,0x1D8A,0x1D8B,0x1D8C,0x1D8D,0x1D8E,0x1D8F,0x1D90,0x1D91,0x1D92,0x1D93,0x1D94,0x1D95,0x1D96,0x1D97,0x1D98,0x1D99,0x1D9A,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF0,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF1,0x1DF2,0x1DF2,0x1DF2,0x1DF2,0x1DF2,0x1DF2,0x1E01,0x1E03,0x1E05,0x1E07,0x1E09,0x1E0B,0x1E0D,0x1E0F,0x1E11,0x1E13,0x1E15,0x1E17,0x1E19,0x1E1B,0x1E1D,0x1E1F,0x1E21,0x1E23,0x1E25,0x1E27,0x1E29,0x1E2B,0x1E2D,0x1E2F,0x1E31,0x1E33,0x1E35,0x1E37,0x1E39,0x1E3B,0x1E3D,0x1E3F,0x1E41,0x1E43,0x1E45,0x1E47,0x1E49,0x1E4B,0x1E4D,0x1E4F,0x1E51,0x1E53,0x1E55,0x1E57,0x1E59,0x1E5B,0x1E5D,0x1E5F,0x1E61,0x1E63,0x1E65,0x1E67,0x1E69,0x1E6B,0x1E6D,0x1E6F,0x1E71,0x1E73,0x1E75,0x1E77,0x1E79,0x1E7B,0x1E7D,0x1E7F,0x1E81,0x1E83,0x1E85,0x1E87,0x1E89,0x1E8B,0x1E8D,0x1E8F,0x1E91,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E92,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E93,0x1E94,0x1E94,0x1E94,0x1E94,0x1E95,0x1E96,0x1E97,0x1E98,0x1E99,0x1E9A,0x1E9B,0x1E9C,0x1E9D,0x1E9F,0x1EA1,0x1EA3,0x1EA5,0x1EA7,0x1EA9,0x1EAB,0x1EAD,0x1EAF,0x1EB1,0x1EB3,0x1EB5,0x1EB7,0x1EB9,0x1EBB,0x1EBD,0x1EBF,0x1EC1,0x1EC3,0x1EC5,0x1EC7,0x1EC9,0x1ECB,0x1ECD,0x1ECF,0x1ED1,0x1ED3,0x1ED5,0x1ED7,0x1ED9,0x1EDB,0x1EDD,0x1EDF,0x1EE1,0x1EE3,0x1EE5,0x1EE7,0x1EE9,0x1EEB,0x1EED,0x1EEF,0x1EF1,0x1EF3,0x1EF5,0x1EF7,0x1EF9,0x1EFB,0x1EFD,0x1EFF,0x1F00,0x1F01,0x1F02,0x1F03,0x1F04,0x1F05,0x1F06,0x1F07,0x1F10,0x1F11,0x1F12,0x1F13,0x1F14,0x1F15,0x1F20,0x1F21,0x1F22,0x1F23,0x1F24,0x1F25,0x1F26,0x1F27,0x1F30,0x1F31,0x1F32,0x1F33,0x1F34,0x1F35,0x1F36,0x1F37,0x1F40,0x1F41,0x1F42,0x1F43,0x1F44,0x1F45,0x1F50,0x1F51,0x1F52,0x1F53,0x1F54,0x1F55,0x1F56,0x1F57,0x1F60,0x1F61,0x1F62,0x1F63,0x1F64,0x1F65,0x1F66,0x1F67,0x1F70,0x1F71,0x1F72,0x1F73,0x1F74,0x1F75,0x1F76,0x1F77,0x1F78,0x1F79,0x1F7A,0x1F7B,0x1F7C,0x1F7D,0x1F80,0x1F81,0x1F82,0x1F83,0x1F84,0x1F85,0x1F86,0x1F87,0x1F90,0x1F91,0x1F92,0x1F93,0x1F94,0x1F95,0x1F96,0x1F97,0x1FA0,0x1FA1,0x1FA2,0x1FA3,0x1FA4,0x1FA5,0x1FA6,0x1FA7,0x1FB0,0x1FB1,0x1FB2,0x1FB3,0x1FB4,0x1FB6,0x1FB7,0x1FBE,0x1FC2,0x1FC3,0x1FC4,0x1FC6,0x1FC7,0x1FD0,0x1FD1,0x1FD2,0x1FD3,0x1FD6,0x1FD7,0x1FE0,0x1FE1,0x1FE2,0x1FE3,0x1FE4,0x1FE5,0x1FE6,0x1FE7,0x1FF2,0x1FF3,0x1FF4,0x1FF6,0x1FF7,0x210A,0x210E,0x210F,0x2113,0x212F,0x2134,0x2139,0x213C,0x213D,0x2146,0x2147,0x2148,0x2149,0x214E,0x2184,0x2C30,0x2C31,0x2C32,0x2C33,0x2C34,0x2C35,0x2C36,0x2C37,0x2C38,0x2C39,0x2C3A,0x2C3B,0x2C3C,0x2C3D,0x2C3E,0x2C3F,0x2C40,0x2C41,0x2C42,0x2C43,0x2C44,0x2C45,0x2C46,0x2C47,0x2C48,0x2C49,0x2C4A,0x2C4B,0x2C4C,0x2C4D,0x2C4E,0x2C4F,0x2C50,0x2C51,0x2C52,0x2C53,0x2C54,0x2C55,0x2C56,0x2C57,0x2C58,0x2C59,0x2C5A,0x2C5B,0x2C5C,0x2C5D,0x2C5E,0x2C5F,0x2C61,0x2C65,0x2C66,0x2C68,0x2C6A,0x2C6C,0x2C71,0x2C73,0x2C74,0x2C76,0x2C77,0x2C78,0x2C79,0x2C7A,0x2C7B,0x2C81,0x2C83,0x2C85,0x2C87,0x2C89,0x2C8B,0x2C8D,0x2C8F,0x2C91,0x2C93,0x2C95,0x2C97,0x2C99,0x2C9B,0x2C9D,0x2C9F,0x2CA1,0x2CA3,0x2CA5,0x2CA7,0x2CA9,0x2CAB,0x2CAD,0x2CAF,0x2CB1,0x2CB3,0x2CB5,0x2CB7,0x2CB9,0x2CBB,0x2CBD,0x2CBF,0x2CC1,0x2CC3,0x2CC5,0x2CC7,0x2CC9,0x2CCB,0x2CCD,0x2CCF,0x2CD1,0x2CD3,0x2CD5,0x2CD7,0x2CD9,0x2CDB,0x2CDD,0x2CDF,0x2CE1,0x2CE3,0x2CE4,0x2CEC,0x2CEE,0x2CF3,0x2D00,0x2D01,0x2D02,0x2D03,0x2D04,0x2D05,0x2D06,0x2D07,0x2D08,0x2D09,0x2D0A,0x2D0B,0x2D0C,0x2D0D,0x2D0E,0x2D0F,0x2D10,0x2D11,0x2D12,0x2D13,0x2D14,0x2D15,0x2D16,0x2D17,0x2D18,0x2D19,0x2D1A,0x2D1B,0x2D1C,0x2D1D,0x2D1E,0x2D1F,0x2D20,0x2D21,0x2D22,0x2D23,0x2D24,0x2D25,0x2D27,0x2D2D,0xA641,0xA643,0xA645,0xA647,0xA649,0xA64B,0xA64D,0xA64F,0xA651,0xA653,0xA655,0xA657,0xA659,0xA65B,0xA65D,0xA65F,0xA661,0xA663,0xA665,0xA667,0xA669,0xA66B,0xA66D,0xA681,0xA683,0xA685,0xA687,0xA689,0xA68B,0xA68D,0xA68F,0xA691,0xA693,0xA695,0xA697,0xA699,0xA69B,0xA723,0xA725,0xA727,0xA729,0xA72B,0xA72D,0xA72F,0xA730,0xA731,0xA733,0xA735,0xA737,0xA739,0xA73B,0xA73D,0xA73F,0xA741,0xA743,0xA745,0xA747,0xA749,0xA74B,0xA74D,0xA74F,0xA751,0xA753,0xA755,0xA757,0xA759,0xA75B,0xA75D,0xA75F,0xA761,0xA763,0xA765,0xA767,0xA769,0xA76B,0xA76D,0xA76F,0xA771,0xA772,0xA773,0xA774,0xA775,0xA776,0xA777,0xA778,0xA77A,0xA77C,0xA77F,0xA781,0xA783,0xA785,0xA787,0xA78C,0xA78E,0xA791,0xA793,0xA794,0xA795,0xA797,0xA799,0xA79B,0xA79D,0xA79F,0xA7A1,0xA7A3,0xA7A5,0xA7A7,0xA7A9,0xA7AF,0xA7B5,0xA7B7,0xA7B9,0xA7BB,0xA7BD,0xA7BF,0xA7C1,0xA7C3,0xA7C8,0xA7CA,0xA7CD,0xA7D1,0xA7D3,0xA7D5,0xA7D7,0xA7D9,0xA7DB,0xA7F6,0xA7FA,0xAB30,0xAB31,0xAB32,0xAB33,0xAB34,0xAB35,0xAB36,0xAB37,0xAB38,0xAB39,0xAB3A,0xAB3B,0xAB3C,0xAB3D,0xAB3E,0xAB3F,0xAB40,0xAB41,0xAB42,0xAB43,0xAB44,0xAB45,0xAB46,0xAB47,0xAB48,0xAB49,0xAB4A,0xAB4B,0xAB4C,0xAB4D,0xAB4E,0xAB4F,0xAB50,0xAB51,0xAB52,0xAB53,0xAB54,0xAB55,0xAB56,0xAB57,0xAB58,0xAB59,0xAB5A,0xAB60,0xAB61,0xAB62,0xAB63,0xAB64,0xAB65,0xAB66,0xAB67,0xAB68,0xAB70,0xAB71,0xAB72,0xAB73,0xAB74,0xAB75,0xAB76,0xAB77,0xAB78,0xAB79,0xAB7A,0xAB7B,0xAB7C,0xAB7D,0xAB7E,0xAB7F,0xAB80,0xAB81,0xAB82,0xAB83,0xAB84,0xAB85,0xAB86,0xAB87,0xAB88,0xAB89,0xAB8A,0xAB8B,0xAB8C,0xAB8D,0xAB8E,0xAB8F,0xAB90,0xAB91,0xAB92,0xAB93,0xAB94,0xAB95,0xAB96,0xAB97,0xAB98,0xAB99,0xAB9A,0xAB9B,0xAB9C,0xAB9D,0xAB9E,0xAB9F,0xABA0,0xABA1,0xABA2,0xABA3,0xABA4,0xABA5,0xABA6,0xABA7,0xABA8,0xABA9,0xABAA,0xABAB,0xABAC,0xABAD,0xABAE,0xABAF,0xABB0,0xABB1,0xABB2,0xABB3,0xABB4,0xABB5,0xABB6,0xABB7,0xABB8,0xABB9,0xABBA,0xABBB,0xABBC,0xABBD,0xABBE,0xABBF,0xFB00,0xFB01,0xFB02,0xFB03,0xFB04,0xFB05,0xFB06,0xFB13,0xFB14,0xFB15,0xFB16,0xFB17,0xFF41,0xFF42,0xFF43,0xFF44,0xFF45,0xFF46,0xFF47,0xFF48,0xFF49,0xFF4A,0xFF4B,0xFF4C,0xFF4D,0xFF4E,0xFF4F,0xFF50,0xFF51,0xFF52,0xFF53,0xFF54,0xFF55,0xFF56,0xFF57,0xFF58,0xFF59,0xFF5A};

size_t unicode_len_Ll = sizeof(unicode_Ll)/sizeof(unicode_Ll[0]);

int unicode_Lu[] = {0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004A,0x004B,0x004C,0x004D,0x004E,0x004F,0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005A,0x00C0,0x00C1,0x00C2,0x00C3,0x00C4,0x00C5,0x00C6,0x00C7,0x00C8,0x00C9,0x00CA,0x00CB,0x00CC,0x00CD,0x00CE,0x00CF,0x00D0,0x00D1,0x00D2,0x00D3,0x00D4,0x00D5,0x00D6,0x00D8,0x00D9,0x00DA,0x00DB,0x00DC,0x00DD,0x00DE,0x0100,0x0102,0x0104,0x0106,0x0108,0x010A,0x010C,0x010E,0x0110,0x0112,0x0114,0x0116,0x0118,0x011A,0x011C,0x011E,0x0120,0x0122,0x0124,0x0126,0x0128,0x012A,0x012C,0x012E,0x0130,0x0132,0x0134,0x0136,0x0139,0x013B,0x013D,0x013F,0x0141,0x0143,0x0145,0x0147,0x014A,0x014C,0x014E,0x0150,0x0152,0x0154,0x0156,0x0158,0x015A,0x015C,0x015E,0x0160,0x0162,0x0164,0x0166,0x0168,0x016A,0x016C,0x016E,0x0170,0x0172,0x0174,0x0176,0x0178,0x0179,0x017B,0x017D,0x0181,0x0182,0x0184,0x0186,0x0187,0x0189,0x018A,0x018B,0x018E,0x018F,0x0190,0x0191,0x0193,0x0194,0x0196,0x0197,0x0198,0x019C,0x019D,0x019F,0x01A0,0x01A2,0x01A4,0x01A6,0x01A7,0x01A9,0x01AC,0x01AE,0x01AF,0x01B1,0x01B2,0x01B3,0x01B5,0x01B7,0x01B8,0x01BC,0x01C4,0x01C7,0x01CA,0x01CD,0x01CF,0x01D1,0x01D3,0x01D5,0x01D7,0x01D9,0x01DB,0x01DE,0x01E0,0x01E2,0x01E4,0x01E6,0x01E8,0x01EA,0x01EC,0x01EE,0x01F1,0x01F4,0x01F6,0x01F7,0x01F8,0x01FA,0x01FC,0x01FE,0x0200,0x0202,0x0204,0x0206,0x0208,0x020A,0x020C,0x020E,0x0210,0x0212,0x0214,0x0216,0x0218,0x021A,0x021C,0x021E,0x0220,0x0222,0x0224,0x0226,0x0228,0x022A,0x022C,0x022E,0x0230,0x0232,0x023A,0x023B,0x023D,0x023E,0x0241,0x0243,0x0244,0x0245,0x0246,0x0248,0x024A,0x024C,0x024E,0x0370,0x0372,0x0376,0x037F,0x0386,0x0388,0x0389,0x038A,0x038C,0x038E,0x038F,0x0391,0x0392,0x0393,0x0394,0x0395,0x0396,0x0397,0x0398,0x0399,0x039A,0x039B,0x039C,0x039D,0x039E,0x039F,0x03A0,0x03A1,0x03A3,0x03A4,0x03A5,0x03A6,0x03A7,0x03A8,0x03A9,0x03AA,0x03AB,0x03CF,0x03D2,0x03D3,0x03D4,0x03D8,0x03DA,0x03DC,0x03DE,0x03E0,0x03E2,0x03E4,0x03E6,0x03E8,0x03EA,0x03EC,0x03EE,0x03F4,0x03F7,0x03F9,0x03FA,0x03FD,0x03FE,0x03FF,0x0400,0x0401,0x0402,0x0403,0x0404,0x0405,0x0406,0x0407,0x0408,0x0409,0x040A,0x040B,0x040C,0x040D,0x040E,0x040F,0x0410,0x0411,0x0412,0x0413,0x0414,0x0415,0x0416,0x0417,0x0418,0x0419,0x041A,0x041B,0x041C,0x041D,0x041E,0x041F,0x0420,0x0421,0x0422,0x0423,0x0424,0x0425,0x0426,0x0427,0x0428,0x0429,0x042A,0x042B,0x042C,0x042D,0x042E,0x042F,0x0460,0x0462,0x0464,0x0466,0x0468,0x046A,0x046C,0x046E,0x0470,0x0472,0x0474,0x0476,0x0478,0x047A,0x047C,0x047E,0x0480,0x048A,0x048C,0x048E,0x0490,0x0492,0x0494,0x0496,0x0498,0x049A,0x049C,0x049E,0x04A0,0x04A2,0x04A4,0x04A6,0x04A8,0x04AA,0x04AC,0x04AE,0x04B0,0x04B2,0x04B4,0x04B6,0x04B8,0x04BA,0x04BC,0x04BE,0x04C0,0x04C1,0x04C3,0x04C5,0x04C7,0x04C9,0x04CB,0x04CD,0x04D0,0x04D2,0x04D4,0x04D6,0x04D8,0x04DA,0x04DC,0x04DE,0x04E0,0x04E2,0x04E4,0x04E6,0x04E8,0x04EA,0x04EC,0x04EE,0x04F0,0x04F2,0x04F4,0x04F6,0x04F8,0x04FA,0x04FC,0x04FE,0x0500,0x0502,0x0504,0x0506,0x0508,0x050A,0x050C,0x050E,0x0510,0x0512,0x0514,0x0516,0x0518,0x051A,0x051C,0x051E,0x0520,0x0522,0x0524,0x0526,0x0528,0x052A,0x052C,0x052E,0x0531,0x0532,0x0533,0x0534,0x0535,0x0536,0x0537,0x0538,0x0539,0x053A,0x053B,0x053C,0x053D,0x053E,0x053F,0x0540,0x0541,0x0542,0x0543,0x0544,0x0545,0x0546,0x0547,0x0548,0x0549,0x054A,0x054B,0x054C,0x054D,0x054E,0x054F,0x0550,0x0551,0x0552,0x0553,0x0554,0x0555,0x0556,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1040,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1041,0x1042,0x1042,0x1042,0x1042,0x1042,0x1042,0x1042,0x1042,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104B,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104C,0x104D,0x104D,0x104D,0x104D,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1057,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1058,0x1059,0x1059,0x1059,0x1059,0x1059,0x10A0,0x10A1,0x10A2,0x10A3,0x10A4,0x10A5,0x10A6,0x10A7,0x10A8,0x10A9,0x10AA,0x10AB,0x10AC,0x10AD,0x10AE,0x10AF,0x10B0,0x10B1,0x10B2,0x10B3,0x10B4,0x10B5,0x10B6,0x10B7,0x10B8,0x10B9,0x10BA,0x10BB,0x10BC,0x10BD,0x10BE,0x10BF,0x10C0,0x10C1,0x10C2,0x10C3,0x10C4,0x10C5,0x10C7,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C8,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10C9,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CA,0x10CB,0x10CB,0x10CB,0x10CD,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D5,0x10D6,0x10D6,0x10D6,0x10D6,0x10D6,0x10D6,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118A,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x118B,0x13A0,0x13A1,0x13A2,0x13A3,0x13A4,0x13A5,0x13A6,0x13A7,0x13A8,0x13A9,0x13AA,0x13AB,0x13AC,0x13AD,0x13AE,0x13AF,0x13B0,0x13B1,0x13B2,0x13B3,0x13B4,0x13B5,0x13B6,0x13B7,0x13B8,0x13B9,0x13BA,0x13BB,0x13BC,0x13BD,0x13BE,0x13BF,0x13C0,0x13C1,0x13C2,0x13C3,0x13C4,0x13C5,0x13C6,0x13C7,0x13C8,0x13C9,0x13CA,0x13CB,0x13CC,0x13CD,0x13CE,0x13CF,0x13D0,0x13D1,0x13D2,0x13D3,0x13D4,0x13D5,0x13D6,0x13D7,0x13D8,0x13D9,0x13DA,0x13DB,0x13DC,0x13DD,0x13DE,0x13DF,0x13E0,0x13E1,0x13E2,0x13E3,0x13E4,0x13E5,0x13E6,0x13E7,0x13E8,0x13E9,0x13EA,0x13EB,0x13EC,0x13ED,0x13EE,0x13EF,0x13F0,0x13F1,0x13F2,0x13F3,0x13F4,0x13F5,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E4,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x16E5,0x1C89,0x1C90,0x1C91,0x1C92,0x1C93,0x1C94,0x1C95,0x1C96,0x1C97,0x1C98,0x1C99,0x1C9A,0x1C9B,0x1C9C,0x1C9D,0x1C9E,0x1C9F,0x1CA0,0x1CA1,0x1CA2,0x1CA3,0x1CA4,0x1CA5,0x1CA6,0x1CA7,0x1CA8,0x1CA9,0x1CAA,0x1CAB,0x1CAC,0x1CAD,0x1CAE,0x1CAF,0x1CB0,0x1CB1,0x1CB2,0x1CB3,0x1CB4,0x1CB5,0x1CB6,0x1CB7,0x1CB8,0x1CB9,0x1CBA,0x1CBD,0x1CBE,0x1CBF,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D40,0x1D41,0x1D41,0x1D41,0x1D41,0x1D41,0x1D41,0x1D41,0x1D41,0x1D41,0x1D41,0x1D43,0x1D43,0x1D43,0x1D43,0x1D43,0x1D43,0x1D43,0x1D43,0x1D43,0x1D43,0x1D43,0x1D43,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D44,0x1D46,0x1D46,0x1D46,0x1D46,0x1D46,0x1D46,0x1D46,0x1D46,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D47,0x1D48,0x1D48,0x1D49,0x1D49,0x1D49,0x1D4A,0x1D4A,0x1D4A,0x1D4A,0x1D4A,0x1D4A,0x1D4A,0x1D4A,0x1D4A,0x1D4B,0x1D4B,0x1D4B,0x1D4B,0x1D4B,0x1D4B,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4D,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D4E,0x1D50,0x1D50,0x1D50,0x1D50,0x1D50,0x1D50,0x1D50,0x1D50,0x1D50,0x1D51,0x1D51,0x1D51,0x1D51,0x1D51,0x1D51,0x1D51,0x1D51,0x1D51,0x1D51,0x1D51,0x1D51,0x1D53,0x1D53,0x1D53,0x1D53,0x1D53,0x1D53,0x1D54,0x1D54,0x1D54,0x1D54,0x1D54,0x1D54,0x1D54,0x1D54,0x1D54,0x1D54,0x1D54,0x1D54,0x1D55,0x1D56,0x1D56,0x1D56,0x1D56,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D57,0x1D58,0x1D58,0x1D58,0x1D58,0x1D58,0x1D58,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5A,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5B,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5D,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D5E,0x1D60,0x1D60,0x1D60,0x1D60,0x1D60,0x1D60,0x1D60,0x1D60,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D61,0x1D62,0x1D62,0x1D63,0x1D63,0x1D63,0x1D63,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D64,0x1D65,0x1D65,0x1D65,0x1D65,0x1D65,0x1D65,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D67,0x1D68,0x1D68,0x1D68,0x1D68,0x1D68,0x1D68,0x1D68,0x1D68,0x1D68,0x1D68,0x1D6A,0x1D6A,0x1D6A,0x1D6A,0x1D6A,0x1D6A,0x1D6A,0x1D6A,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6B,0x1D6C,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6E,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D6F,0x1D71,0x1D71,0x1D71,0x1D71,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D72,0x1D73,0x1D73,0x1D73,0x1D73,0x1D73,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D75,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D76,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D79,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7A,0x1D7C,0x1E00,0x1E02,0x1E04,0x1E06,0x1E08,0x1E0A,0x1E0C,0x1E0E,0x1E10,0x1E12,0x1E14,0x1E16,0x1E18,0x1E1A,0x1E1C,0x1E1E,0x1E20,0x1E22,0x1E24,0x1E26,0x1E28,0x1E2A,0x1E2C,0x1E2E,0x1E30,0x1E32,0x1E34,0x1E36,0x1E38,0x1E3A,0x1E3C,0x1E3E,0x1E40,0x1E42,0x1E44,0x1E46,0x1E48,0x1E4A,0x1E4C,0x1E4E,0x1E50,0x1E52,0x1E54,0x1E56,0x1E58,0x1E5A,0x1E5C,0x1E5E,0x1E60,0x1E62,0x1E64,0x1E66,0x1E68,0x1E6A,0x1E6C,0x1E6E,0x1E70,0x1E72,0x1E74,0x1E76,0x1E78,0x1E7A,0x1E7C,0x1E7E,0x1E80,0x1E82,0x1E84,0x1E86,0x1E88,0x1E8A,0x1E8C,0x1E8E,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E90,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E91,0x1E92,0x1E92,0x1E92,0x1E94,0x1E9E,0x1EA0,0x1EA2,0x1EA4,0x1EA6,0x1EA8,0x1EAA,0x1EAC,0x1EAE,0x1EB0,0x1EB2,0x1EB4,0x1EB6,0x1EB8,0x1EBA,0x1EBC,0x1EBE,0x1EC0,0x1EC2,0x1EC4,0x1EC6,0x1EC8,0x1ECA,0x1ECC,0x1ECE,0x1ED0,0x1ED2,0x1ED4,0x1ED6,0x1ED8,0x1EDA,0x1EDC,0x1EDE,0x1EE0,0x1EE2,0x1EE4,0x1EE6,0x1EE8,0x1EEA,0x1EEC,0x1EEE,0x1EF0,0x1EF2,0x1EF4,0x1EF6,0x1EF8,0x1EFA,0x1EFC,0x1EFE,0x1F08,0x1F09,0x1F0A,0x1F0B,0x1F0C,0x1F0D,0x1F0E,0x1F0F,0x1F18,0x1F19,0x1F1A,0x1F1B,0x1F1C,0x1F1D,0x1F28,0x1F29,0x1F2A,0x1F2B,0x1F2C,0x1F2D,0x1F2E,0x1F2F,0x1F38,0x1F39,0x1F3A,0x1F3B,0x1F3C,0x1F3D,0x1F3E,0x1F3F,0x1F48,0x1F49,0x1F4A,0x1F4B,0x1F4C,0x1F4D,0x1F59,0x1F5B,0x1F5D,0x1F5F,0x1F68,0x1F69,0x1F6A,0x1F6B,0x1F6C,0x1F6D,0x1F6E,0x1F6F,0x1FB8,0x1FB9,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FD8,0x1FD9,0x1FDA,0x1FDB,0x1FE8,0x1FE9,0x1FEA,0x1FEB,0x1FEC,0x1FF8,0x1FF9,0x1FFA,0x1FFB,0x2102,0x2107,0x210B,0x210C,0x210D,0x2110,0x2111,0x2112,0x2115,0x2119,0x211A,0x211B,0x211C,0x211D,0x2124,0x2126,0x2128,0x212A,0x212B,0x212C,0x212D,0x2130,0x2131,0x2132,0x2133,0x213E,0x213F,0x2145,0x2183,0x2C00,0x2C01,0x2C02,0x2C03,0x2C04,0x2C05,0x2C06,0x2C07,0x2C08,0x2C09,0x2C0A,0x2C0B,0x2C0C,0x2C0D,0x2C0E,0x2C0F,0x2C10,0x2C11,0x2C12,0x2C13,0x2C14,0x2C15,0x2C16,0x2C17,0x2C18,0x2C19,0x2C1A,0x2C1B,0x2C1C,0x2C1D,0x2C1E,0x2C1F,0x2C20,0x2C21,0x2C22,0x2C23,0x2C24,0x2C25,0x2C26,0x2C27,0x2C28,0x2C29,0x2C2A,0x2C2B,0x2C2C,0x2C2D,0x2C2E,0x2C2F,0x2C60,0x2C62,0x2C63,0x2C64,0x2C67,0x2C69,0x2C6B,0x2C6D,0x2C6E,0x2C6F,0x2C70,0x2C72,0x2C75,0x2C7E,0x2C7F,0x2C80,0x2C82,0x2C84,0x2C86,0x2C88,0x2C8A,0x2C8C,0x2C8E,0x2C90,0x2C92,0x2C94,0x2C96,0x2C98,0x2C9A,0x2C9C,0x2C9E,0x2CA0,0x2CA2,0x2CA4,0x2CA6,0x2CA8,0x2CAA,0x2CAC,0x2CAE,0x2CB0,0x2CB2,0x2CB4,0x2CB6,0x2CB8,0x2CBA,0x2CBC,0x2CBE,0x2CC0,0x2CC2,0x2CC4,0x2CC6,0x2CC8,0x2CCA,0x2CCC,0x2CCE,0x2CD0,0x2CD2,0x2CD4,0x2CD6,0x2CD8,0x2CDA,0x2CDC,0x2CDE,0x2CE0,0x2CE2,0x2CEB,0x2CED,0x2CF2,0xA640,0xA642,0xA644,0xA646,0xA648,0xA64A,0xA64C,0xA64E,0xA650,0xA652,0xA654,0xA656,0xA658,0xA65A,0xA65C,0xA65E,0xA660,0xA662,0xA664,0xA666,0xA668,0xA66A,0xA66C,0xA680,0xA682,0xA684,0xA686,0xA688,0xA68A,0xA68C,0xA68E,0xA690,0xA692,0xA694,0xA696,0xA698,0xA69A,0xA722,0xA724,0xA726,0xA728,0xA72A,0xA72C,0xA72E,0xA732,0xA734,0xA736,0xA738,0xA73A,0xA73C,0xA73E,0xA740,0xA742,0xA744,0xA746,0xA748,0xA74A,0xA74C,0xA74E,0xA750,0xA752,0xA754,0xA756,0xA758,0xA75A,0xA75C,0xA75E,0xA760,0xA762,0xA764,0xA766,0xA768,0xA76A,0xA76C,0xA76E,0xA779,0xA77B,0xA77D,0xA77E,0xA780,0xA782,0xA784,0xA786,0xA78B,0xA78D,0xA790,0xA792,0xA796,0xA798,0xA79A,0xA79C,0xA79E,0xA7A0,0xA7A2,0xA7A4,0xA7A6,0xA7A8,0xA7AA,0xA7AB,0xA7AC,0xA7AD,0xA7AE,0xA7B0,0xA7B1,0xA7B2,0xA7B3,0xA7B4,0xA7B6,0xA7B8,0xA7BA,0xA7BC,0xA7BE,0xA7C0,0xA7C2,0xA7C4,0xA7C5,0xA7C6,0xA7C7,0xA7C9,0xA7CB,0xA7CC,0xA7D0,0xA7D6,0xA7D8,0xA7DA,0xA7DC,0xA7F5,0xFF21,0xFF22,0xFF23,0xFF24,0xFF25,0xFF26,0xFF27,0xFF28,0xFF29,0xFF2A,0xFF2B,0xFF2C,0xFF2D,0xFF2E,0xFF2F,0xFF30,0xFF31,0xFF32,0xFF33,0xFF34,0xFF35,0xFF36,0xFF37,0xFF38,0xFF39,0xFF3A};

size_t unicode_len_Lu = sizeof(unicode_Lu)/sizeof(unicode_Lu[0]);

int unicode_Lt[] = {0x01C5,0x01C8,0x01CB,0x01F2,0x1F88,0x1F89,0x1F8A,0x1F8B,0x1F8C,0x1F8D,0x1F8E,0x1F8F,0x1F98,0x1F99,0x1F9A,0x1F9B,0x1F9C,0x1F9D,0x1F9E,0x1F9F,0x1FA8,0x1FA9,0x1FAA,0x1FAB,0x1FAC,0x1FAD,0x1FAE,0x1FAF,0x1FBC,0x1FCC,0x1FFC};
size_t unicode_len_Lt = sizeof(unicode_Lt)/sizeof(unicode_Lt[0]);

int unicode_LC[] = {-1};
size_t unicode_len_LC = sizeof(unicode_LC)/sizeof(unicode_LC[0]);

int unicode_Lm[] = {0x02B0,0x02B1,0x02B2,0x02B3,0x02B4,0x02B5,0x02B6,0x02B7,0x02B8,0x02B9,0x02BA,0x02BB,0x02BC,0x02BD,0x02BE,0x02BF,0x02C0,0x02C1,0x02C6,0x02C7,0x02C8,0x02C9,0x02CA,0x02CB,0x02CC,0x02CD,0x02CE,0x02CF,0x02D0,0x02D1,0x02E0,0x02E1,0x02E2,0x02E3,0x02E4,0x02EC,0x02EE,0x0374,0x037A,0x0559,0x0640,0x06E5,0x06E6,0x07F4,0x07F5,0x07FA,0x081A,0x0824,0x0828,0x08C9,0x0971,0x0E46,0x0EC6,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1078,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x1079,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107A,0x107B,0x107B,0x107B,0x107B,0x107B,0x107B,0x107B,0x107B,0x107B,0x107B,0x10D4,0x10D6,0x10FC,0x16B4,0x16B4,0x16B4,0x16B4,0x16D4,0x16D4,0x16D4,0x16D6,0x16D6,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16F9,0x16FE,0x16FE,0x16FE,0x17D7,0x1843,0x1AA7,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1AFF,0x1C78,0x1C79,0x1C7A,0x1C7B,0x1C7C,0x1C7D,0x1D2C,0x1D2D,0x1D2E,0x1D2F,0x1D30,0x1D31,0x1D32,0x1D33,0x1D34,0x1D35,0x1D36,0x1D37,0x1D38,0x1D39,0x1D3A,0x1D3B,0x1D3C,0x1D3D,0x1D3E,0x1D3F,0x1D40,0x1D41,0x1D42,0x1D43,0x1D44,0x1D45,0x1D46,0x1D47,0x1D48,0x1D49,0x1D4A,0x1D4B,0x1D4C,0x1D4D,0x1D4E,0x1D4F,0x1D50,0x1D51,0x1D52,0x1D53,0x1D54,0x1D55,0x1D56,0x1D57,0x1D58,0x1D59,0x1D5A,0x1D5B,0x1D5C,0x1D5D,0x1D5E,0x1D5F,0x1D60,0x1D61,0x1D62,0x1D63,0x1D64,0x1D65,0x1D66,0x1D67,0x1D68,0x1D69,0x1D6A,0x1D78,0x1D9B,0x1D9C,0x1D9D,0x1D9E,0x1D9F,0x1DA0,0x1DA1,0x1DA2,0x1DA3,0x1DA4,0x1DA5,0x1DA6,0x1DA7,0x1DA8,0x1DA9,0x1DAA,0x1DAB,0x1DAC,0x1DAD,0x1DAE,0x1DAF,0x1DB0,0x1DB1,0x1DB2,0x1DB3,0x1DB4,0x1DB5,0x1DB6,0x1DB7,0x1DB8,0x1DB9,0x1DBA,0x1DBB,0x1DBC,0x1DBD,0x1DBE,0x1DBF,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E03,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E04,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E05,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E06,0x1E13,0x1E13,0x1E13,0x1E13,0x1E13,0x1E13,0x1E13,0x1E4E,0x1E94,0x2071,0x207F,0x2090,0x2091,0x2092,0x2093,0x2094,0x2095,0x2096,0x2097,0x2098,0x2099,0x209A,0x209B,0x209C,0x2C7C,0x2C7D,0x2D6F,0x2E2F,0x3005,0x3031,0x3032,0x3033,0x3034,0x3035,0x303B,0x309D,0x309E,0x30FC,0x30FD,0x30FE,0xA015,0xA4F8,0xA4F9,0xA4FA,0xA4FB,0xA4FC,0xA4FD,0xA60C,0xA67F,0xA69C,0xA69D,0xA717,0xA718,0xA719,0xA71A,0xA71B,0xA71C,0xA71D,0xA71E,0xA71F,0xA770,0xA788,0xA7F2,0xA7F3,0xA7F4,0xA7F8,0xA7F9,0xA9CF,0xA9E6,0xAA70,0xAADD,0xAAF3,0xAAF4,0xAB5C,0xAB5D,0xAB5E,0xAB5F,0xAB69,0xFF70,0xFF9E,0xFF9F};

size_t unicode_len_Lm = sizeof(unicode_Lm)/sizeof(unicode_Lm[0]);

int unicode_Lo[] = {0x00AA,0x00BA,0x01BB,0x01C0,0x01C1,0x01C2,0x01C3,0x0294,0x05D0,0x05D1,0x05D2,0x05D3,0x05D4,0x05D5,0x05D6,0x05D7,0x05D8,0x05D9,0x05DA,0x05DB,0x05DC,0x05DD,0x05DE,0x05DF,0x05E0,0x05E1,0x05E2,0x05E3,0x05E4,0x05E5,0x05E6,0x05E7,0x05E8,0x05E9,0x05EA,0x05EF,0x05F0,0x05F1,0x05F2,0x0620,0x0621,0x0622,0x0623,0x0624,0x0625,0x0626,0x0627,0x0628,0x0629,0x062A,0x062B,0x062C,0x062D,0x062E,0x062F,0x0630,0x0631,0x0632,0x0633,0x0634,0x0635,0x0636,0x0637,0x0638,0x0639,0x063A,0x063B,0x063C,0x063D,0x063E,0x063F,0x0641,0x0642,0x0643,0x0644,0x0645,0x0646,0x0647,0x0648,0x0649,0x064A,0x066E,0x066F,0x0671,0x0672,0x0673,0x0674,0x0675,0x0676,0x0677,0x0678,0x0679,0x067A,0x067B,0x067C,0x067D,0x067E,0x067F,0x0680,0x0681,0x0682,0x0683,0x0684,0x0685,0x0686,0x0687,0x0688,0x0689,0x068A,0x068B,0x068C,0x068D,0x068E,0x068F,0x0690,0x0691,0x0692,0x0693,0x0694,0x0695,0x0696,0x0697,0x0698,0x0699,0x069A,0x069B,0x069C,0x069D,0x069E,0x069F,0x06A0,0x06A1,0x06A2,0x06A3,0x06A4,0x06A5,0x06A6,0x06A7,0x06A8,0x06A9,0x06AA,0x06AB,0x06AC,0x06AD,0x06AE,0x06AF,0x06B0,0x06B1,0x06B2,0x06B3,0x06B4,0x06B5,0x06B6,0x06B7,0x06B8,0x06B9,0x06BA,0x06BB,0x06BC,0x06BD,0x06BE,0x06BF,0x06C0,0x06C1,0x06C2,0x06C3,0x06C4,0x06C5,0x06C6,0x06C7,0x06C8,0x06C9,0x06CA,0x06CB,0x06CC,0x06CD,0x06CE,0x06CF,0x06D0,0x06D1,0x06D2,0x06D3,0x06D5,0x06EE,0x06EF,0x06FA,0x06FB,0x06FC,0x06FF,0x0710,0x0712,0x0713,0x0714,0x0715,0x0716,0x0717,0x0718,0x0719,0x071A,0x071B,0x071C,0x071D,0x071E,0x071F,0x0720,0x0721,0x0722,0x0723,0x0724,0x0725,0x0726,0x0727,0x0728,0x0729,0x072A,0x072B,0x072C,0x072D,0x072E,0x072F,0x074D,0x074E,0x074F,0x0750,0x0751,0x0752,0x0753,0x0754,0x0755,0x0756,0x0757,0x0758,0x0759,0x075A,0x075B,0x075C,0x075D,0x075E,0x075F,0x0760,0x0761,0x0762,0x0763,0x0764,0x0765,0x0766,0x0767,0x0768,0x0769,0x076A,0x076B,0x076C,0x076D,0x076E,0x076F,0x0770,0x0771,0x0772,0x0773,0x0774,0x0775,0x0776,0x0777,0x0778,0x0779,0x077A,0x077B,0x077C,0x077D,0x077E,0x077F,0x0780,0x0781,0x0782,0x0783,0x0784,0x0785,0x0786,0x0787,0x0788,0x0789,0x078A,0x078B,0x078C,0x078D,0x078E,0x078F,0x0790,0x0791,0x0792,0x0793,0x0794,0x0795,0x0796,0x0797,0x0798,0x0799,0x079A,0x079B,0x079C,0x079D,0x079E,0x079F,0x07A0,0x07A1,0x07A2,0x07A3,0x07A4,0x07A5,0x07B1,0x07CA,0x07CB,0x07CC,0x07CD,0x07CE,0x07CF,0x07D0,0x07D1,0x07D2,0x07D3,0x07D4,0x07D5,0x07D6,0x07D7,0x07D8,0x07D9,0x07DA,0x07DB,0x07DC,0x07DD,0x07DE,0x07DF,0x07E0,0x07E1,0x07E2,0x07E3,0x07E4,0x07E5,0x07E6,0x07E7,0x07E8,0x07E9,0x07EA,0x0800,0x0801,0x0802,0x0803,0x0804,0x0805,0x0806,0x0807,0x0808,0x0809,0x080A,0x080B,0x080C,0x080D,0x080E,0x080F,0x0810,0x0811,0x0812,0x0813,0x0814,0x0815,0x0840,0x0841,0x0842,0x0843,0x0844,0x0845,0x0846,0x0847,0x0848,0x0849,0x084A,0x084B,0x084C,0x084D,0x084E,0x084F,0x0850,0x0851,0x0852,0x0853,0x0854,0x0855,0x0856,0x0857,0x0858,0x0860,0x0861,0x0862,0x0863,0x0864,0x0865,0x0866,0x0867,0x0868,0x0869,0x086A,0x0870,0x0871,0x0872,0x0873,0x0874,0x0875,0x0876,0x0877,0x0878,0x0879,0x087A,0x087B,0x087C,0x087D,0x087E,0x087F,0x0880,0x0881,0x0882,0x0883,0x0884,0x0885,0x0886,0x0887,0x0889,0x088A,0x088B,0x088C,0x088D,0x088E,0x08A0,0x08A1,0x08A2,0x08A3,0x08A4,0x08A5,0x08A6,0x08A7,0x08A8,0x08A9,0x08AA,0x08AB,0x08AC,0x08AD,0x08AE,0x08AF,0x08B0,0x08B1,0x08B2,0x08B3,0x08B4,0x08B5,0x08B6,0x08B7,0x08B8,0x08B9,0x08BA,0x08BB,0x08BC,0x08BD,0x08BE,0x08BF,0x08C0,0x08C1,0x08C2,0x08C3,0x08C4,0x08C5,0x08C6,0x08C7,0x08C8,0x0904,0x0905,0x0906,0x0907,0x0908,0x0909,0x090A,0x090B,0x090C,0x090D,0x090E,0x090F,0x0910,0x0911,0x0912,0x0913,0x0914,0x0915,0x0916,0x0917,0x0918,0x0919,0x091A,0x091B,0x091C,0x091D,0x091E,0x091F,0x0920,0x0921,0x0922,0x0923,0x0924,0x0925,0x0926,0x0927,0x0928,0x0929,0x092A,0x092B,0x092C,0x092D,0x092E,0x092F,0x0930,0x0931,0x0932,0x0933,0x0934,0x0935,0x0936,0x0937,0x0938,0x0939,0x093D,0x0950,0x0958,0x0959,0x095A,0x095B,0x095C,0x095D,0x095E,0x095F,0x0960,0x0961,0x0972,0x0973,0x0974,0x0975,0x0976,0x0977,0x0978,0x0979,0x097A,0x097B,0x097C,0x097D,0x097E,0x097F,0x0980,0x0985,0x0986,0x0987,0x0988,0x0989,0x098A,0x098B,0x098C,0x098F,0x0990,0x0993,0x0994,0x0995,0x0996,0x0997,0x0998,0x0999,0x099A,0x099B,0x099C,0x099D,0x099E,0x099F,0x09A0,0x09A1,0x09A2,0x09A3,0x09A4,0x09A5,0x09A6,0x09A7,0x09A8,0x09AA,0x09AB,0x09AC,0x09AD,0x09AE,0x09AF,0x09B0,0x09B2,0x09B6,0x09B7,0x09B8,0x09B9,0x09BD,0x09CE,0x09DC,0x09DD,0x09DF,0x09E0,0x09E1,0x09F0,0x09F1,0x09FC,0x0A05,0x0A06,0x0A07,0x0A08,0x0A09,0x0A0A,0x0A0F,0x0A10,0x0A13,0x0A14,0x0A15,0x0A16,0x0A17,0x0A18,0x0A19,0x0A1A,0x0A1B,0x0A1C,0x0A1D,0x0A1E,0x0A1F,0x0A20,0x0A21,0x0A22,0x0A23,0x0A24,0x0A25,0x0A26,0x0A27,0x0A28,0x0A2A,0x0A2B,0x0A2C,0x0A2D,0x0A2E,0x0A2F,0x0A30,0x0A32,0x0A33,0x0A35,0x0A36,0x0A38,0x0A39,0x0A59,0x0A5A,0x0A5B,0x0A5C,0x0A5E,0x0A72,0x0A73,0x0A74,0x0A85,0x0A86,0x0A87,0x0A88,0x0A89,0x0A8A,0x0A8B,0x0A8C,0x0A8D,0x0A8F,0x0A90,0x0A91,0x0A93,0x0A94,0x0A95,0x0A96,0x0A97,0x0A98,0x0A99,0x0A9A,0x0A9B,0x0A9C,0x0A9D,0x0A9E,0x0A9F,0x0AA0,0x0AA1,0x0AA2,0x0AA3,0x0AA4,0x0AA5,0x0AA6,0x0AA7,0x0AA8,0x0AAA,0x0AAB,0x0AAC,0x0AAD,0x0AAE,0x0AAF,0x0AB0,0x0AB2,0x0AB3,0x0AB5,0x0AB6,0x0AB7,0x0AB8,0x0AB9,0x0ABD,0x0AD0,0x0AE0,0x0AE1,0x0AF9,0x0B05,0x0B06,0x0B07,0x0B08,0x0B09,0x0B0A,0x0B0B,0x0B0C,0x0B0F,0x0B10,0x0B13,0x0B14,0x0B15,0x0B16,0x0B17,0x0B18,0x0B19,0x0B1A,0x0B1B,0x0B1C,0x0B1D,0x0B1E,0x0B1F,0x0B20,0x0B21,0x0B22,0x0B23,0x0B24,0x0B25,0x0B26,0x0B27,0x0B28,0x0B2A,0x0B2B,0x0B2C,0x0B2D,0x0B2E,0x0B2F,0x0B30,0x0B32,0x0B33,0x0B35,0x0B36,0x0B37,0x0B38,0x0B39,0x0B3D,0x0B5C,0x0B5D,0x0B5F,0x0B60,0x0B61,0x0B71,0x0B83,0x0B85,0x0B86,0x0B87,0x0B88,0x0B89,0x0B8A,0x0B8E,0x0B8F,0x0B90,0x0B92,0x0B93,0x0B94,0x0B95,0x0B99,0x0B9A,0x0B9C,0x0B9E,0x0B9F,0x0BA3,0x0BA4,0x0BA8,0x0BA9,0x0BAA,0x0BAE,0x0BAF,0x0BB0,0x0BB1,0x0BB2,0x0BB3,0x0BB4,0x0BB5,0x0BB6,0x0BB7,0x0BB8,0x0BB9,0x0BD0,0x0C05,0x0C06,0x0C07,0x0C08,0x0C09,0x0C0A,0x0C0B,0x0C0C,0x0C0E,0x0C0F,0x0C10,0x0C12,0x0C13,0x0C14,0x0C15,0x0C16,0x0C17,0x0C18,0x0C19,0x0C1A,0x0C1B,0x0C1C,0x0C1D,0x0C1E,0x0C1F,0x0C20,0x0C21,0x0C22,0x0C23,0x0C24,0x0C25,0x0C26,0x0C27,0x0C28,0x0C2A,0x0C2B,0x0C2C,0x0C2D,0x0C2E,0x0C2F,0x0C30,0x0C31,0x0C32,0x0C33,0x0C34,0x0C35,0x0C36,0x0C37,0x0C38,0x0C39,0x0C3D,0x0C58,0x0C59,0x0C5A,0x0C5D,0x0C60,0x0C61,0x0C80,0x0C85,0x0C86,0x0C87,0x0C88,0x0C89,0x0C8A,0x0C8B,0x0C8C,0x0C8E,0x0C8F,0x0C90,0x0C92,0x0C93,0x0C94,0x0C95,0x0C96,0x0C97,0x0C98,0x0C99,0x0C9A,0x0C9B,0x0C9C,0x0C9D,0x0C9E,0x0C9F,0x0CA0,0x0CA1,0x0CA2,0x0CA3,0x0CA4,0x0CA5,0x0CA6,0x0CA7,0x0CA8,0x0CAA,0x0CAB,0x0CAC,0x0CAD,0x0CAE,0x0CAF,0x0CB0,0x0CB1,0x0CB2,0x0CB3,0x0CB5,0x0CB6,0x0CB7,0x0CB8,0x0CB9,0x0CBD,0x0CDD,0x0CDE,0x0CE0,0x0CE1,0x0CF1,0x0CF2,0x0D04,0x0D05,0x0D06,0x0D07,0x0D08,0x0D09,0x0D0A,0x0D0B,0x0D0C,0x0D0E,0x0D0F,0x0D10,0x0D12,0x0D13,0x0D14,0x0D15,0x0D16,0x0D17,0x0D18,0x0D19,0x0D1A,0x0D1B,0x0D1C,0x0D1D,0x0D1E,0x0D1F,0x0D20,0x0D21,0x0D22,0x0D23,0x0D24,0x0D25,0x0D26,0x0D27,0x0D28,0x0D29,0x0D2A,0x0D2B,0x0D2C,0x0D2D,0x0D2E,0x0D2F,0x0D30,0x0D31,0x0D32,0x0D33,0x0D34,0x0D35,0x0D36,0x0D37,0x0D38,0x0D39,0x0D3A,0x0D3D,0x0D4E,0x0D54,0x0D55,0x0D56,0x0D5F,0x0D60,0x0D61,0x0D7A,0x0D7B,0x0D7C,0x0D7D,0x0D7E,0x0D7F,0x0D85,0x0D86,0x0D87,0x0D88,0x0D89,0x0D8A,0x0D8B,0x0D8C,0x0D8D,0x0D8E,0x0D8F,0x0D90,0x0D91,0x0D92,0x0D93,0x0D94,0x0D95,0x0D96,0x0D9A,0x0D9B,0x0D9C,0x0D9D,0x0D9E,0x0D9F,0x0DA0,0x0DA1,0x0DA2,0x0DA3,0x0DA4,0x0DA5,0x0DA6,0x0DA7,0x0DA8,0x0DA9,0x0DAA,0x0DAB,0x0DAC,0x0DAD,0x0DAE,0x0DAF,0x0DB0,0x0DB1,0x0DB3,0x0DB4,0x0DB5,0x0DB6,0x0DB7,0x0DB8,0x0DB9,0x0DBA,0x0DBB,0x0DBD,0x0DC0,0x0DC1,0x0DC2,0x0DC3,0x0DC4,0x0DC5,0x0DC6,0x0E01,0x0E02,0x0E03,0x0E04,0x0E05,0x0E06,0x0E07,0x0E08,0x0E09,0x0E0A,0x0E0B,0x0E0C,0x0E0D,0x0E0E,0x0E0F,0x0E10,0x0E11,0x0E12,0x0E13,0x0E14,0x0E15,0x0E16,0x0E17,0x0E18,0x0E19,0x0E1A,0x0E1B,0x0E1C,0x0E1D,0x0E1E,0x0E1F,0x0E20,0x0E21,0x0E22,0x0E23,0x0E24,0x0E25,0x0E26,0x0E27,0x0E28,0x0E29,0x0E2A,0x0E2B,0x0E2C,0x0E2D,0x0E2E,0x0E2F,0x0E30,0x0E32,0x0E33,0x0E40,0x0E41,0x0E42,0x0E43,0x0E44,0x0E45,0x0E81,0x0E82,0x0E84,0x0E86,0x0E87,0x0E88,0x0E89,0x0E8A,0x0E8C,0x0E8D,0x0E8E,0x0E8F,0x0E90,0x0E91,0x0E92,0x0E93,0x0E94,0x0E95,0x0E96,0x0E97,0x0E98,0x0E99,0x0E9A,0x0E9B,0x0E9C,0x0E9D,0x0E9E,0x0E9F,0x0EA0,0x0EA1,0x0EA2,0x0EA3,0x0EA5,0x0EA7,0x0EA8,0x0EA9,0x0EAA,0x0EAB,0x0EAC,0x0EAD,0x0EAE,0x0EAF,0x0EB0,0x0EB2,0x0EB3,0x0EBD,0x0EC0,0x0EC1,0x0EC2,0x0EC3,0x0EC4,0x0EDC,0x0EDD,0x0EDE,0x0EDF,0x0F00,0x0F40,0x0F41,0x0F42,0x0F43,0x0F44,0x0F45,0x0F46,0x0F47,0x0F49,0x0F4A,0x0F4B,0x0F4C,0x0F4D,0x0F4E,0x0F4F,0x0F50,0x0F51,0x0F52,0x0F53,0x0F54,0x0F55,0x0F56,0x0F57,0x0F58,0x0F59,0x0F5A,0x0F5B,0x0F5C,0x0F5D,0x0F5E,0x0F5F,0x0F60,0x0F61,0x0F62,0x0F63,0x0F64,0x0F65,0x0F66,0x0F67,0x0F68,0x0F69,0x0F6A,0x0F6B,0x0F6C,0x0F88,0x0F89,0x0F8A,0x0F8B,0x0F8C,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1000,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1001,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1002,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1003,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1004,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1005,0x1006,0x1007,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1008,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x1009,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100A,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100B,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100C,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100D,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100E,0x100F,0x100F,0x100F,0x100F,0x100F,0x100F,0x100F,0x100F,0x100F,0x100F,0x100F,0x100F,0x1010,0x1011,0x1012,0x1013,0x1014,0x1015,0x1016,0x1017,0x1018,0x1019,0x101A,0x101B,0x101C,0x101D,0x101E,0x101F,0x1020,0x1021,0x1022,0x1023,0x1024,0x1025,0x1026,0x1027,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1028,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x1029,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102A,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102B,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102C,0x102D,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1030,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1031,0x1032,0x1032,0x1032,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1033,0x1034,0x1034,0x1034,0x1034,0x1034,0x1034,0x1034,0x1034,0x1034,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1035,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1036,0x1037,0x1037,0x1037,0x1037,0x1037,0x1037,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1038,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x1039,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103A,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103B,0x103C,0x103C,0x103C,0x103C,0x103C,0x103C,0x103C,0x103C,0x103C,0x103C,0x103C,0x103C,0x103F,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1045,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1046,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1047,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1048,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1049,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1050,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1051,0x1052,0x1052,0x1052,0x1052,0x1052,0x1052,0x1052,0x1052,0x1052,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1053,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1054,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1055,0x1056,0x1056,0x1056,0x1056,0x105A,0x105B,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105C,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105D,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105E,0x105F,0x105F,0x105F,0x105F,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1060,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1061,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1062,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1063,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1064,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1065,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1066,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1067,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1068,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x1069,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106A,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106B,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106C,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106D,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106E,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x106F,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1070,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1071,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1072,0x1073,0x1073,0x1073,0x1073,0x1073,0x1073,0x1073,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1074,0x1075,0x1075,0x1075,0x1075,0x1075,0x1075,0x1075,0x1076,0x1076,0x1076,0x1076,0x1076,0x1076,0x1076,0x1076,0x1076,0x1077,0x1078,0x1079,0x107A,0x107B,0x107C,0x107D,0x107E,0x107F,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1080,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1081,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1082,0x1083,0x1083,0x1083,0x1083,0x1083,0x1083,0x1083,0x1083,0x1083,0x1083,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1084,0x1085,0x1085,0x1085,0x1085,0x1085,0x1085,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1086,0x1087,0x1087,0x1087,0x1087,0x1087,0x1087,0x1087,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1088,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x1089,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108E,0x108F,0x108F,0x108F,0x108F,0x108F,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1090,0x1091,0x1091,0x1091,0x1091,0x1091,0x1091,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1092,0x1093,0x1093,0x1093,0x1093,0x1093,0x1093,0x1093,0x1093,0x1093,0x1093,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1098,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x1099,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109A,0x109B,0x109B,0x109B,0x109B,0x109B,0x109B,0x109B,0x109B,0x109B,0x109B,0x10A0,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A1,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A2,0x10A3,0x10A3,0x10A3,0x10A3,0x10A3,0x10A3,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A6,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A7,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A8,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10A9,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AC,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AD,0x10AE,0x10AE,0x10AE,0x10AE,0x10AE,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B0,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B1,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B2,0x10B3,0x10B3,0x10B3,0x10B3,0x10B3,0x10B3,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B4,0x10B5,0x10B5,0x10B5,0x10B5,0x10B5,0x10B5,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B6,0x10B7,0x10B7,0x10B7,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B8,0x10B9,0x10B9,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C0,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C1,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C2,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C3,0x10C4,0x10C4,0x10C4,0x10C4,0x10C4,0x10C4,0x10C4,0x10C4,0x10C4,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D0,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D1,0x10D2,0x10D2,0x10D2,0x10D2,0x10D4,0x10D4,0x10D4,0x10D4,0x10D4,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E8,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10E9,0x10EA,0x10EA,0x10EA,0x10EA,0x10EA,0x10EA,0x10EA,0x10EA,0x10EA,0x10EA,0x10EB,0x10EB,0x10EC,0x10EC,0x10EC,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F0,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F1,0x10F2,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F3,0x10F4,0x10F4,0x10F4,0x10F4,0x10F4,0x10F4,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F7,0x10F8,0x10F8,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FB,0x10FC,0x10FC,0x10FC,0x10FC,0x10FC,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FE,0x10FF,0x10FF,0x10FF,0x10FF,0x10FF,0x10FF,0x10FF,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1100,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1101,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1102,0x1103,0x1103,0x1103,0x1103,0x1103,0x1103,0x1103,0x1103,0x1103,0x1104,0x1105,0x1106,0x1107,0x1107,0x1107,0x1107,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1108,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x1109,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110A,0x110B,0x110C,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110D,0x110E,0x110E,0x110E,0x110E,0x110E,0x110E,0x110E,0x110E,0x110E,0x110E,0x110F,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1110,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1111,0x1112,0x1112,0x1112,0x1112,0x1112,0x1112,0x1112,0x1112,0x1113,0x1114,0x1114,0x1114,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1115,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1116,0x1117,0x1117,0x1117,0x1117,0x1117,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1118,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x1119,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111A,0x111B,0x111B,0x111B,0x111B,0x111C,0x111C,0x111C,0x111C,0x111C,0x111D,0x111D,0x111D,0x111E,0x111F,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1120,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1121,0x1122,0x1122,0x1122,0x1122,0x1122,0x1122,0x1122,0x1122,0x1122,0x1122,0x1122,0x1122,0x1122,0x1123,0x1123,0x1124,0x1124,0x1125,0x1126,0x1127,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1128,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x1129,0x112A,0x112A,0x112A,0x112A,0x112A,0x112A,0x112A,0x112A,0x112A,0x112A,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112B,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112C,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112D,0x112E,0x112F,0x1130,0x1130,0x1130,0x1130,0x1130,0x1130,0x1130,0x1130,0x1130,0x1130,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1131,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1132,0x1133,0x1133,0x1133,0x1133,0x1133,0x1133,0x1133,0x1133,0x1133,0x1133,0x1134,0x1135,0x1135,0x1135,0x1135,0x1135,0x1136,0x1136,0x1136,0x1137,0x1138,0x1138,0x1138,0x1138,0x1138,0x1138,0x1138,0x1138,0x1138,0x1138,0x1138,0x1138,0x1138,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x1139,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113A,0x113B,0x113B,0x113B,0x113B,0x113B,0x113B,0x113B,0x113B,0x113C,0x113D,0x113D,0x113D,0x113E,0x113F,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1140,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1141,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1142,0x1143,0x1143,0x1143,0x1143,0x1143,0x1143,0x1144,0x1144,0x1144,0x1144,0x1144,0x1145,0x1145,0x1146,0x1146,0x1146,0x1147,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1148,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x1149,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114A,0x114B,0x114C,0x114C,0x114C,0x114C,0x114D,0x114E,0x114F,0x1150,0x1151,0x1152,0x1153,0x1154,0x1155,0x1156,0x1157,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1158,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x1159,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115A,0x115B,0x115C,0x115D,0x115D,0x115D,0x115D,0x115D,0x115E,0x115F,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1160,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1161,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1162,0x1163,0x1164,0x1164,0x1165,0x1166,0x1167,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1168,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x1169,0x116A,0x116A,0x116A,0x116A,0x116A,0x116A,0x116A,0x116A,0x116A,0x116A,0x116A,0x116A,0x116B,0x116B,0x116C,0x116D,0x116E,0x116F,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1170,0x1171,0x1171,0x1171,0x1171,0x1171,0x1171,0x1171,0x1171,0x1171,0x1171,0x1171,0x1171,0x1172,0x1173,0x1174,0x1174,0x1174,0x1174,0x1174,0x1174,0x1174,0x1174,0x1175,0x1176,0x1177,0x1178,0x1179,0x117A,0x117B,0x117C,0x117D,0x117E,0x117F,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1180,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1181,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1182,0x1183,0x1184,0x1185,0x1186,0x1187,0x1188,0x1189,0x118A,0x118B,0x118C,0x118D,0x118E,0x118F,0x118F,0x1190,0x1190,0x1190,0x1190,0x1190,0x1190,0x1190,0x1190,0x1190,0x1190,0x1190,0x1190,0x1190,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1191,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1192,0x1193,0x1193,0x1194,0x1194,0x1195,0x1196,0x1197,0x1198,0x1199,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119A,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119B,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119C,0x119D,0x119D,0x119E,0x119E,0x119E,0x119F,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A1,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A2,0x11A3,0x11A3,0x11A3,0x11A3,0x11A3,0x11A4,0x11A5,0x11A5,0x11A5,0x11A5,0x11A5,0x11A5,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A6,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A7,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A9,0x11A9,0x11AA,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AB,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AC,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AD,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AE,0x11AF,0x11AF,0x11AF,0x11AF,0x11AF,0x11AF,0x11AF,0x11AF,0x11AF,0x11AF,0x11B0,0x11B1,0x11B2,0x11B3,0x11B4,0x11B5,0x11B6,0x11B7,0x11B8,0x11B9,0x11BA,0x11BB,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BC,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BD,0x11BE,0x11BE,0x11BF,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C0,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C1,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C2,0x11C3,0x11C4,0x11C4,0x11C5,0x11C6,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C7,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C8,0x11C9,0x11CA,0x11CB,0x11CC,0x11CD,0x11CE,0x11CF,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D0,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D1,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D2,0x11D3,0x11D3,0x11D4,0x11D4,0x11D5,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D6,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D7,0x11D8,0x11D8,0x11D8,0x11D8,0x11D8,0x11D8,0x11D8,0x11D8,0x11D8,0x11D8,0x11D8,0x11D9,0x11D9,0x11DA,0x11DB,0x11DC,0x11DD,0x11DE,0x11DF,0x11E0,0x11E1,0x11E2,0x11E3,0x11E4,0x11E5,0x11E6,0x11E7,0x11E8,0x11E9,0x11EA,0x11EB,0x11EC,0x11ED,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EE,0x11EF,0x11EF,0x11EF,0x11EF,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F0,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F1,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F2,0x11F3,0x11F3,0x11F3,0x11F3,0x11F3,0x11F4,0x11F5,0x11F6,0x11F7,0x11F8,0x11F9,0x11FA,0x11FB,0x11FB,0x11FC,0x11FD,0x11FE,0x11FF,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1200,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1201,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1202,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1203,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1204,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1205,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1206,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1207,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1208,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x1209,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120A,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120B,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120C,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120D,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120E,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x120F,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1210,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1211,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1212,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1213,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1214,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1215,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1216,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1217,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1218,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x1219,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121A,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121B,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121C,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121D,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121E,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x121F,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1220,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1221,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1222,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1223,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1224,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1225,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1226,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1227,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1228,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x1229,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122A,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122B,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122C,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122D,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122E,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x122F,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1230,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1231,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1232,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1233,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1234,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1235,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1236,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1237,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1238,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x1239,0x123A,0x123B,0x123C,0x123D,0x123E,0x123F,0x1240,0x1241,0x1242,0x1243,0x1244,0x1245,0x1246,0x1247,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1248,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x1249,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124A,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124B,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124C,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124D,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124E,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x124F,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1250,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1251,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1252,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1253,0x1254,0x1254,0x1254,0x1254,0x1254,0x1255,0x1256,0x1258,0x125A,0x125B,0x125C,0x125D,0x1260,0x1261,0x1262,0x1263,0x1264,0x1265,0x1266,0x1267,0x1268,0x1269,0x126A,0x126B,0x126C,0x126D,0x126E,0x126F,0x1270,0x1271,0x1272,0x1273,0x1274,0x1275,0x1276,0x1277,0x1278,0x1279,0x127A,0x127B,0x127C,0x127D,0x127E,0x127F,0x1280,0x1281,0x1282,0x1283,0x1284,0x1285,0x1286,0x1287,0x1288,0x128A,0x128B,0x128C,0x128D,0x1290,0x1291,0x1292,0x1293,0x1294,0x1295,0x1296,0x1297,0x1298,0x1299,0x129A,0x129B,0x129C,0x129D,0x129E,0x129F,0x12A0,0x12A1,0x12A2,0x12A3,0x12A4,0x12A5,0x12A6,0x12A7,0x12A8,0x12A9,0x12AA,0x12AB,0x12AC,0x12AD,0x12AE,0x12AF,0x12B0,0x12B2,0x12B3,0x12B4,0x12B5,0x12B8,0x12B9,0x12BA,0x12BB,0x12BC,0x12BD,0x12BE,0x12C0,0x12C2,0x12C3,0x12C4,0x12C5,0x12C8,0x12C9,0x12CA,0x12CB,0x12CC,0x12CD,0x12CE,0x12CF,0x12D0,0x12D1,0x12D2,0x12D3,0x12D4,0x12D5,0x12D6,0x12D8,0x12D9,0x12DA,0x12DB,0x12DC,0x12DD,0x12DE,0x12DF,0x12E0,0x12E1,0x12E2,0x12E3,0x12E4,0x12E5,0x12E6,0x12E7,0x12E8,0x12E9,0x12EA,0x12EB,0x12EC,0x12ED,0x12EE,0x12EF,0x12F0,0x12F1,0x12F2,0x12F3,0x12F4,0x12F5,0x12F6,0x12F7,0x12F8,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12F9,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FA,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FB,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FC,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FD,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FE,0x12FF,0x12FF,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1300,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1301,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1302,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1303,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1304,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1305,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1306,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1307,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1308,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x1309,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130A,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130B,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130C,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130D,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130E,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x130F,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1310,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1311,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1312,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1313,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1314,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1315,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1316,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1317,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1318,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x1319,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131A,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131B,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131C,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131D,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131E,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x131F,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1320,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1321,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1322,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1323,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1324,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1325,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1326,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1327,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1328,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x1329,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132A,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132B,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132C,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132D,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132E,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x132F,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1330,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1331,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1332,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1333,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1334,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1335,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1336,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1337,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1338,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x1339,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133A,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133B,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133C,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133D,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133E,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x133F,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1340,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1341,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1342,0x1343,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1345,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1346,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1347,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1348,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x1349,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134A,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134B,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134C,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134D,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134E,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x134F,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1350,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1351,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1352,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1353,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1354,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1355,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1356,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1357,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1358,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x1359,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135A,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135B,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135C,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135D,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135E,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x135F,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1360,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1361,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1362,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1363,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1364,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1365,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1366,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1367,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1368,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x1369,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136A,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136B,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136C,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136D,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136E,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x136F,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1370,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1371,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1372,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1373,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1374,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1375,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1376,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1377,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1378,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x1379,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137A,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137B,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137C,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137D,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137E,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x137F,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1380,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1381,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1382,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1383,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1384,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1385,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1386,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1387,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1388,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x1389,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138A,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138B,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138C,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138D,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138E,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x138F,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1390,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1391,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1392,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1393,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1394,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1395,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1396,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1397,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1398,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x1399,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139A,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139B,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139C,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139D,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139E,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x139F,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A0,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A1,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A2,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A3,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A4,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A5,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A6,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A7,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A8,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13A9,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AA,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AB,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AC,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AD,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AE,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13AF,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B0,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B1,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B2,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B3,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B4,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B5,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B6,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B7,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B8,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13B9,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BA,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BB,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BC,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BD,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BE,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13BF,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C0,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C1,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C2,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C3,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C4,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C5,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C6,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C7,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C8,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13C9,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CA,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CB,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CC,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CD,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CE,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13CF,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D0,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D1,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D2,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D3,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D4,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D5,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D6,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D7,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D8,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13D9,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DA,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DB,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DC,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DD,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DE,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13DF,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E0,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E1,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E2,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E3,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E4,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E5,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E6,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E7,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E8,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13E9,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EA,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EB,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13EC,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13ED,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EE,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13EF,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F0,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F1,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F2,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F3,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F4,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F5,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F6,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F7,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F8,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13F9,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FA,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FB,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FC,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FD,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FE,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x13FF,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1400,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1401,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1402,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1403,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1404,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1405,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1406,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1407,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1408,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x1409,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140A,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140B,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140C,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140D,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140E,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x140F,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1410,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1411,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1412,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1413,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1414,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1415,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1416,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1417,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1418,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x1419,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141A,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141B,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141C,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141D,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141E,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x141F,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1420,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1421,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1422,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1423,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1424,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1425,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1426,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1427,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1428,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x1429,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142A,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142B,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142C,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142D,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142E,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x142F,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1430,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1431,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1432,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1433,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1434,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1435,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1436,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1437,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1438,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x1439,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143A,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143B,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143C,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143D,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143E,0x143F,0x143F,0x143F,0x143F,0x143F,0x143F,0x143F,0x143F,0x143F,0x143F,0x143F,0x143F,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1440,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1441,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1442,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1443,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1444,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1445,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1446,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1447,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1448,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x1449,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144A,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144B,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144C,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144D,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144E,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x144F,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1450,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1451,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1452,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1453,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1454,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1455,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1456,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1457,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1458,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x1459,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145A,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145B,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145C,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145D,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145E,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x145F,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1460,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1461,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1462,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1463,0x1464,0x1464,0x1464,0x1464,0x1464,0x1464,0x1464,0x1464,0x1465,0x1466,0x1467,0x1468,0x1469,0x146A,0x146B,0x146C,0x146D,0x146E,0x146F,0x1470,0x1471,0x1472,0x1473,0x1474,0x1475,0x1476,0x1477,0x1478,0x1479,0x147A,0x147B,0x147C,0x147D,0x147E,0x147F,0x1480,0x1481,0x1482,0x1483,0x1484,0x1485,0x1486,0x1487,0x1488,0x1489,0x148A,0x148B,0x148C,0x148D,0x148E,0x148F,0x1490,0x1491,0x1492,0x1493,0x1494,0x1495,0x1496,0x1497,0x1498,0x1499,0x149A,0x149B,0x149C,0x149D,0x149E,0x149F,0x14A0,0x14A1,0x14A2,0x14A3,0x14A4,0x14A5,0x14A6,0x14A7,0x14A8,0x14A9,0x14AA,0x14AB,0x14AC,0x14AD,0x14AE,0x14AF,0x14B0,0x14B1,0x14B2,0x14B3,0x14B4,0x14B5,0x14B6,0x14B7,0x14B8,0x14B9,0x14BA,0x14BB,0x14BC,0x14BD,0x14BE,0x14BF,0x14C0,0x14C1,0x14C2,0x14C3,0x14C4,0x14C5,0x14C6,0x14C7,0x14C8,0x14C9,0x14CA,0x14CB,0x14CC,0x14CD,0x14CE,0x14CF,0x14D0,0x14D1,0x14D2,0x14D3,0x14D4,0x14D5,0x14D6,0x14D7,0x14D8,0x14D9,0x14DA,0x14DB,0x14DC,0x14DD,0x14DE,0x14DF,0x14E0,0x14E1,0x14E2,0x14E3,0x14E4,0x14E5,0x14E6,0x14E7,0x14E8,0x14E9,0x14EA,0x14EB,0x14EC,0x14ED,0x14EE,0x14EF,0x14F0,0x14F1,0x14F2,0x14F3,0x14F4,0x14F5,0x14F6,0x14F7,0x14F8,0x14F9,0x14FA,0x14FB,0x14FC,0x14FD,0x14FE,0x14FF,0x1500,0x1501,0x1502,0x1503,0x1504,0x1505,0x1506,0x1507,0x1508,0x1509,0x150A,0x150B,0x150C,0x150D,0x150E,0x150F,0x1510,0x1511,0x1512,0x1513,0x1514,0x1515,0x1516,0x1517,0x1518,0x1519,0x151A,0x151B,0x151C,0x151D,0x151E,0x151F,0x1520,0x1521,0x1522,0x1523,0x1524,0x1525,0x1526,0x1527,0x1528,0x1529,0x152A,0x152B,0x152C,0x152D,0x152E,0x152F,0x1530,0x1531,0x1532,0x1533,0x1534,0x1535,0x1536,0x1537,0x1538,0x1539,0x153A,0x153B,0x153C,0x153D,0x153E,0x153F,0x1540,0x1541,0x1542,0x1543,0x1544,0x1545,0x1546,0x1547,0x1548,0x1549,0x154A,0x154B,0x154C,0x154D,0x154E,0x154F,0x1550,0x1551,0x1552,0x1553,0x1554,0x1555,0x1556,0x1557,0x1558,0x1559,0x155A,0x155B,0x155C,0x155D,0x155E,0x155F,0x1560,0x1561,0x1562,0x1563,0x1564,0x1565,0x1566,0x1567,0x1568,0x1569,0x156A,0x156B,0x156C,0x156D,0x156E,0x156F,0x1570,0x1571,0x1572,0x1573,0x1574,0x1575,0x1576,0x1577,0x1578,0x1579,0x157A,0x157B,0x157C,0x157D,0x157E,0x157F,0x1580,0x1581,0x1582,0x1583,0x1584,0x1585,0x1586,0x1587,0x1588,0x1589,0x158A,0x158B,0x158C,0x158D,0x158E,0x158F,0x1590,0x1591,0x1592,0x1593,0x1594,0x1595,0x1596,0x1597,0x1598,0x1599,0x159A,0x159B,0x159C,0x159D,0x159E,0x159F,0x15A0,0x15A1,0x15A2,0x15A3,0x15A4,0x15A5,0x15A6,0x15A7,0x15A8,0x15A9,0x15AA,0x15AB,0x15AC,0x15AD,0x15AE,0x15AF,0x15B0,0x15B1,0x15B2,0x15B3,0x15B4,0x15B5,0x15B6,0x15B7,0x15B8,0x15B9,0x15BA,0x15BB,0x15BC,0x15BD,0x15BE,0x15BF,0x15C0,0x15C1,0x15C2,0x15C3,0x15C4,0x15C5,0x15C6,0x15C7,0x15C8,0x15C9,0x15CA,0x15CB,0x15CC,0x15CD,0x15CE,0x15CF,0x15D0,0x15D1,0x15D2,0x15D3,0x15D4,0x15D5,0x15D6,0x15D7,0x15D8,0x15D9,0x15DA,0x15DB,0x15DC,0x15DD,0x15DE,0x15DF,0x15E0,0x15E1,0x15E2,0x15E3,0x15E4,0x15E5,0x15E6,0x15E7,0x15E8,0x15E9,0x15EA,0x15EB,0x15EC,0x15ED,0x15EE,0x15EF,0x15F0,0x15F1,0x15F2,0x15F3,0x15F4,0x15F5,0x15F6,0x15F7,0x15F8,0x15F9,0x15FA,0x15FB,0x15FC,0x15FD,0x15FE,0x15FF,0x1600,0x1601,0x1602,0x1603,0x1604,0x1605,0x1606,0x1607,0x1608,0x1609,0x160A,0x160B,0x160C,0x160D,0x160E,0x160F,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1610,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1611,0x1612,0x1613,0x1614,0x1615,0x1616,0x1617,0x1618,0x1619,0x161A,0x161B,0x161C,0x161D,0x161E,0x161F,0x1620,0x1621,0x1622,0x1623,0x1624,0x1625,0x1626,0x1627,0x1628,0x1629,0x162A,0x162B,0x162C,0x162D,0x162E,0x162F,0x1630,0x1631,0x1632,0x1633,0x1634,0x1635,0x1636,0x1637,0x1638,0x1639,0x163A,0x163B,0x163C,0x163D,0x163E,0x163F,0x1640,0x1641,0x1642,0x1643,0x1644,0x1645,0x1646,0x1647,0x1648,0x1649,0x164A,0x164B,0x164C,0x164D,0x164E,0x164F,0x1650,0x1651,0x1652,0x1653,0x1654,0x1655,0x1656,0x1657,0x1658,0x1659,0x165A,0x165B,0x165C,0x165D,0x165E,0x165F,0x1660,0x1661,0x1662,0x1663,0x1664,0x1665,0x1666,0x1667,0x1668,0x1669,0x166A,0x166B,0x166C,0x166F,0x1670,0x1671,0x1672,0x1673,0x1674,0x1675,0x1676,0x1677,0x1678,0x1679,0x167A,0x167B,0x167C,0x167D,0x167E,0x167F,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1680,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1681,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1682,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1683,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1684,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1685,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1686,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1687,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1688,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x1689,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168A,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168B,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168C,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168D,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168E,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x168F,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1690,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1691,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1692,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1693,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1694,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1695,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1696,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1697,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1698,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x1699,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169A,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169B,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169C,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169D,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169E,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x169F,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A0,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A1,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A2,0x16A3,0x16A3,0x16A3,0x16A3,0x16A3,0x16A3,0x16A3,0x16A3,0x16A3,0x16A3,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A4,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A5,0x16A6,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A7,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A8,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16A9,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AA,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AB,0x16AC,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AD,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AE,0x16AF,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B0,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B1,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B2,0x16B3,0x16B4,0x16B5,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B6,0x16B7,0x16B7,0x16B7,0x16B7,0x16B7,0x16B7,0x16B7,0x16B7,0x16B7,0x16B7,0x16B7,0x16B7,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B8,0x16B9,0x16BA,0x16BB,0x16BC,0x16BD,0x16BE,0x16BF,0x16C0,0x16C1,0x16C2,0x16C3,0x16C4,0x16C5,0x16C6,0x16C7,0x16C8,0x16C9,0x16CA,0x16CB,0x16CC,0x16CD,0x16CE,0x16CF,0x16D0,0x16D1,0x16D2,0x16D3,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D4,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D5,0x16D6,0x16D6,0x16D6,0x16D6,0x16D6,0x16D6,0x16D6,0x16D6,0x16D6,0x16D6,0x16D6,0x16D6,0x16D7,0x16D8,0x16D9,0x16DA,0x16DB,0x16DC,0x16DD,0x16DE,0x16DF,0x16E0,0x16E1,0x16E2,0x16E3,0x16E4,0x16E5,0x16E6,0x16E7,0x16E8,0x16E9,0x16EA,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F0,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F1,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F2,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F3,0x16F4,0x16F4,0x16F4,0x16F4,0x16F4,0x16F4,0x16F4,0x16F4,0x16F4,0x16F4,0x16F4,0x16F4,0x16F5,0x16F5,0x16F6,0x16F7,0x16F8,0x1700,0x1700,0x1701,0x1702,0x1703,0x1704,0x1705,0x1706,0x1707,0x1708,0x1709,0x170A,0x170B,0x170C,0x170D,0x170E,0x170F,0x1710,0x1711,0x171F,0x1720,0x1721,0x1722,0x1723,0x1724,0x1725,0x1726,0x1727,0x1728,0x1729,0x172A,0x172B,0x172C,0x172D,0x172E,0x172F,0x1730,0x1731,0x1740,0x1741,0x1742,0x1743,0x1744,0x1745,0x1746,0x1747,0x1748,0x1749,0x174A,0x174B,0x174C,0x174D,0x174E,0x174F,0x1750,0x1751,0x1760,0x1761,0x1762,0x1763,0x1764,0x1765,0x1766,0x1767,0x1768,0x1769,0x176A,0x176B,0x176C,0x176E,0x176F,0x1770,0x1780,0x1781,0x1782,0x1783,0x1784,0x1785,0x1786,0x1787,0x1788,0x1789,0x178A,0x178B,0x178C,0x178D,0x178E,0x178F,0x1790,0x1791,0x1792,0x1793,0x1794,0x1795,0x1796,0x1797,0x1798,0x1799,0x179A,0x179B,0x179C,0x179D,0x179E,0x179F,0x17A0,0x17A1,0x17A2,0x17A3,0x17A4,0x17A5,0x17A6,0x17A7,0x17A8,0x17A9,0x17AA,0x17AB,0x17AC,0x17AD,0x17AE,0x17AF,0x17B0,0x17B1,0x17B2,0x17B3,0x17DC,0x1820,0x1821,0x1822,0x1823,0x1824,0x1825,0x1826,0x1827,0x1828,0x1829,0x182A,0x182B,0x182C,0x182D,0x182E,0x182F,0x1830,0x1831,0x1832,0x1833,0x1834,0x1835,0x1836,0x1837,0x1838,0x1839,0x183A,0x183B,0x183C,0x183D,0x183E,0x183F,0x1840,0x1841,0x1842,0x1844,0x1845,0x1846,0x1847,0x1848,0x1849,0x184A,0x184B,0x184C,0x184D,0x184E,0x184F,0x1850,0x1851,0x1852,0x1853,0x1854,0x1855,0x1856,0x1857,0x1858,0x1859,0x185A,0x185B,0x185C,0x185D,0x185E,0x185F,0x1860,0x1861,0x1862,0x1863,0x1864,0x1865,0x1866,0x1867,0x1868,0x1869,0x186A,0x186B,0x186C,0x186D,0x186E,0x186F,0x1870,0x1871,0x1872,0x1873,0x1874,0x1875,0x1876,0x1877,0x1878,0x187F,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1880,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1881,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1882,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1883,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1884,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1885,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1886,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1887,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1888,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x1889,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188A,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188B,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188C,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188D,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188E,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x188F,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1890,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1891,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1892,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1893,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1894,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1895,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1896,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1897,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1898,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x1899,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189A,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189B,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189C,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189D,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189E,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x189F,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A0,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A1,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A2,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A3,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A4,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A5,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A6,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A7,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A8,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18A9,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AA,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AB,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AC,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AD,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AE,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18AF,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B0,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B1,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B2,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B3,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B4,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B5,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B6,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B7,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B8,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18B9,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BA,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BB,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BC,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BD,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BE,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18BF,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C0,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C1,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C2,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C3,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C4,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C5,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C6,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C7,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C8,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18C9,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CA,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CB,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CC,0x18CD,0x18CD,0x18CD,0x18CD,0x18CD,0x18CD,0x18CD,0x18CE,0x18CF,0x18CF,0x18D0,0x18D0,0x18D0,0x18D1,0x18D2,0x18D3,0x18D4,0x18D5,0x18D6,0x18D7,0x18D8,0x18D9,0x18DA,0x18DB,0x18DC,0x18DD,0x18DE,0x18DF,0x18E0,0x18E1,0x18E2,0x18E3,0x18E4,0x18E5,0x18E6,0x18E7,0x18E8,0x18E9,0x18EA,0x18EB,0x18EC,0x18ED,0x18EE,0x18EF,0x18F0,0x18F1,0x18F2,0x18F3,0x18F4,0x18F5,0x1900,0x1901,0x1902,0x1903,0x1904,0x1905,0x1906,0x1907,0x1908,0x1909,0x190A,0x190B,0x190C,0x190D,0x190E,0x190F,0x1910,0x1911,0x1912,0x1913,0x1914,0x1915,0x1916,0x1917,0x1918,0x1919,0x191A,0x191B,0x191C,0x191D,0x191E,0x1950,0x1951,0x1952,0x1953,0x1954,0x1955,0x1956,0x1957,0x1958,0x1959,0x195A,0x195B,0x195C,0x195D,0x195E,0x195F,0x1960,0x1961,0x1962,0x1963,0x1964,0x1965,0x1966,0x1967,0x1968,0x1969,0x196A,0x196B,0x196C,0x196D,0x1970,0x1971,0x1972,0x1973,0x1974,0x1980,0x1981,0x1982,0x1983,0x1984,0x1985,0x1986,0x1987,0x1988,0x1989,0x198A,0x198B,0x198C,0x198D,0x198E,0x198F,0x1990,0x1991,0x1992,0x1993,0x1994,0x1995,0x1996,0x1997,0x1998,0x1999,0x199A,0x199B,0x199C,0x199D,0x199E,0x199F,0x19A0,0x19A1,0x19A2,0x19A3,0x19A4,0x19A5,0x19A6,0x19A7,0x19A8,0x19A9,0x19AA,0x19AB,0x19B0,0x19B1,0x19B2,0x19B3,0x19B4,0x19B5,0x19B6,0x19B7,0x19B8,0x19B9,0x19BA,0x19BB,0x19BC,0x19BD,0x19BE,0x19BF,0x19C0,0x19C1,0x19C2,0x19C3,0x19C4,0x19C5,0x19C6,0x19C7,0x19C8,0x19C9,0x1A00,0x1A01,0x1A02,0x1A03,0x1A04,0x1A05,0x1A06,0x1A07,0x1A08,0x1A09,0x1A0A,0x1A0B,0x1A0C,0x1A0D,0x1A0E,0x1A0F,0x1A10,0x1A11,0x1A12,0x1A13,0x1A14,0x1A15,0x1A16,0x1A20,0x1A21,0x1A22,0x1A23,0x1A24,0x1A25,0x1A26,0x1A27,0x1A28,0x1A29,0x1A2A,0x1A2B,0x1A2C,0x1A2D,0x1A2E,0x1A2F,0x1A30,0x1A31,0x1A32,0x1A33,0x1A34,0x1A35,0x1A36,0x1A37,0x1A38,0x1A39,0x1A3A,0x1A3B,0x1A3C,0x1A3D,0x1A3E,0x1A3F,0x1A40,0x1A41,0x1A42,0x1A43,0x1A44,0x1A45,0x1A46,0x1A47,0x1A48,0x1A49,0x1A4A,0x1A4B,0x1A4C,0x1A4D,0x1A4E,0x1A4F,0x1A50,0x1A51,0x1A52,0x1A53,0x1A54,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B00,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B01,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B02,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B03,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B04,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B05,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B06,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B07,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B08,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B09,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0A,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0B,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0C,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0D,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0E,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B0F,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B10,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B11,0x1B12,0x1B12,0x1B12,0x1B12,0x1B13,0x1B13,0x1B14,0x1B15,0x1B15,0x1B15,0x1B15,0x1B15,0x1B16,0x1B16,0x1B16,0x1B16,0x1B16,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B17,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B18,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B19,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1A,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1B,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1C,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1D,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1E,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B1F,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B20,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B21,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B22,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B23,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B24,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B25,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B26,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B27,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B28,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B29,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2A,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2B,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2C,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2D,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2E,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B2F,0x1B30,0x1B31,0x1B32,0x1B33,0x1B45,0x1B46,0x1B47,0x1B48,0x1B49,0x1B4A,0x1B4B,0x1B4C,0x1B83,0x1B84,0x1B85,0x1B86,0x1B87,0x1B88,0x1B89,0x1B8A,0x1B8B,0x1B8C,0x1B8D,0x1B8E,0x1B8F,0x1B90,0x1B91,0x1B92,0x1B93,0x1B94,0x1B95,0x1B96,0x1B97,0x1B98,0x1B99,0x1B9A,0x1B9B,0x1B9C,0x1B9D,0x1B9E,0x1B9F,0x1BA0,0x1BAE,0x1BAF,0x1BBA,0x1BBB,0x1BBC,0x1BBD,0x1BBE,0x1BBF,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC0,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC1,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC2,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC3,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC4,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC5,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC6,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC7,0x1BC8,0x1BC8,0x1BC8,0x1BC8,0x1BC8,0x1BC8,0x1BC8,0x1BC8,0x1BC8,0x1BC8,0x1BC9,0x1BC9,0x1BC9,0x1BC9,0x1BC9,0x1BC9,0x1BC9,0x1BC9,0x1BC9,0x1BC9,0x1BC9,0x1BCA,0x1BCB,0x1BCC,0x1BCD,0x1BCE,0x1BCF,0x1BD0,0x1BD1,0x1BD2,0x1BD3,0x1BD4,0x1BD5,0x1BD6,0x1BD7,0x1BD8,0x1BD9,0x1BDA,0x1BDB,0x1BDC,0x1BDD,0x1BDE,0x1BDF,0x1BE0,0x1BE1,0x1BE2,0x1BE3,0x1BE4,0x1BE5,0x1C00,0x1C01,0x1C02,0x1C03,0x1C04,0x1C05,0x1C06,0x1C07,0x1C08,0x1C09,0x1C0A,0x1C0B,0x1C0C,0x1C0D,0x1C0E,0x1C0F,0x1C10,0x1C11,0x1C12,0x1C13,0x1C14,0x1C15,0x1C16,0x1C17,0x1C18,0x1C19,0x1C1A,0x1C1B,0x1C1C,0x1C1D,0x1C1E,0x1C1F,0x1C20,0x1C21,0x1C22,0x1C23,0x1C4D,0x1C4E,0x1C4F,0x1C5A,0x1C5B,0x1C5C,0x1C5D,0x1C5E,0x1C5F,0x1C60,0x1C61,0x1C62,0x1C63,0x1C64,0x1C65,0x1C66,0x1C67,0x1C68,0x1C69,0x1C6A,0x1C6B,0x1C6C,0x1C6D,0x1C6E,0x1C6F,0x1C70,0x1C71,0x1C72,0x1C73,0x1C74,0x1C75,0x1C76,0x1C77,0x1CE9,0x1CEA,0x1CEB,0x1CEC,0x1CEE,0x1CEF,0x1CF0,0x1CF1,0x1CF2,0x1CF3,0x1CF5,0x1CF6,0x1CFA,0x1DF0,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E10,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E11,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E12,0x1E14,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E29,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2A,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2C,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2D,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4D,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5D,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5E,0x1E5F,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7E,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E7F,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E80,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E81,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E82,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E83,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E84,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E85,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E86,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E87,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E88,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E89,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8A,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8B,0x1E8C,0x1E8C,0x1E8C,0x1E8C,0x1E8C,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE0,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE1,0x1EE2,0x1EE2,0x1EE2,0x1EE2,0x1EE2,0x1EE2,0x1EE2,0x1EE2,0x1EE2,0x1EE2,0x1EE2,0x1EE3,0x1EE3,0x1EE3,0x1EE3,0x1EE3,0x1EE3,0x1EE3,0x1EE3,0x1EE3,0x1EE4,0x1EE4,0x1EE4,0x1EE4,0x1EE4,0x1EE4,0x1EE4,0x1EE5,0x1EE5,0x1EE5,0x1EE5,0x1EE5,0x1EE5,0x1EE5,0x1EE5,0x1EE6,0x1EE6,0x1EE6,0x1EE6,0x1EE6,0x1EE6,0x1EE6,0x1EE6,0x1EE6,0x1EE6,0x1EE6,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE7,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE8,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EE9,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEA,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x1EEB,0x2000,0x2135,0x2136,0x2137,0x2138,0x2A6D,0x2A70,0x2B73,0x2B74,0x2B81,0x2B82,0x2CEA,0x2CEB,0x2D30,0x2D31,0x2D32,0x2D33,0x2D34,0x2D35,0x2D36,0x2D37,0x2D38,0x2D39,0x2D3A,0x2D3B,0x2D3C,0x2D3D,0x2D3E,0x2D3F,0x2D40,0x2D41,0x2D42,0x2D43,0x2D44,0x2D45,0x2D46,0x2D47,0x2D48,0x2D49,0x2D4A,0x2D4B,0x2D4C,0x2D4D,0x2D4E,0x2D4F,0x2D50,0x2D51,0x2D52,0x2D53,0x2D54,0x2D55,0x2D56,0x2D57,0x2D58,0x2D59,0x2D5A,0x2D5B,0x2D5C,0x2D5D,0x2D5E,0x2D5F,0x2D60,0x2D61,0x2D62,0x2D63,0x2D64,0x2D65,0x2D66,0x2D67,0x2D80,0x2D81,0x2D82,0x2D83,0x2D84,0x2D85,0x2D86,0x2D87,0x2D88,0x2D89,0x2D8A,0x2D8B,0x2D8C,0x2D8D,0x2D8E,0x2D8F,0x2D90,0x2D91,0x2D92,0x2D93,0x2D94,0x2D95,0x2D96,0x2DA0,0x2DA1,0x2DA2,0x2DA3,0x2DA4,0x2DA5,0x2DA6,0x2DA8,0x2DA9,0x2DAA,0x2DAB,0x2DAC,0x2DAD,0x2DAE,0x2DB0,0x2DB1,0x2DB2,0x2DB3,0x2DB4,0x2DB5,0x2DB6,0x2DB8,0x2DB9,0x2DBA,0x2DBB,0x2DBC,0x2DBD,0x2DBE,0x2DC0,0x2DC1,0x2DC2,0x2DC3,0x2DC4,0x2DC5,0x2DC6,0x2DC8,0x2DC9,0x2DCA,0x2DCB,0x2DCC,0x2DCD,0x2DCE,0x2DD0,0x2DD1,0x2DD2,0x2DD3,0x2DD4,0x2DD5,0x2DD6,0x2DD8,0x2DD9,0x2DDA,0x2DDB,0x2DDC,0x2DDD,0x2DDE,0x2EBE,0x2EBF,0x2EE5,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F80,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F81,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F82,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F83,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F84,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F85,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F86,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F87,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F88,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F89,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8A,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8B,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8C,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8D,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8E,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F8F,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F90,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F91,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F92,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F93,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F94,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F95,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F96,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F97,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F98,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F99,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9A,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9B,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9C,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9D,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9E,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2F9F,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA0,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x2FA1,0x3000,0x3006,0x303C,0x3041,0x3042,0x3043,0x3044,0x3045,0x3046,0x3047,0x3048,0x3049,0x304A,0x304B,0x304C,0x304D,0x304E,0x304F,0x3050,0x3051,0x3052,0x3053,0x3054,0x3055,0x3056,0x3057,0x3058,0x3059,0x305A,0x305B,0x305C,0x305D,0x305E,0x305F,0x3060,0x3061,0x3062,0x3063,0x3064,0x3065,0x3066,0x3067,0x3068,0x3069,0x306A,0x306B,0x306C,0x306D,0x306E,0x306F,0x3070,0x3071,0x3072,0x3073,0x3074,0x3075,0x3076,0x3077,0x3078,0x3079,0x307A,0x307B,0x307C,0x307D,0x307E,0x307F,0x3080,0x3081,0x3082,0x3083,0x3084,0x3085,0x3086,0x3087,0x3088,0x3089,0x308A,0x308B,0x308C,0x308D,0x308E,0x308F,0x3090,0x3091,0x3092,0x3093,0x3094,0x3095,0x3096,0x309F,0x30A1,0x30A2,0x30A3,0x30A4,0x30A5,0x30A6,0x30A7,0x30A8,0x30A9,0x30AA,0x30AB,0x30AC,0x30AD,0x30AE,0x30AF,0x30B0,0x30B1,0x30B2,0x30B3,0x30B4,0x30B5,0x30B6,0x30B7,0x30B8,0x30B9,0x30BA,0x30BB,0x30BC,0x30BD,0x30BE,0x30BF,0x30C0,0x30C1,0x30C2,0x30C3,0x30C4,0x30C5,0x30C6,0x30C7,0x30C8,0x30C9,0x30CA,0x30CB,0x30CC,0x30CD,0x30CE,0x30CF,0x30D0,0x30D1,0x30D2,0x30D3,0x30D4,0x30D5,0x30D6,0x30D7,0x30D8,0x30D9,0x30DA,0x30DB,0x30DC,0x30DD,0x30DE,0x30DF,0x30E0,0x30E1,0x30E2,0x30E3,0x30E4,0x30E5,0x30E6,0x30E7,0x30E8,0x30E9,0x30EA,0x30EB,0x30EC,0x30ED,0x30EE,0x30EF,0x30F0,0x30F1,0x30F2,0x30F3,0x30F4,0x30F5,0x30F6,0x30F7,0x30F8,0x30F9,0x30FA,0x30FF,0x3105,0x3106,0x3107,0x3108,0x3109,0x310A,0x310B,0x310C,0x310D,0x310E,0x310F,0x3110,0x3111,0x3112,0x3113,0x3114,0x3115,0x3116,0x3117,0x3118,0x3119,0x311A,0x311B,0x311C,0x311D,0x311E,0x311F,0x3120,0x3121,0x3122,0x3123,0x3124,0x3125,0x3126,0x3127,0x3128,0x3129,0x312A,0x312B,0x312C,0x312D,0x312E,0x312F,0x3131,0x3132,0x3133,0x3134,0x3134,0x3135,0x3135,0x3136,0x3137,0x3138,0x3139,0x313A,0x313B,0x313C,0x313D,0x313E,0x313F,0x3140,0x3141,0x3142,0x3143,0x3144,0x3145,0x3146,0x3147,0x3148,0x3149,0x314A,0x314B,0x314C,0x314D,0x314E,0x314F,0x3150,0x3151,0x3152,0x3153,0x3154,0x3155,0x3156,0x3157,0x3158,0x3159,0x315A,0x315B,0x315C,0x315D,0x315E,0x315F,0x3160,0x3161,0x3162,0x3163,0x3164,0x3165,0x3166,0x3167,0x3168,0x3169,0x316A,0x316B,0x316C,0x316D,0x316E,0x316F,0x3170,0x3171,0x3172,0x3173,0x3174,0x3175,0x3176,0x3177,0x3178,0x3179,0x317A,0x317B,0x317C,0x317D,0x317E,0x317F,0x3180,0x3181,0x3182,0x3183,0x3184,0x3185,0x3186,0x3187,0x3188,0x3189,0x318A,0x318B,0x318C,0x318D,0x318E,0x31A0,0x31A1,0x31A2,0x31A3,0x31A4,0x31A5,0x31A6,0x31A7,0x31A8,0x31A9,0x31AA,0x31AB,0x31AC,0x31AD,0x31AE,0x31AF,0x31B0,0x31B1,0x31B2,0x31B3,0x31B4,0x31B5,0x31B6,0x31B7,0x31B8,0x31B9,0x31BA,0x31BB,0x31BC,0x31BD,0x31BE,0x31BF,0x31F0,0x31F1,0x31F2,0x31F3,0x31F4,0x31F5,0x31F6,0x31F7,0x31F8,0x31F9,0x31FA,0x31FB,0x31FC,0x31FD,0x31FE,0x31FF,0x323A,0x3400,0x4DBF,0x4E00,0x9FFF,0xA000,0xA001,0xA002,0xA003,0xA004,0xA005,0xA006,0xA007,0xA008,0xA009,0xA00A,0xA00B,0xA00C,0xA00D,0xA00E,0xA00F,0xA010,0xA011,0xA012,0xA013,0xA014,0xA016,0xA017,0xA018,0xA019,0xA01A,0xA01B,0xA01C,0xA01D,0xA01E,0xA01F,0xA020,0xA021,0xA022,0xA023,0xA024,0xA025,0xA026,0xA027,0xA028,0xA029,0xA02A,0xA02B,0xA02C,0xA02D,0xA02E,0xA02F,0xA030,0xA031,0xA032,0xA033,0xA034,0xA035,0xA036,0xA037,0xA038,0xA039,0xA03A,0xA03B,0xA03C,0xA03D,0xA03E,0xA03F,0xA040,0xA041,0xA042,0xA043,0xA044,0xA045,0xA046,0xA047,0xA048,0xA049,0xA04A,0xA04B,0xA04C,0xA04D,0xA04E,0xA04F,0xA050,0xA051,0xA052,0xA053,0xA054,0xA055,0xA056,0xA057,0xA058,0xA059,0xA05A,0xA05B,0xA05C,0xA05D,0xA05E,0xA05F,0xA060,0xA061,0xA062,0xA063,0xA064,0xA065,0xA066,0xA067,0xA068,0xA069,0xA06A,0xA06B,0xA06C,0xA06D,0xA06E,0xA06F,0xA070,0xA071,0xA072,0xA073,0xA074,0xA075,0xA076,0xA077,0xA078,0xA079,0xA07A,0xA07B,0xA07C,0xA07D,0xA07E,0xA07F,0xA080,0xA081,0xA082,0xA083,0xA084,0xA085,0xA086,0xA087,0xA088,0xA089,0xA08A,0xA08B,0xA08C,0xA08D,0xA08E,0xA08F,0xA090,0xA091,0xA092,0xA093,0xA094,0xA095,0xA096,0xA097,0xA098,0xA099,0xA09A,0xA09B,0xA09C,0xA09D,0xA09E,0xA09F,0xA0A0,0xA0A1,0xA0A2,0xA0A3,0xA0A4,0xA0A5,0xA0A6,0xA0A7,0xA0A8,0xA0A9,0xA0AA,0xA0AB,0xA0AC,0xA0AD,0xA0AE,0xA0AF,0xA0B0,0xA0B1,0xA0B2,0xA0B3,0xA0B4,0xA0B5,0xA0B6,0xA0B7,0xA0B8,0xA0B9,0xA0BA,0xA0BB,0xA0BC,0xA0BD,0xA0BE,0xA0BF,0xA0C0,0xA0C1,0xA0C2,0xA0C3,0xA0C4,0xA0C5,0xA0C6,0xA0C7,0xA0C8,0xA0C9,0xA0CA,0xA0CB,0xA0CC,0xA0CD,0xA0CE,0xA0CF,0xA0D0,0xA0D1,0xA0D2,0xA0D3,0xA0D4,0xA0D5,0xA0D6,0xA0D7,0xA0D8,0xA0D9,0xA0DA,0xA0DB,0xA0DC,0xA0DD,0xA0DE,0xA0DF,0xA0E0,0xA0E1,0xA0E2,0xA0E3,0xA0E4,0xA0E5,0xA0E6,0xA0E7,0xA0E8,0xA0E9,0xA0EA,0xA0EB,0xA0EC,0xA0ED,0xA0EE,0xA0EF,0xA0F0,0xA0F1,0xA0F2,0xA0F3,0xA0F4,0xA0F5,0xA0F6,0xA0F7,0xA0F8,0xA0F9,0xA0FA,0xA0FB,0xA0FC,0xA0FD,0xA0FE,0xA0FF,0xA100,0xA101,0xA102,0xA103,0xA104,0xA105,0xA106,0xA107,0xA108,0xA109,0xA10A,0xA10B,0xA10C,0xA10D,0xA10E,0xA10F,0xA110,0xA111,0xA112,0xA113,0xA114,0xA115,0xA116,0xA117,0xA118,0xA119,0xA11A,0xA11B,0xA11C,0xA11D,0xA11E,0xA11F,0xA120,0xA121,0xA122,0xA123,0xA124,0xA125,0xA126,0xA127,0xA128,0xA129,0xA12A,0xA12B,0xA12C,0xA12D,0xA12E,0xA12F,0xA130,0xA131,0xA132,0xA133,0xA134,0xA135,0xA136,0xA137,0xA138,0xA139,0xA13A,0xA13B,0xA13C,0xA13D,0xA13E,0xA13F,0xA140,0xA141,0xA142,0xA143,0xA144,0xA145,0xA146,0xA147,0xA148,0xA149,0xA14A,0xA14B,0xA14C,0xA14D,0xA14E,0xA14F,0xA150,0xA151,0xA152,0xA153,0xA154,0xA155,0xA156,0xA157,0xA158,0xA159,0xA15A,0xA15B,0xA15C,0xA15D,0xA15E,0xA15F,0xA160,0xA161,0xA162,0xA163,0xA164,0xA165,0xA166,0xA167,0xA168,0xA169,0xA16A,0xA16B,0xA16C,0xA16D,0xA16E,0xA16F,0xA170,0xA171,0xA172,0xA173,0xA174,0xA175,0xA176,0xA177,0xA178,0xA179,0xA17A,0xA17B,0xA17C,0xA17D,0xA17E,0xA17F,0xA180,0xA181,0xA182,0xA183,0xA184,0xA185,0xA186,0xA187,0xA188,0xA189,0xA18A,0xA18B,0xA18C,0xA18D,0xA18E,0xA18F,0xA190,0xA191,0xA192,0xA193,0xA194,0xA195,0xA196,0xA197,0xA198,0xA199,0xA19A,0xA19B,0xA19C,0xA19D,0xA19E,0xA19F,0xA1A0,0xA1A1,0xA1A2,0xA1A3,0xA1A4,0xA1A5,0xA1A6,0xA1A7,0xA1A8,0xA1A9,0xA1AA,0xA1AB,0xA1AC,0xA1AD,0xA1AE,0xA1AF,0xA1B0,0xA1B1,0xA1B2,0xA1B3,0xA1B4,0xA1B5,0xA1B6,0xA1B7,0xA1B8,0xA1B9,0xA1BA,0xA1BB,0xA1BC,0xA1BD,0xA1BE,0xA1BF,0xA1C0,0xA1C1,0xA1C2,0xA1C3,0xA1C4,0xA1C5,0xA1C6,0xA1C7,0xA1C8,0xA1C9,0xA1CA,0xA1CB,0xA1CC,0xA1CD,0xA1CE,0xA1CF,0xA1D0,0xA1D1,0xA1D2,0xA1D3,0xA1D4,0xA1D5,0xA1D6,0xA1D7,0xA1D8,0xA1D9,0xA1DA,0xA1DB,0xA1DC,0xA1DD,0xA1DE,0xA1DF,0xA1E0,0xA1E1,0xA1E2,0xA1E3,0xA1E4,0xA1E5,0xA1E6,0xA1E7,0xA1E8,0xA1E9,0xA1EA,0xA1EB,0xA1EC,0xA1ED,0xA1EE,0xA1EF,0xA1F0,0xA1F1,0xA1F2,0xA1F3,0xA1F4,0xA1F5,0xA1F6,0xA1F7,0xA1F8,0xA1F9,0xA1FA,0xA1FB,0xA1FC,0xA1FD,0xA1FE,0xA1FF,0xA200,0xA201,0xA202,0xA203,0xA204,0xA205,0xA206,0xA207,0xA208,0xA209,0xA20A,0xA20B,0xA20C,0xA20D,0xA20E,0xA20F,0xA210,0xA211,0xA212,0xA213,0xA214,0xA215,0xA216,0xA217,0xA218,0xA219,0xA21A,0xA21B,0xA21C,0xA21D,0xA21E,0xA21F,0xA220,0xA221,0xA222,0xA223,0xA224,0xA225,0xA226,0xA227,0xA228,0xA229,0xA22A,0xA22B,0xA22C,0xA22D,0xA22E,0xA22F,0xA230,0xA231,0xA232,0xA233,0xA234,0xA235,0xA236,0xA237,0xA238,0xA239,0xA23A,0xA23B,0xA23C,0xA23D,0xA23E,0xA23F,0xA240,0xA241,0xA242,0xA243,0xA244,0xA245,0xA246,0xA247,0xA248,0xA249,0xA24A,0xA24B,0xA24C,0xA24D,0xA24E,0xA24F,0xA250,0xA251,0xA252,0xA253,0xA254,0xA255,0xA256,0xA257,0xA258,0xA259,0xA25A,0xA25B,0xA25C,0xA25D,0xA25E,0xA25F,0xA260,0xA261,0xA262,0xA263,0xA264,0xA265,0xA266,0xA267,0xA268,0xA269,0xA26A,0xA26B,0xA26C,0xA26D,0xA26E,0xA26F,0xA270,0xA271,0xA272,0xA273,0xA274,0xA275,0xA276,0xA277,0xA278,0xA279,0xA27A,0xA27B,0xA27C,0xA27D,0xA27E,0xA27F,0xA280,0xA281,0xA282,0xA283,0xA284,0xA285,0xA286,0xA287,0xA288,0xA289,0xA28A,0xA28B,0xA28C,0xA28D,0xA28E,0xA28F,0xA290,0xA291,0xA292,0xA293,0xA294,0xA295,0xA296,0xA297,0xA298,0xA299,0xA29A,0xA29B,0xA29C,0xA29D,0xA29E,0xA29F,0xA2A0,0xA2A1,0xA2A2,0xA2A3,0xA2A4,0xA2A5,0xA2A6,0xA2A7,0xA2A8,0xA2A9,0xA2AA,0xA2AB,0xA2AC,0xA2AD,0xA2AE,0xA2AF,0xA2B0,0xA2B1,0xA2B2,0xA2B3,0xA2B4,0xA2B5,0xA2B6,0xA2B7,0xA2B8,0xA2B9,0xA2BA,0xA2BB,0xA2BC,0xA2BD,0xA2BE,0xA2BF,0xA2C0,0xA2C1,0xA2C2,0xA2C3,0xA2C4,0xA2C5,0xA2C6,0xA2C7,0xA2C8,0xA2C9,0xA2CA,0xA2CB,0xA2CC,0xA2CD,0xA2CE,0xA2CF,0xA2D0,0xA2D1,0xA2D2,0xA2D3,0xA2D4,0xA2D5,0xA2D6,0xA2D7,0xA2D8,0xA2D9,0xA2DA,0xA2DB,0xA2DC,0xA2DD,0xA2DE,0xA2DF,0xA2E0,0xA2E1,0xA2E2,0xA2E3,0xA2E4,0xA2E5,0xA2E6,0xA2E7,0xA2E8,0xA2E9,0xA2EA,0xA2EB,0xA2EC,0xA2ED,0xA2EE,0xA2EF,0xA2F0,0xA2F1,0xA2F2,0xA2F3,0xA2F4,0xA2F5,0xA2F6,0xA2F7,0xA2F8,0xA2F9,0xA2FA,0xA2FB,0xA2FC,0xA2FD,0xA2FE,0xA2FF,0xA300,0xA301,0xA302,0xA303,0xA304,0xA305,0xA306,0xA307,0xA308,0xA309,0xA30A,0xA30B,0xA30C,0xA30D,0xA30E,0xA30F,0xA310,0xA311,0xA312,0xA313,0xA314,0xA315,0xA316,0xA317,0xA318,0xA319,0xA31A,0xA31B,0xA31C,0xA31D,0xA31E,0xA31F,0xA320,0xA321,0xA322,0xA323,0xA324,0xA325,0xA326,0xA327,0xA328,0xA329,0xA32A,0xA32B,0xA32C,0xA32D,0xA32E,0xA32F,0xA330,0xA331,0xA332,0xA333,0xA334,0xA335,0xA336,0xA337,0xA338,0xA339,0xA33A,0xA33B,0xA33C,0xA33D,0xA33E,0xA33F,0xA340,0xA341,0xA342,0xA343,0xA344,0xA345,0xA346,0xA347,0xA348,0xA349,0xA34A,0xA34B,0xA34C,0xA34D,0xA34E,0xA34F,0xA350,0xA351,0xA352,0xA353,0xA354,0xA355,0xA356,0xA357,0xA358,0xA359,0xA35A,0xA35B,0xA35C,0xA35D,0xA35E,0xA35F,0xA360,0xA361,0xA362,0xA363,0xA364,0xA365,0xA366,0xA367,0xA368,0xA369,0xA36A,0xA36B,0xA36C,0xA36D,0xA36E,0xA36F,0xA370,0xA371,0xA372,0xA373,0xA374,0xA375,0xA376,0xA377,0xA378,0xA379,0xA37A,0xA37B,0xA37C,0xA37D,0xA37E,0xA37F,0xA380,0xA381,0xA382,0xA383,0xA384,0xA385,0xA386,0xA387,0xA388,0xA389,0xA38A,0xA38B,0xA38C,0xA38D,0xA38E,0xA38F,0xA390,0xA391,0xA392,0xA393,0xA394,0xA395,0xA396,0xA397,0xA398,0xA399,0xA39A,0xA39B,0xA39C,0xA39D,0xA39E,0xA39F,0xA3A0,0xA3A1,0xA3A2,0xA3A3,0xA3A4,0xA3A5,0xA3A6,0xA3A7,0xA3A8,0xA3A9,0xA3AA,0xA3AB,0xA3AC,0xA3AD,0xA3AE,0xA3AF,0xA3B0,0xA3B1,0xA3B2,0xA3B3,0xA3B4,0xA3B5,0xA3B6,0xA3B7,0xA3B8,0xA3B9,0xA3BA,0xA3BB,0xA3BC,0xA3BD,0xA3BE,0xA3BF,0xA3C0,0xA3C1,0xA3C2,0xA3C3,0xA3C4,0xA3C5,0xA3C6,0xA3C7,0xA3C8,0xA3C9,0xA3CA,0xA3CB,0xA3CC,0xA3CD,0xA3CE,0xA3CF,0xA3D0,0xA3D1,0xA3D2,0xA3D3,0xA3D4,0xA3D5,0xA3D6,0xA3D7,0xA3D8,0xA3D9,0xA3DA,0xA3DB,0xA3DC,0xA3DD,0xA3DE,0xA3DF,0xA3E0,0xA3E1,0xA3E2,0xA3E3,0xA3E4,0xA3E5,0xA3E6,0xA3E7,0xA3E8,0xA3E9,0xA3EA,0xA3EB,0xA3EC,0xA3ED,0xA3EE,0xA3EF,0xA3F0,0xA3F1,0xA3F2,0xA3F3,0xA3F4,0xA3F5,0xA3F6,0xA3F7,0xA3F8,0xA3F9,0xA3FA,0xA3FB,0xA3FC,0xA3FD,0xA3FE,0xA3FF,0xA400,0xA401,0xA402,0xA403,0xA404,0xA405,0xA406,0xA407,0xA408,0xA409,0xA40A,0xA40B,0xA40C,0xA40D,0xA40E,0xA40F,0xA410,0xA411,0xA412,0xA413,0xA414,0xA415,0xA416,0xA417,0xA418,0xA419,0xA41A,0xA41B,0xA41C,0xA41D,0xA41E,0xA41F,0xA420,0xA421,0xA422,0xA423,0xA424,0xA425,0xA426,0xA427,0xA428,0xA429,0xA42A,0xA42B,0xA42C,0xA42D,0xA42E,0xA42F,0xA430,0xA431,0xA432,0xA433,0xA434,0xA435,0xA436,0xA437,0xA438,0xA439,0xA43A,0xA43B,0xA43C,0xA43D,0xA43E,0xA43F,0xA440,0xA441,0xA442,0xA443,0xA444,0xA445,0xA446,0xA447,0xA448,0xA449,0xA44A,0xA44B,0xA44C,0xA44D,0xA44E,0xA44F,0xA450,0xA451,0xA452,0xA453,0xA454,0xA455,0xA456,0xA457,0xA458,0xA459,0xA45A,0xA45B,0xA45C,0xA45D,0xA45E,0xA45F,0xA460,0xA461,0xA462,0xA463,0xA464,0xA465,0xA466,0xA467,0xA468,0xA469,0xA46A,0xA46B,0xA46C,0xA46D,0xA46E,0xA46F,0xA470,0xA471,0xA472,0xA473,0xA474,0xA475,0xA476,0xA477,0xA478,0xA479,0xA47A,0xA47B,0xA47C,0xA47D,0xA47E,0xA47F,0xA480,0xA481,0xA482,0xA483,0xA484,0xA485,0xA486,0xA487,0xA488,0xA489,0xA48A,0xA48B,0xA48C,0xA4D0,0xA4D1,0xA4D2,0xA4D3,0xA4D4,0xA4D5,0xA4D6,0xA4D7,0xA4D8,0xA4D9,0xA4DA,0xA4DB,0xA4DC,0xA4DD,0xA4DE,0xA4DF,0xA4E0,0xA4E1,0xA4E2,0xA4E3,0xA4E4,0xA4E5,0xA4E6,0xA4E7,0xA4E8,0xA4E9,0xA4EA,0xA4EB,0xA4EC,0xA4ED,0xA4EE,0xA4EF,0xA4F0,0xA4F1,0xA4F2,0xA4F3,0xA4F4,0xA4F5,0xA4F6,0xA4F7,0xA500,0xA501,0xA502,0xA503,0xA504,0xA505,0xA506,0xA507,0xA508,0xA509,0xA50A,0xA50B,0xA50C,0xA50D,0xA50E,0xA50F,0xA510,0xA511,0xA512,0xA513,0xA514,0xA515,0xA516,0xA517,0xA518,0xA519,0xA51A,0xA51B,0xA51C,0xA51D,0xA51E,0xA51F,0xA520,0xA521,0xA522,0xA523,0xA524,0xA525,0xA526,0xA527,0xA528,0xA529,0xA52A,0xA52B,0xA52C,0xA52D,0xA52E,0xA52F,0xA530,0xA531,0xA532,0xA533,0xA534,0xA535,0xA536,0xA537,0xA538,0xA539,0xA53A,0xA53B,0xA53C,0xA53D,0xA53E,0xA53F,0xA540,0xA541,0xA542,0xA543,0xA544,0xA545,0xA546,0xA547,0xA548,0xA549,0xA54A,0xA54B,0xA54C,0xA54D,0xA54E,0xA54F,0xA550,0xA551,0xA552,0xA553,0xA554,0xA555,0xA556,0xA557,0xA558,0xA559,0xA55A,0xA55B,0xA55C,0xA55D,0xA55E,0xA55F,0xA560,0xA561,0xA562,0xA563,0xA564,0xA565,0xA566,0xA567,0xA568,0xA569,0xA56A,0xA56B,0xA56C,0xA56D,0xA56E,0xA56F,0xA570,0xA571,0xA572,0xA573,0xA574,0xA575,0xA576,0xA577,0xA578,0xA579,0xA57A,0xA57B,0xA57C,0xA57D,0xA57E,0xA57F,0xA580,0xA581,0xA582,0xA583,0xA584,0xA585,0xA586,0xA587,0xA588,0xA589,0xA58A,0xA58B,0xA58C,0xA58D,0xA58E,0xA58F,0xA590,0xA591,0xA592,0xA593,0xA594,0xA595,0xA596,0xA597,0xA598,0xA599,0xA59A,0xA59B,0xA59C,0xA59D,0xA59E,0xA59F,0xA5A0,0xA5A1,0xA5A2,0xA5A3,0xA5A4,0xA5A5,0xA5A6,0xA5A7,0xA5A8,0xA5A9,0xA5AA,0xA5AB,0xA5AC,0xA5AD,0xA5AE,0xA5AF,0xA5B0,0xA5B1,0xA5B2,0xA5B3,0xA5B4,0xA5B5,0xA5B6,0xA5B7,0xA5B8,0xA5B9,0xA5BA,0xA5BB,0xA5BC,0xA5BD,0xA5BE,0xA5BF,0xA5C0,0xA5C1,0xA5C2,0xA5C3,0xA5C4,0xA5C5,0xA5C6,0xA5C7,0xA5C8,0xA5C9,0xA5CA,0xA5CB,0xA5CC,0xA5CD,0xA5CE,0xA5CF,0xA5D0,0xA5D1,0xA5D2,0xA5D3,0xA5D4,0xA5D5,0xA5D6,0xA5D7,0xA5D8,0xA5D9,0xA5DA,0xA5DB,0xA5DC,0xA5DD,0xA5DE,0xA5DF,0xA5E0,0xA5E1,0xA5E2,0xA5E3,0xA5E4,0xA5E5,0xA5E6,0xA5E7,0xA5E8,0xA5E9,0xA5EA,0xA5EB,0xA5EC,0xA5ED,0xA5EE,0xA5EF,0xA5F0,0xA5F1,0xA5F2,0xA5F3,0xA5F4,0xA5F5,0xA5F6,0xA5F7,0xA5F8,0xA5F9,0xA5FA,0xA5FB,0xA5FC,0xA5FD,0xA5FE,0xA5FF,0xA600,0xA601,0xA602,0xA603,0xA604,0xA605,0xA606,0xA607,0xA608,0xA609,0xA60A,0xA60B,0xA610,0xA611,0xA612,0xA613,0xA614,0xA615,0xA616,0xA617,0xA618,0xA619,0xA61A,0xA61B,0xA61C,0xA61D,0xA61E,0xA61F,0xA62A,0xA62B,0xA66E,0xA6A0,0xA6A1,0xA6A2,0xA6A3,0xA6A4,0xA6A5,0xA6A6,0xA6A7,0xA6A8,0xA6A9,0xA6AA,0xA6AB,0xA6AC,0xA6AD,0xA6AE,0xA6AF,0xA6B0,0xA6B1,0xA6B2,0xA6B3,0xA6B4,0xA6B5,0xA6B6,0xA6B7,0xA6B8,0xA6B9,0xA6BA,0xA6BB,0xA6BC,0xA6BD,0xA6BE,0xA6BF,0xA6C0,0xA6C1,0xA6C2,0xA6C3,0xA6C4,0xA6C5,0xA6C6,0xA6C7,0xA6C8,0xA6C9,0xA6CA,0xA6CB,0xA6CC,0xA6CD,0xA6CE,0xA6CF,0xA6D0,0xA6D1,0xA6D2,0xA6D3,0xA6D4,0xA6D5,0xA6D6,0xA6D7,0xA6D8,0xA6D9,0xA6DA,0xA6DB,0xA6DC,0xA6DD,0xA6DE,0xA6DF,0xA6E0,0xA6E1,0xA6E2,0xA6E3,0xA6E4,0xA6E5,0xA78F,0xA7F7,0xA7FB,0xA7FC,0xA7FD,0xA7FE,0xA7FF,0xA800,0xA801,0xA803,0xA804,0xA805,0xA807,0xA808,0xA809,0xA80A,0xA80C,0xA80D,0xA80E,0xA80F,0xA810,0xA811,0xA812,0xA813,0xA814,0xA815,0xA816,0xA817,0xA818,0xA819,0xA81A,0xA81B,0xA81C,0xA81D,0xA81E,0xA81F,0xA820,0xA821,0xA822,0xA840,0xA841,0xA842,0xA843,0xA844,0xA845,0xA846,0xA847,0xA848,0xA849,0xA84A,0xA84B,0xA84C,0xA84D,0xA84E,0xA84F,0xA850,0xA851,0xA852,0xA853,0xA854,0xA855,0xA856,0xA857,0xA858,0xA859,0xA85A,0xA85B,0xA85C,0xA85D,0xA85E,0xA85F,0xA860,0xA861,0xA862,0xA863,0xA864,0xA865,0xA866,0xA867,0xA868,0xA869,0xA86A,0xA86B,0xA86C,0xA86D,0xA86E,0xA86F,0xA870,0xA871,0xA872,0xA873,0xA882,0xA883,0xA884,0xA885,0xA886,0xA887,0xA888,0xA889,0xA88A,0xA88B,0xA88C,0xA88D,0xA88E,0xA88F,0xA890,0xA891,0xA892,0xA893,0xA894,0xA895,0xA896,0xA897,0xA898,0xA899,0xA89A,0xA89B,0xA89C,0xA89D,0xA89E,0xA89F,0xA8A0,0xA8A1,0xA8A2,0xA8A3,0xA8A4,0xA8A5,0xA8A6,0xA8A7,0xA8A8,0xA8A9,0xA8AA,0xA8AB,0xA8AC,0xA8AD,0xA8AE,0xA8AF,0xA8B0,0xA8B1,0xA8B2,0xA8B3,0xA8F2,0xA8F3,0xA8F4,0xA8F5,0xA8F6,0xA8F7,0xA8FB,0xA8FD,0xA8FE,0xA90A,0xA90B,0xA90C,0xA90D,0xA90E,0xA90F,0xA910,0xA911,0xA912,0xA913,0xA914,0xA915,0xA916,0xA917,0xA918,0xA919,0xA91A,0xA91B,0xA91C,0xA91D,0xA91E,0xA91F,0xA920,0xA921,0xA922,0xA923,0xA924,0xA925,0xA930,0xA931,0xA932,0xA933,0xA934,0xA935,0xA936,0xA937,0xA938,0xA939,0xA93A,0xA93B,0xA93C,0xA93D,0xA93E,0xA93F,0xA940,0xA941,0xA942,0xA943,0xA944,0xA945,0xA946,0xA960,0xA961,0xA962,0xA963,0xA964,0xA965,0xA966,0xA967,0xA968,0xA969,0xA96A,0xA96B,0xA96C,0xA96D,0xA96E,0xA96F,0xA970,0xA971,0xA972,0xA973,0xA974,0xA975,0xA976,0xA977,0xA978,0xA979,0xA97A,0xA97B,0xA97C,0xA984,0xA985,0xA986,0xA987,0xA988,0xA989,0xA98A,0xA98B,0xA98C,0xA98D,0xA98E,0xA98F,0xA990,0xA991,0xA992,0xA993,0xA994,0xA995,0xA996,0xA997,0xA998,0xA999,0xA99A,0xA99B,0xA99C,0xA99D,0xA99E,0xA99F,0xA9A0,0xA9A1,0xA9A2,0xA9A3,0xA9A4,0xA9A5,0xA9A6,0xA9A7,0xA9A8,0xA9A9,0xA9AA,0xA9AB,0xA9AC,0xA9AD,0xA9AE,0xA9AF,0xA9B0,0xA9B1,0xA9B2,0xA9E0,0xA9E1,0xA9E2,0xA9E3,0xA9E4,0xA9E7,0xA9E8,0xA9E9,0xA9EA,0xA9EB,0xA9EC,0xA9ED,0xA9EE,0xA9EF,0xA9FA,0xA9FB,0xA9FC,0xA9FD,0xA9FE,0xAA00,0xAA01,0xAA02,0xAA03,0xAA04,0xAA05,0xAA06,0xAA07,0xAA08,0xAA09,0xAA0A,0xAA0B,0xAA0C,0xAA0D,0xAA0E,0xAA0F,0xAA10,0xAA11,0xAA12,0xAA13,0xAA14,0xAA15,0xAA16,0xAA17,0xAA18,0xAA19,0xAA1A,0xAA1B,0xAA1C,0xAA1D,0xAA1E,0xAA1F,0xAA20,0xAA21,0xAA22,0xAA23,0xAA24,0xAA25,0xAA26,0xAA27,0xAA28,0xAA40,0xAA41,0xAA42,0xAA44,0xAA45,0xAA46,0xAA47,0xAA48,0xAA49,0xAA4A,0xAA4B,0xAA60,0xAA61,0xAA62,0xAA63,0xAA64,0xAA65,0xAA66,0xAA67,0xAA68,0xAA69,0xAA6A,0xAA6B,0xAA6C,0xAA6D,0xAA6E,0xAA6F,0xAA71,0xAA72,0xAA73,0xAA74,0xAA75,0xAA76,0xAA7A,0xAA7E,0xAA7F,0xAA80,0xAA81,0xAA82,0xAA83,0xAA84,0xAA85,0xAA86,0xAA87,0xAA88,0xAA89,0xAA8A,0xAA8B,0xAA8C,0xAA8D,0xAA8E,0xAA8F,0xAA90,0xAA91,0xAA92,0xAA93,0xAA94,0xAA95,0xAA96,0xAA97,0xAA98,0xAA99,0xAA9A,0xAA9B,0xAA9C,0xAA9D,0xAA9E,0xAA9F,0xAAA0,0xAAA1,0xAAA2,0xAAA3,0xAAA4,0xAAA5,0xAAA6,0xAAA7,0xAAA8,0xAAA9,0xAAAA,0xAAAB,0xAAAC,0xAAAD,0xAAAE,0xAAAF,0xAAB1,0xAAB5,0xAAB6,0xAAB9,0xAABA,0xAABB,0xAABC,0xAABD,0xAAC0,0xAAC2,0xAADB,0xAADC,0xAAE0,0xAAE1,0xAAE2,0xAAE3,0xAAE4,0xAAE5,0xAAE6,0xAAE7,0xAAE8,0xAAE9,0xAAEA,0xAAF2,0xAB01,0xAB02,0xAB03,0xAB04,0xAB05,0xAB06,0xAB09,0xAB0A,0xAB0B,0xAB0C,0xAB0D,0xAB0E,0xAB11,0xAB12,0xAB13,0xAB14,0xAB15,0xAB16,0xAB20,0xAB21,0xAB22,0xAB23,0xAB24,0xAB25,0xAB26,0xAB28,0xAB29,0xAB2A,0xAB2B,0xAB2C,0xAB2D,0xAB2E,0xABC0,0xABC1,0xABC2,0xABC3,0xABC4,0xABC5,0xABC6,0xABC7,0xABC8,0xABC9,0xABCA,0xABCB,0xABCC,0xABCD,0xABCE,0xABCF,0xABD0,0xABD1,0xABD2,0xABD3,0xABD4,0xABD5,0xABD6,0xABD7,0xABD8,0xABD9,0xABDA,0xABDB,0xABDC,0xABDD,0xABDE,0xABDF,0xABE0,0xABE1,0xABE2,0xAC00,0xD7A3,0xD7B0,0xD7B1,0xD7B2,0xD7B3,0xD7B4,0xD7B5,0xD7B6,0xD7B7,0xD7B8,0xD7B9,0xD7BA,0xD7BB,0xD7BC,0xD7BD,0xD7BE,0xD7BF,0xD7C0,0xD7C1,0xD7C2,0xD7C3,0xD7C4,0xD7C5,0xD7C6,0xD7CB,0xD7CC,0xD7CD,0xD7CE,0xD7CF,0xD7D0,0xD7D1,0xD7D2,0xD7D3,0xD7D4,0xD7D5,0xD7D6,0xD7D7,0xD7D8,0xD7D9,0xD7DA,0xD7DB,0xD7DC,0xD7DD,0xD7DE,0xD7DF,0xD7E0,0xD7E1,0xD7E2,0xD7E3,0xD7E4,0xD7E5,0xD7E6,0xD7E7,0xD7E8,0xD7E9,0xD7EA,0xD7EB,0xD7EC,0xD7ED,0xD7EE,0xD7EF,0xD7F0,0xD7F1,0xD7F2,0xD7F3,0xD7F4,0xD7F5,0xD7F6,0xD7F7,0xD7F8,0xD7F9,0xD7FA,0xD7FB,0xF900,0xF901,0xF902,0xF903,0xF904,0xF905,0xF906,0xF907,0xF908,0xF909,0xF90A,0xF90B,0xF90C,0xF90D,0xF90E,0xF90F,0xF910,0xF911,0xF912,0xF913,0xF914,0xF915,0xF916,0xF917,0xF918,0xF919,0xF91A,0xF91B,0xF91C,0xF91D,0xF91E,0xF91F,0xF920,0xF921,0xF922,0xF923,0xF924,0xF925,0xF926,0xF927,0xF928,0xF929,0xF92A,0xF92B,0xF92C,0xF92D,0xF92E,0xF92F,0xF930,0xF931,0xF932,0xF933,0xF934,0xF935,0xF936,0xF937,0xF938,0xF939,0xF93A,0xF93B,0xF93C,0xF93D,0xF93E,0xF93F,0xF940,0xF941,0xF942,0xF943,0xF944,0xF945,0xF946,0xF947,0xF948,0xF949,0xF94A,0xF94B,0xF94C,0xF94D,0xF94E,0xF94F,0xF950,0xF951,0xF952,0xF953,0xF954,0xF955,0xF956,0xF957,0xF958,0xF959,0xF95A,0xF95B,0xF95C,0xF95D,0xF95E,0xF95F,0xF960,0xF961,0xF962,0xF963,0xF964,0xF965,0xF966,0xF967,0xF968,0xF969,0xF96A,0xF96B,0xF96C,0xF96D,0xF96E,0xF96F,0xF970,0xF971,0xF972,0xF973,0xF974,0xF975,0xF976,0xF977,0xF978,0xF979,0xF97A,0xF97B,0xF97C,0xF97D,0xF97E,0xF97F,0xF980,0xF981,0xF982,0xF983,0xF984,0xF985,0xF986,0xF987,0xF988,0xF989,0xF98A,0xF98B,0xF98C,0xF98D,0xF98E,0xF98F,0xF990,0xF991,0xF992,0xF993,0xF994,0xF995,0xF996,0xF997,0xF998,0xF999,0xF99A,0xF99B,0xF99C,0xF99D,0xF99E,0xF99F,0xF9A0,0xF9A1,0xF9A2,0xF9A3,0xF9A4,0xF9A5,0xF9A6,0xF9A7,0xF9A8,0xF9A9,0xF9AA,0xF9AB,0xF9AC,0xF9AD,0xF9AE,0xF9AF,0xF9B0,0xF9B1,0xF9B2,0xF9B3,0xF9B4,0xF9B5,0xF9B6,0xF9B7,0xF9B8,0xF9B9,0xF9BA,0xF9BB,0xF9BC,0xF9BD,0xF9BE,0xF9BF,0xF9C0,0xF9C1,0xF9C2,0xF9C3,0xF9C4,0xF9C5,0xF9C6,0xF9C7,0xF9C8,0xF9C9,0xF9CA,0xF9CB,0xF9CC,0xF9CD,0xF9CE,0xF9CF,0xF9D0,0xF9D1,0xF9D2,0xF9D3,0xF9D4,0xF9D5,0xF9D6,0xF9D7,0xF9D8,0xF9D9,0xF9DA,0xF9DB,0xF9DC,0xF9DD,0xF9DE,0xF9DF,0xF9E0,0xF9E1,0xF9E2,0xF9E3,0xF9E4,0xF9E5,0xF9E6,0xF9E7,0xF9E8,0xF9E9,0xF9EA,0xF9EB,0xF9EC,0xF9ED,0xF9EE,0xF9EF,0xF9F0,0xF9F1,0xF9F2,0xF9F3,0xF9F4,0xF9F5,0xF9F6,0xF9F7,0xF9F8,0xF9F9,0xF9FA,0xF9FB,0xF9FC,0xF9FD,0xF9FE,0xF9FF,0xFA00,0xFA01,0xFA02,0xFA03,0xFA04,0xFA05,0xFA06,0xFA07,0xFA08,0xFA09,0xFA0A,0xFA0B,0xFA0C,0xFA0D,0xFA0E,0xFA0F,0xFA10,0xFA11,0xFA12,0xFA13,0xFA14,0xFA15,0xFA16,0xFA17,0xFA18,0xFA19,0xFA1A,0xFA1B,0xFA1C,0xFA1D,0xFA1E,0xFA1F,0xFA20,0xFA21,0xFA22,0xFA23,0xFA24,0xFA25,0xFA26,0xFA27,0xFA28,0xFA29,0xFA2A,0xFA2B,0xFA2C,0xFA2D,0xFA2E,0xFA2F,0xFA30,0xFA31,0xFA32,0xFA33,0xFA34,0xFA35,0xFA36,0xFA37,0xFA38,0xFA39,0xFA3A,0xFA3B,0xFA3C,0xFA3D,0xFA3E,0xFA3F,0xFA40,0xFA41,0xFA42,0xFA43,0xFA44,0xFA45,0xFA46,0xFA47,0xFA48,0xFA49,0xFA4A,0xFA4B,0xFA4C,0xFA4D,0xFA4E,0xFA4F,0xFA50,0xFA51,0xFA52,0xFA53,0xFA54,0xFA55,0xFA56,0xFA57,0xFA58,0xFA59,0xFA5A,0xFA5B,0xFA5C,0xFA5D,0xFA5E,0xFA5F,0xFA60,0xFA61,0xFA62,0xFA63,0xFA64,0xFA65,0xFA66,0xFA67,0xFA68,0xFA69,0xFA6A,0xFA6B,0xFA6C,0xFA6D,0xFA70,0xFA71,0xFA72,0xFA73,0xFA74,0xFA75,0xFA76,0xFA77,0xFA78,0xFA79,0xFA7A,0xFA7B,0xFA7C,0xFA7D,0xFA7E,0xFA7F,0xFA80,0xFA81,0xFA82,0xFA83,0xFA84,0xFA85,0xFA86,0xFA87,0xFA88,0xFA89,0xFA8A,0xFA8B,0xFA8C,0xFA8D,0xFA8E,0xFA8F,0xFA90,0xFA91,0xFA92,0xFA93,0xFA94,0xFA95,0xFA96,0xFA97,0xFA98,0xFA99,0xFA9A,0xFA9B,0xFA9C,0xFA9D,0xFA9E,0xFA9F,0xFAA0,0xFAA1,0xFAA2,0xFAA3,0xFAA4,0xFAA5,0xFAA6,0xFAA7,0xFAA8,0xFAA9,0xFAAA,0xFAAB,0xFAAC,0xFAAD,0xFAAE,0xFAAF,0xFAB0,0xFAB1,0xFAB2,0xFAB3,0xFAB4,0xFAB5,0xFAB6,0xFAB7,0xFAB8,0xFAB9,0xFABA,0xFABB,0xFABC,0xFABD,0xFABE,0xFABF,0xFAC0,0xFAC1,0xFAC2,0xFAC3,0xFAC4,0xFAC5,0xFAC6,0xFAC7,0xFAC8,0xFAC9,0xFACA,0xFACB,0xFACC,0xFACD,0xFACE,0xFACF,0xFAD0,0xFAD1,0xFAD2,0xFAD3,0xFAD4,0xFAD5,0xFAD6,0xFAD7,0xFAD8,0xFAD9,0xFB1D,0xFB1F,0xFB20,0xFB21,0xFB22,0xFB23,0xFB24,0xFB25,0xFB26,0xFB27,0xFB28,0xFB2A,0xFB2B,0xFB2C,0xFB2D,0xFB2E,0xFB2F,0xFB30,0xFB31,0xFB32,0xFB33,0xFB34,0xFB35,0xFB36,0xFB38,0xFB39,0xFB3A,0xFB3B,0xFB3C,0xFB3E,0xFB40,0xFB41,0xFB43,0xFB44,0xFB46,0xFB47,0xFB48,0xFB49,0xFB4A,0xFB4B,0xFB4C,0xFB4D,0xFB4E,0xFB4F,0xFB50,0xFB51,0xFB52,0xFB53,0xFB54,0xFB55,0xFB56,0xFB57,0xFB58,0xFB59,0xFB5A,0xFB5B,0xFB5C,0xFB5D,0xFB5E,0xFB5F,0xFB60,0xFB61,0xFB62,0xFB63,0xFB64,0xFB65,0xFB66,0xFB67,0xFB68,0xFB69,0xFB6A,0xFB6B,0xFB6C,0xFB6D,0xFB6E,0xFB6F,0xFB70,0xFB71,0xFB72,0xFB73,0xFB74,0xFB75,0xFB76,0xFB77,0xFB78,0xFB79,0xFB7A,0xFB7B,0xFB7C,0xFB7D,0xFB7E,0xFB7F,0xFB80,0xFB81,0xFB82,0xFB83,0xFB84,0xFB85,0xFB86,0xFB87,0xFB88,0xFB89,0xFB8A,0xFB8B,0xFB8C,0xFB8D,0xFB8E,0xFB8F,0xFB90,0xFB91,0xFB92,0xFB93,0xFB94,0xFB95,0xFB96,0xFB97,0xFB98,0xFB99,0xFB9A,0xFB9B,0xFB9C,0xFB9D,0xFB9E,0xFB9F,0xFBA0,0xFBA1,0xFBA2,0xFBA3,0xFBA4,0xFBA5,0xFBA6,0xFBA7,0xFBA8,0xFBA9,0xFBAA,0xFBAB,0xFBAC,0xFBAD,0xFBAE,0xFBAF,0xFBB0,0xFBB1,0xFBD3,0xFBD4,0xFBD5,0xFBD6,0xFBD7,0xFBD8,0xFBD9,0xFBDA,0xFBDB,0xFBDC,0xFBDD,0xFBDE,0xFBDF,0xFBE0,0xFBE1,0xFBE2,0xFBE3,0xFBE4,0xFBE5,0xFBE6,0xFBE7,0xFBE8,0xFBE9,0xFBEA,0xFBEB,0xFBEC,0xFBED,0xFBEE,0xFBEF,0xFBF0,0xFBF1,0xFBF2,0xFBF3,0xFBF4,0xFBF5,0xFBF6,0xFBF7,0xFBF8,0xFBF9,0xFBFA,0xFBFB,0xFBFC,0xFBFD,0xFBFE,0xFBFF,0xFC00,0xFC01,0xFC02,0xFC03,0xFC04,0xFC05,0xFC06,0xFC07,0xFC08,0xFC09,0xFC0A,0xFC0B,0xFC0C,0xFC0D,0xFC0E,0xFC0F,0xFC10,0xFC11,0xFC12,0xFC13,0xFC14,0xFC15,0xFC16,0xFC17,0xFC18,0xFC19,0xFC1A,0xFC1B,0xFC1C,0xFC1D,0xFC1E,0xFC1F,0xFC20,0xFC21,0xFC22,0xFC23,0xFC24,0xFC25,0xFC26,0xFC27,0xFC28,0xFC29,0xFC2A,0xFC2B,0xFC2C,0xFC2D,0xFC2E,0xFC2F,0xFC30,0xFC31,0xFC32,0xFC33,0xFC34,0xFC35,0xFC36,0xFC37,0xFC38,0xFC39,0xFC3A,0xFC3B,0xFC3C,0xFC3D,0xFC3E,0xFC3F,0xFC40,0xFC41,0xFC42,0xFC43,0xFC44,0xFC45,0xFC46,0xFC47,0xFC48,0xFC49,0xFC4A,0xFC4B,0xFC4C,0xFC4D,0xFC4E,0xFC4F,0xFC50,0xFC51,0xFC52,0xFC53,0xFC54,0xFC55,0xFC56,0xFC57,0xFC58,0xFC59,0xFC5A,0xFC5B,0xFC5C,0xFC5D,0xFC5E,0xFC5F,0xFC60,0xFC61,0xFC62,0xFC63,0xFC64,0xFC65,0xFC66,0xFC67,0xFC68,0xFC69,0xFC6A,0xFC6B,0xFC6C,0xFC6D,0xFC6E,0xFC6F,0xFC70,0xFC71,0xFC72,0xFC73,0xFC74,0xFC75,0xFC76,0xFC77,0xFC78,0xFC79,0xFC7A,0xFC7B,0xFC7C,0xFC7D,0xFC7E,0xFC7F,0xFC80,0xFC81,0xFC82,0xFC83,0xFC84,0xFC85,0xFC86,0xFC87,0xFC88,0xFC89,0xFC8A,0xFC8B,0xFC8C,0xFC8D,0xFC8E,0xFC8F,0xFC90,0xFC91,0xFC92,0xFC93,0xFC94,0xFC95,0xFC96,0xFC97,0xFC98,0xFC99,0xFC9A,0xFC9B,0xFC9C,0xFC9D,0xFC9E,0xFC9F,0xFCA0,0xFCA1,0xFCA2,0xFCA3,0xFCA4,0xFCA5,0xFCA6,0xFCA7,0xFCA8,0xFCA9,0xFCAA,0xFCAB,0xFCAC,0xFCAD,0xFCAE,0xFCAF,0xFCB0,0xFCB1,0xFCB2,0xFCB3,0xFCB4,0xFCB5,0xFCB6,0xFCB7,0xFCB8,0xFCB9,0xFCBA,0xFCBB,0xFCBC,0xFCBD,0xFCBE,0xFCBF,0xFCC0,0xFCC1,0xFCC2,0xFCC3,0xFCC4,0xFCC5,0xFCC6,0xFCC7,0xFCC8,0xFCC9,0xFCCA,0xFCCB,0xFCCC,0xFCCD,0xFCCE,0xFCCF,0xFCD0,0xFCD1,0xFCD2,0xFCD3,0xFCD4,0xFCD5,0xFCD6,0xFCD7,0xFCD8,0xFCD9,0xFCDA,0xFCDB,0xFCDC,0xFCDD,0xFCDE,0xFCDF,0xFCE0,0xFCE1,0xFCE2,0xFCE3,0xFCE4,0xFCE5,0xFCE6,0xFCE7,0xFCE8,0xFCE9,0xFCEA,0xFCEB,0xFCEC,0xFCED,0xFCEE,0xFCEF,0xFCF0,0xFCF1,0xFCF2,0xFCF3,0xFCF4,0xFCF5,0xFCF6,0xFCF7,0xFCF8,0xFCF9,0xFCFA,0xFCFB,0xFCFC,0xFCFD,0xFCFE,0xFCFF,0xFD00,0xFD01,0xFD02,0xFD03,0xFD04,0xFD05,0xFD06,0xFD07,0xFD08,0xFD09,0xFD0A,0xFD0B,0xFD0C,0xFD0D,0xFD0E,0xFD0F,0xFD10,0xFD11,0xFD12,0xFD13,0xFD14,0xFD15,0xFD16,0xFD17,0xFD18,0xFD19,0xFD1A,0xFD1B,0xFD1C,0xFD1D,0xFD1E,0xFD1F,0xFD20,0xFD21,0xFD22,0xFD23,0xFD24,0xFD25,0xFD26,0xFD27,0xFD28,0xFD29,0xFD2A,0xFD2B,0xFD2C,0xFD2D,0xFD2E,0xFD2F,0xFD30,0xFD31,0xFD32,0xFD33,0xFD34,0xFD35,0xFD36,0xFD37,0xFD38,0xFD39,0xFD3A,0xFD3B,0xFD3C,0xFD3D,0xFD50,0xFD51,0xFD52,0xFD53,0xFD54,0xFD55,0xFD56,0xFD57,0xFD58,0xFD59,0xFD5A,0xFD5B,0xFD5C,0xFD5D,0xFD5E,0xFD5F,0xFD60,0xFD61,0xFD62,0xFD63,0xFD64,0xFD65,0xFD66,0xFD67,0xFD68,0xFD69,0xFD6A,0xFD6B,0xFD6C,0xFD6D,0xFD6E,0xFD6F,0xFD70,0xFD71,0xFD72,0xFD73,0xFD74,0xFD75,0xFD76,0xFD77,0xFD78,0xFD79,0xFD7A,0xFD7B,0xFD7C,0xFD7D,0xFD7E,0xFD7F,0xFD80,0xFD81,0xFD82,0xFD83,0xFD84,0xFD85,0xFD86,0xFD87,0xFD88,0xFD89,0xFD8A,0xFD8B,0xFD8C,0xFD8D,0xFD8E,0xFD8F,0xFD92,0xFD93,0xFD94,0xFD95,0xFD96,0xFD97,0xFD98,0xFD99,0xFD9A,0xFD9B,0xFD9C,0xFD9D,0xFD9E,0xFD9F,0xFDA0,0xFDA1,0xFDA2,0xFDA3,0xFDA4,0xFDA5,0xFDA6,0xFDA7,0xFDA8,0xFDA9,0xFDAA,0xFDAB,0xFDAC,0xFDAD,0xFDAE,0xFDAF,0xFDB0,0xFDB1,0xFDB2,0xFDB3,0xFDB4,0xFDB5,0xFDB6,0xFDB7,0xFDB8,0xFDB9,0xFDBA,0xFDBB,0xFDBC,0xFDBD,0xFDBE,0xFDBF,0xFDC0,0xFDC1,0xFDC2,0xFDC3,0xFDC4,0xFDC5,0xFDC6,0xFDC7,0xFDF0,0xFDF1,0xFDF2,0xFDF3,0xFDF4,0xFDF5,0xFDF6,0xFDF7,0xFDF8,0xFDF9,0xFDFA,0xFDFB,0xFE70,0xFE71,0xFE72,0xFE73,0xFE74,0xFE76,0xFE77,0xFE78,0xFE79,0xFE7A,0xFE7B,0xFE7C,0xFE7D,0xFE7E,0xFE7F,0xFE80,0xFE81,0xFE82,0xFE83,0xFE84,0xFE85,0xFE86,0xFE87,0xFE88,0xFE89,0xFE8A,0xFE8B,0xFE8C,0xFE8D,0xFE8E,0xFE8F,0xFE90,0xFE91,0xFE92,0xFE93,0xFE94,0xFE95,0xFE96,0xFE97,0xFE98,0xFE99,0xFE9A,0xFE9B,0xFE9C,0xFE9D,0xFE9E,0xFE9F,0xFEA0,0xFEA1,0xFEA2,0xFEA3,0xFEA4,0xFEA5,0xFEA6,0xFEA7,0xFEA8,0xFEA9,0xFEAA,0xFEAB,0xFEAC,0xFEAD,0xFEAE,0xFEAF,0xFEB0,0xFEB1,0xFEB2,0xFEB3,0xFEB4,0xFEB5,0xFEB6,0xFEB7,0xFEB8,0xFEB9,0xFEBA,0xFEBB,0xFEBC,0xFEBD,0xFEBE,0xFEBF,0xFEC0,0xFEC1,0xFEC2,0xFEC3,0xFEC4,0xFEC5,0xFEC6,0xFEC7,0xFEC8,0xFEC9,0xFECA,0xFECB,0xFECC,0xFECD,0xFECE,0xFECF,0xFED0,0xFED1,0xFED2,0xFED3,0xFED4,0xFED5,0xFED6,0xFED7,0xFED8,0xFED9,0xFEDA,0xFEDB,0xFEDC,0xFEDD,0xFEDE,0xFEDF,0xFEE0,0xFEE1,0xFEE2,0xFEE3,0xFEE4,0xFEE5,0xFEE6,0xFEE7,0xFEE8,0xFEE9,0xFEEA,0xFEEB,0xFEEC,0xFEED,0xFEEE,0xFEEF,0xFEF0,0xFEF1,0xFEF2,0xFEF3,0xFEF4,0xFEF5,0xFEF6,0xFEF7,0xFEF8,0xFEF9,0xFEFA,0xFEFB,0xFEFC,0xFF66,0xFF67,0xFF68,0xFF69,0xFF6A,0xFF6B,0xFF6C,0xFF6D,0xFF6E,0xFF6F,0xFF71,0xFF72,0xFF73,0xFF74,0xFF75,0xFF76,0xFF77,0xFF78,0xFF79,0xFF7A,0xFF7B,0xFF7C,0xFF7D,0xFF7E,0xFF7F,0xFF80,0xFF81,0xFF82,0xFF83,0xFF84,0xFF85,0xFF86,0xFF87,0xFF88,0xFF89,0xFF8A,0xFF8B,0xFF8C,0xFF8D,0xFF8E,0xFF8F,0xFF90,0xFF91,0xFF92,0xFF93,0xFF94,0xFF95,0xFF96,0xFF97,0xFF98,0xFF99,0xFF9A,0xFF9B,0xFF9C,0xFF9D,0xFFA0,0xFFA1,0xFFA2,0xFFA3,0xFFA4,0xFFA5,0xFFA6,0xFFA7,0xFFA8,0xFFA9,0xFFAA,0xFFAB,0xFFAC,0xFFAD,0xFFAE,0xFFAF,0xFFB0,0xFFB1,0xFFB2,0xFFB3,0xFFB4,0xFFB5,0xFFB6,0xFFB7,0xFFB8,0xFFB9,0xFFBA,0xFFBB,0xFFBC,0xFFBD,0xFFBE,0xFFC2,0xFFC3,0xFFC4,0xFFC5,0xFFC6,0xFFC7,0xFFCA,0xFFCB,0xFFCC,0xFFCD,0xFFCE,0xFFCF,0xFFD2,0xFFD3,0xFFD4,0xFFD5,0xFFD6,0xFFD7,0xFFDA,0xFFDB,0xFFDC};

size_t unicode_len_Lo = sizeof(unicode_Lo)/sizeof(unicode_Lo[0]);

int unicode_L[] = {-1};
size_t unicode_len_L = sizeof(unicode_L)/sizeof(unicode_L[0]);

int unicode_Mn[] = {0x0300,0x0301,0x0302,0x0303,0x0304,0x0305,0x0306,0x0307,0x0308,0x0309,0x030A,0x030B,0x030C,0x030D,0x030E,0x030F,0x0310,0x0311,0x0312,0x0313,0x0314,0x0315,0x0316,0x0317,0x0318,0x0319,0x031A,0x031B,0x031C,0x031D,0x031E,0x031F,0x0320,0x0321,0x0322,0x0323,0x0324,0x0325,0x0326,0x0327,0x0328,0x0329,0x032A,0x032B,0x032C,0x032D,0x032E,0x032F,0x0330,0x0331,0x0332,0x0333,0x0334,0x0335,0x0336,0x0337,0x0338,0x0339,0x033A,0x033B,0x033C,0x033D,0x033E,0x033F,0x0340,0x0341,0x0342,0x0343,0x0344,0x0345,0x0346,0x0347,0x0348,0x0349,0x034A,0x034B,0x034C,0x034D,0x034E,0x034F,0x0350,0x0351,0x0352,0x0353,0x0354,0x0355,0x0356,0x0357,0x0358,0x0359,0x035A,0x035B,0x035C,0x035D,0x035E,0x035F,0x0360,0x0361,0x0362,0x0363,0x0364,0x0365,0x0366,0x0367,0x0368,0x0369,0x036A,0x036B,0x036C,0x036D,0x036E,0x036F,0x0483,0x0484,0x0485,0x0486,0x0487,0x0591,0x0592,0x0593,0x0594,0x0595,0x0596,0x0597,0x0598,0x0599,0x059A,0x059B,0x059C,0x059D,0x059E,0x059F,0x05A0,0x05A1,0x05A2,0x05A3,0x05A4,0x05A5,0x05A6,0x05A7,0x05A8,0x05A9,0x05AA,0x05AB,0x05AC,0x05AD,0x05AE,0x05AF,0x05B0,0x05B1,0x05B2,0x05B3,0x05B4,0x05B5,0x05B6,0x05B7,0x05B8,0x05B9,0x05BA,0x05BB,0x05BC,0x05BD,0x05BF,0x05C1,0x05C2,0x05C4,0x05C5,0x05C7,0x0610,0x0611,0x0612,0x0613,0x0614,0x0615,0x0616,0x0617,0x0618,0x0619,0x061A,0x064B,0x064C,0x064D,0x064E,0x064F,0x0650,0x0651,0x0652,0x0653,0x0654,0x0655,0x0656,0x0657,0x0658,0x0659,0x065A,0x065B,0x065C,0x065D,0x065E,0x065F,0x0670,0x06D6,0x06D7,0x06D8,0x06D9,0x06DA,0x06DB,0x06DC,0x06DF,0x06E0,0x06E1,0x06E2,0x06E3,0x06E4,0x06E7,0x06E8,0x06EA,0x06EB,0x06EC,0x06ED,0x0711,0x0730,0x0731,0x0732,0x0733,0x0734,0x0735,0x0736,0x0737,0x0738,0x0739,0x073A,0x073B,0x073C,0x073D,0x073E,0x073F,0x0740,0x0741,0x0742,0x0743,0x0744,0x0745,0x0746,0x0747,0x0748,0x0749,0x074A,0x07A6,0x07A7,0x07A8,0x07A9,0x07AA,0x07AB,0x07AC,0x07AD,0x07AE,0x07AF,0x07B0,0x07EB,0x07EC,0x07ED,0x07EE,0x07EF,0x07F0,0x07F1,0x07F2,0x07F3,0x07FD,0x0816,0x0817,0x0818,0x0819,0x081B,0x081C,0x081D,0x081E,0x081F,0x0820,0x0821,0x0822,0x0823,0x0825,0x0826,0x0827,0x0829,0x082A,0x082B,0x082C,0x082D,0x0859,0x085A,0x085B,0x0897,0x0898,0x0899,0x089A,0x089B,0x089C,0x089D,0x089E,0x089F,0x08CA,0x08CB,0x08CC,0x08CD,0x08CE,0x08CF,0x08D0,0x08D1,0x08D2,0x08D3,0x08D4,0x08D5,0x08D6,0x08D7,0x08D8,0x08D9,0x08DA,0x08DB,0x08DC,0x08DD,0x08DE,0x08DF,0x08E0,0x08E1,0x08E3,0x08E4,0x08E5,0x08E6,0x08E7,0x08E8,0x08E9,0x08EA,0x08EB,0x08EC,0x08ED,0x08EE,0x08EF,0x08F0,0x08F1,0x08F2,0x08F3,0x08F4,0x08F5,0x08F6,0x08F7,0x08F8,0x08F9,0x08FA,0x08FB,0x08FC,0x08FD,0x08FE,0x08FF,0x0900,0x0901,0x0902,0x093A,0x093C,0x0941,0x0942,0x0943,0x0944,0x0945,0x0946,0x0947,0x0948,0x094D,0x0951,0x0952,0x0953,0x0954,0x0955,0x0956,0x0957,0x0962,0x0963,0x0981,0x09BC,0x09C1,0x09C2,0x09C3,0x09C4,0x09CD,0x09E2,0x09E3,0x09FE,0x0A01,0x0A02,0x0A3C,0x0A41,0x0A42,0x0A47,0x0A48,0x0A4B,0x0A4C,0x0A4D,0x0A51,0x0A70,0x0A71,0x0A75,0x0A81,0x0A82,0x0ABC,0x0AC1,0x0AC2,0x0AC3,0x0AC4,0x0AC5,0x0AC7,0x0AC8,0x0ACD,0x0AE2,0x0AE3,0x0AFA,0x0AFB,0x0AFC,0x0AFD,0x0AFE,0x0AFF,0x0B01,0x0B3C,0x0B3F,0x0B41,0x0B42,0x0B43,0x0B44,0x0B4D,0x0B55,0x0B56,0x0B62,0x0B63,0x0B82,0x0BC0,0x0BCD,0x0C00,0x0C04,0x0C3C,0x0C3E,0x0C3F,0x0C40,0x0C46,0x0C47,0x0C48,0x0C4A,0x0C4B,0x0C4C,0x0C4D,0x0C55,0x0C56,0x0C62,0x0C63,0x0C81,0x0CBC,0x0CBF,0x0CC6,0x0CCC,0x0CCD,0x0CE2,0x0CE3,0x0D00,0x0D01,0x0D3B,0x0D3C,0x0D41,0x0D42,0x0D43,0x0D44,0x0D4D,0x0D62,0x0D63,0x0D81,0x0DCA,0x0DD2,0x0DD3,0x0DD4,0x0DD6,0x0E31,0x0E34,0x0E35,0x0E36,0x0E37,0x0E38,0x0E39,0x0E3A,0x0E47,0x0E48,0x0E49,0x0E4A,0x0E4B,0x0E4C,0x0E4D,0x0E4E,0x0EB1,0x0EB4,0x0EB5,0x0EB6,0x0EB7,0x0EB8,0x0EB9,0x0EBA,0x0EBB,0x0EBC,0x0EC8,0x0EC9,0x0ECA,0x0ECB,0x0ECC,0x0ECD,0x0ECE,0x0F18,0x0F19,0x0F35,0x0F37,0x0F39,0x0F71,0x0F72,0x0F73,0x0F74,0x0F75,0x0F76,0x0F77,0x0F78,0x0F79,0x0F7A,0x0F7B,0x0F7C,0x0F7D,0x0F7E,0x0F80,0x0F81,0x0F82,0x0F83,0x0F84,0x0F86,0x0F87,0x0F8D,0x0F8E,0x0F8F,0x0F90,0x0F91,0x0F92,0x0F93,0x0F94,0x0F95,0x0F96,0x0F97,0x0F99,0x0F9A,0x0F9B,0x0F9C,0x0F9D,0x0F9E,0x0F9F,0x0FA0,0x0FA1,0x0FA2,0x0FA3,0x0FA4,0x0FA5,0x0FA6,0x0FA7,0x0FA8,0x0FA9,0x0FAA,0x0FAB,0x0FAC,0x0FAD,0x0FAE,0x0FAF,0x0FB0,0x0FB1,0x0FB2,0x0FB3,0x0FB4,0x0FB5,0x0FB6,0x0FB7,0x0FB8,0x0FB9,0x0FBA,0x0FBB,0x0FBC,0x0FC6,0x101F,0x102D,0x102E,0x102E,0x102F,0x1030,0x1032,0x1033,0x1034,0x1035,0x1036,0x1037,0x1037,0x1037,0x1037,0x1037,0x1037,0x1039,0x103A,0x103D,0x103E,0x1058,0x1059,0x105E,0x105F,0x1060,0x1071,0x1072,0x1073,0x1074,0x1082,0x1085,0x1086,0x108D,0x109D,0x10A0,0x10A0,0x10A0,0x10A0,0x10A0,0x10A0,0x10A0,0x10A0,0x10A0,0x10A3,0x10A3,0x10A3,0x10A3,0x10AE,0x10AE,0x10D2,0x10D2,0x10D2,0x10D2,0x10D6,0x10D6,0x10D6,0x10D6,0x10D6,0x10EA,0x10EA,0x10EF,0x10EF,0x10EF,0x10EF,0x10F4,0x10F4,0x10F4,0x10F4,0x10F4,0x10F4,0x10F4,0x10F4,0x10F4,0x10F4,0x10F5,0x10F8,0x10F8,0x10F8,0x10F8,0x1100,0x1103,0x1103,0x1103,0x1103,0x1103,0x1103,0x1103,0x1103,0x1104,0x1104,0x1104,0x1104,0x1104,0x1104,0x1104,0x1107,0x1107,0x1107,0x1107,0x1108,0x1108,0x110B,0x110B,0x110B,0x110B,0x110B,0x110B,0x110C,0x1110,0x1110,0x1110,0x1112,0x1112,0x1112,0x1112,0x1112,0x1112,0x1112,0x1112,0x1113,0x1113,0x1113,0x1113,0x1113,0x1117,0x1118,0x1118,0x111B,0x111B,0x111B,0x111B,0x111B,0x111B,0x111B,0x111B,0x111B,0x111C,0x111C,0x111C,0x111C,0x111C,0x1122,0x1123,0x1123,0x1123,0x1123,0x1123,0x1123,0x1124,0x112D,0x112E,0x112E,0x112E,0x112E,0x112E,0x112E,0x112E,0x112E,0x1130,0x1130,0x1133,0x1133,0x1134,0x1136,0x1136,0x1136,0x1136,0x1136,0x1136,0x1136,0x1137,0x1137,0x1137,0x1137,0x1137,0x113B,0x113B,0x113B,0x113B,0x113B,0x113C,0x113C,0x113D,0x113D,0x113E,0x113E,0x1143,0x1143,0x1143,0x1143,0x1143,0x1143,0x1143,0x1143,0x1144,0x1144,0x1144,0x1144,0x1145,0x114B,0x114B,0x114B,0x114B,0x114B,0x114B,0x114B,0x114B,0x114C,0x114C,0x114C,0x115B,0x115B,0x115B,0x115B,0x115B,0x115B,0x115B,0x115C,0x115D,0x115D,0x1163,0x1163,0x1163,0x1163,0x1163,0x1163,0x1163,0x1163,0x1163,0x1163,0x1164,0x116A,0x116A,0x116B,0x116B,0x116B,0x116B,0x116B,0x116B,0x116B,0x1171,0x1171,0x1172,0x1172,0x1172,0x1172,0x1172,0x1172,0x1172,0x1172,0x1172,0x1182,0x1183,0x1183,0x1183,0x1183,0x1183,0x1183,0x1183,0x1183,0x1183,0x1183,0x1193,0x1193,0x1193,0x1194,0x119D,0x119D,0x119D,0x119D,0x119D,0x119D,0x119E,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A0,0x11A3,0x11A3,0x11A3,0x11A3,0x11A3,0x11A3,0x11A3,0x11A3,0x11A3,0x11A3,0x11A4,0x11A5,0x11A5,0x11A5,0x11A5,0x11A5,0x11A5,0x11A5,0x11A5,0x11A5,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A8,0x11A9,0x11A9,0x11A9,0x11A9,0x11A9,0x11A9,0x11A9,0x11A9,0x11A9,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C3,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11C9,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CA,0x11CB,0x11CB,0x11CB,0x11CB,0x11CB,0x11D3,0x11D3,0x11D3,0x11D3,0x11D3,0x11D3,0x11D3,0x11D3,0x11D3,0x11D3,0x11D4,0x11D4,0x11D4,0x11D4,0x11D4,0x11D4,0x11D4,0x11D9,0x11D9,0x11D9,0x11D9,0x11EF,0x11EF,0x11F0,0x11F0,0x11F3,0x11F3,0x11F3,0x11F3,0x11F3,0x11F4,0x11F4,0x11F5,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1344,0x1345,0x1345,0x1345,0x1345,0x1345,0x1345,0x135D,0x135E,0x135F,0x1611,0x1611,0x1612,0x1612,0x1612,0x1612,0x1612,0x1612,0x1612,0x1612,0x1612,0x1612,0x1612,0x1612,0x1612,0x16AF,0x16AF,0x16AF,0x16AF,0x16AF,0x16B3,0x16B3,0x16B3,0x16B3,0x16B3,0x16B3,0x16B3,0x16F4,0x16F8,0x16F9,0x16F9,0x16F9,0x16FE,0x1712,0x1713,0x1714,0x1732,0x1733,0x1752,0x1753,0x1772,0x1773,0x17B4,0x17B5,0x17B7,0x17B8,0x17B9,0x17BA,0x17BB,0x17BC,0x17BD,0x17C6,0x17C9,0x17CA,0x17CB,0x17CC,0x17CD,0x17CE,0x17CF,0x17D0,0x17D1,0x17D2,0x17D3,0x17DD,0x180B,0x180C,0x180D,0x180F,0x1885,0x1886,0x18A9,0x1920,0x1921,0x1922,0x1927,0x1928,0x1932,0x1939,0x193A,0x193B,0x1A17,0x1A18,0x1A1B,0x1A56,0x1A58,0x1A59,0x1A5A,0x1A5B,0x1A5C,0x1A5D,0x1A5E,0x1A60,0x1A62,0x1A65,0x1A66,0x1A67,0x1A68,0x1A69,0x1A6A,0x1A6B,0x1A6C,0x1A73,0x1A74,0x1A75,0x1A76,0x1A77,0x1A78,0x1A79,0x1A7A,0x1A7B,0x1A7C,0x1A7F,0x1AB0,0x1AB1,0x1AB2,0x1AB3,0x1AB4,0x1AB5,0x1AB6,0x1AB7,0x1AB8,0x1AB9,0x1ABA,0x1ABB,0x1ABC,0x1ABD,0x1ABF,0x1AC0,0x1AC1,0x1AC2,0x1AC3,0x1AC4,0x1AC5,0x1AC6,0x1AC7,0x1AC8,0x1AC9,0x1ACA,0x1ACB,0x1ACC,0x1ACD,0x1ACE,0x1B00,0x1B01,0x1B02,0x1B03,0x1B34,0x1B36,0x1B37,0x1B38,0x1B39,0x1B3A,0x1B3C,0x1B42,0x1B6B,0x1B6C,0x1B6D,0x1B6E,0x1B6F,0x1B70,0x1B71,0x1B72,0x1B73,0x1B80,0x1B81,0x1BA2,0x1BA3,0x1BA4,0x1BA5,0x1BA8,0x1BA9,0x1BAB,0x1BAC,0x1BAD,0x1BC9,0x1BC9,0x1BE6,0x1BE8,0x1BE9,0x1BED,0x1BEF,0x1BF0,0x1BF1,0x1C2C,0x1C2D,0x1C2E,0x1C2F,0x1C30,0x1C31,0x1C32,0x1C33,0x1C36,0x1C37,0x1CD0,0x1CD1,0x1CD2,0x1CD4,0x1CD5,0x1CD6,0x1CD7,0x1CD8,0x1CD9,0x1CDA,0x1CDB,0x1CDC,0x1CDD,0x1CDE,0x1CDF,0x1CE0,0x1CE2,0x1CE3,0x1CE4,0x1CE5,0x1CE6,0x1CE7,0x1CE8,0x1CED,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF0,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF1,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF2,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF3,0x1CF4,0x1CF4,0x1CF4,0x1CF4,0x1CF4,0x1CF4,0x1CF4,0x1CF4,0x1CF8,0x1CF9,0x1D16,0x1D16,0x1D16,0x1D17,0x1D17,0x1D17,0x1D17,0x1D17,0x1D18,0x1D18,0x1D18,0x1D18,0x1D18,0x1D18,0x1D18,0x1D18,0x1D18,0x1D18,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D24,0x1D24,0x1D24,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA0,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA1,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA2,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA4,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA5,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA6,0x1DA7,0x1DA8,0x1DA9,0x1DA9,0x1DA9,0x1DA9,0x1DA9,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DAA,0x1DC0,0x1DC1,0x1DC2,0x1DC3,0x1DC4,0x1DC5,0x1DC6,0x1DC7,0x1DC8,0x1DC9,0x1DCA,0x1DCB,0x1DCC,0x1DCD,0x1DCE,0x1DCF,0x1DD0,0x1DD1,0x1DD2,0x1DD3,0x1DD4,0x1DD5,0x1DD6,0x1DD7,0x1DD8,0x1DD9,0x1DDA,0x1DDB,0x1DDC,0x1DDD,0x1DDE,0x1DDF,0x1DE0,0x1DE1,0x1DE2,0x1DE3,0x1DE4,0x1DE5,0x1DE6,0x1DE7,0x1DE8,0x1DE9,0x1DEA,0x1DEB,0x1DEC,0x1DED,0x1DEE,0x1DEF,0x1DF0,0x1DF1,0x1DF2,0x1DF3,0x1DF4,0x1DF5,0x1DF6,0x1DF7,0x1DF8,0x1DF9,0x1DFA,0x1DFB,0x1DFC,0x1DFD,0x1DFE,0x1DFF,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E00,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E01,0x1E02,0x1E02,0x1E02,0x1E02,0x1E02,0x1E02,0x1E02,0x1E02,0x1E02,0x1E08,0x1E13,0x1E13,0x1E13,0x1E13,0x1E13,0x1E13,0x1E13,0x1E2A,0x1E2E,0x1E2E,0x1E2E,0x1E2E,0x1E4E,0x1E4E,0x1E4E,0x1E4E,0x1E5E,0x1E5E,0x1E8D,0x1E8D,0x1E8D,0x1E8D,0x1E8D,0x1E8D,0x1E8D,0x1E94,0x1E94,0x1E94,0x1E94,0x1E94,0x1E94,0x1E94,0x20D0,0x20D1,0x20D2,0x20D3,0x20D4,0x20D5,0x20D6,0x20D7,0x20D8,0x20D9,0x20DA,0x20DB,0x20DC,0x20E1,0x20E5,0x20E6,0x20E7,0x20E8,0x20E9,0x20EA,0x20EB,0x20EC,0x20ED,0x20EE,0x20EF,0x20F0,0x2CEF,0x2CF0,0x2CF1,0x2D7F,0x2DE0,0x2DE1,0x2DE2,0x2DE3,0x2DE4,0x2DE5,0x2DE6,0x2DE7,0x2DE8,0x2DE9,0x2DEA,0x2DEB,0x2DEC,0x2DED,0x2DEE,0x2DEF,0x2DF0,0x2DF1,0x2DF2,0x2DF3,0x2DF4,0x2DF5,0x2DF6,0x2DF7,0x2DF8,0x2DF9,0x2DFA,0x2DFB,0x2DFC,0x2DFD,0x2DFE,0x2DFF,0x302A,0x302B,0x302C,0x302D,0x3099,0x309A,0xA66F,0xA674,0xA675,0xA676,0xA677,0xA678,0xA679,0xA67A,0xA67B,0xA67C,0xA67D,0xA69E,0xA69F,0xA6F0,0xA6F1,0xA802,0xA806,0xA80B,0xA825,0xA826,0xA82C,0xA8C4,0xA8C5,0xA8E0,0xA8E1,0xA8E2,0xA8E3,0xA8E4,0xA8E5,0xA8E6,0xA8E7,0xA8E8,0xA8E9,0xA8EA,0xA8EB,0xA8EC,0xA8ED,0xA8EE,0xA8EF,0xA8F0,0xA8F1,0xA8FF,0xA926,0xA927,0xA928,0xA929,0xA92A,0xA92B,0xA92C,0xA92D,0xA947,0xA948,0xA949,0xA94A,0xA94B,0xA94C,0xA94D,0xA94E,0xA94F,0xA950,0xA951,0xA980,0xA981,0xA982,0xA9B3,0xA9B6,0xA9B7,0xA9B8,0xA9B9,0xA9BC,0xA9BD,0xA9E5,0xAA29,0xAA2A,0xAA2B,0xAA2C,0xAA2D,0xAA2E,0xAA31,0xAA32,0xAA35,0xAA36,0xAA43,0xAA4C,0xAA7C,0xAAB0,0xAAB2,0xAAB3,0xAAB4,0xAAB7,0xAAB8,0xAABE,0xAABF,0xAAC1,0xAAEC,0xAAED,0xAAF6,0xABE5,0xABE8,0xABED,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE010,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE011,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE012,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE013,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE014,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE015,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE016,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE017,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE018,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE019,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01A,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01B,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01C,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01D,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xE01E,0xFB1E,0xFE00,0xFE01,0xFE02,0xFE03,0xFE04,0xFE05,0xFE06,0xFE07,0xFE08,0xFE09,0xFE0A,0xFE0B,0xFE0C,0xFE0D,0xFE0E,0xFE0F,0xFE20,0xFE21,0xFE22,0xFE23,0xFE24,0xFE25,0xFE26,0xFE27,0xFE28,0xFE29,0xFE2A,0xFE2B,0xFE2C,0xFE2D,0xFE2E,0xFE2F};

size_t unicode_len_Mn = sizeof(unicode_Mn)/sizeof(unicode_Mn[0]);

int unicode_Mc[] = {0x0903,0x093B,0x093E,0x093F,0x0940,0x0949,0x094A,0x094B,0x094C,0x094E,0x094F,0x0982,0x0983,0x09BE,0x09BF,0x09C0,0x09C7,0x09C8,0x09CB,0x09CC,0x09D7,0x0A03,0x0A3E,0x0A3F,0x0A40,0x0A83,0x0ABE,0x0ABF,0x0AC0,0x0AC9,0x0ACB,0x0ACC,0x0B02,0x0B03,0x0B3E,0x0B40,0x0B47,0x0B48,0x0B4B,0x0B4C,0x0B57,0x0BBE,0x0BBF,0x0BC1,0x0BC2,0x0BC6,0x0BC7,0x0BC8,0x0BCA,0x0BCB,0x0BCC,0x0BD7,0x0C01,0x0C02,0x0C03,0x0C41,0x0C42,0x0C43,0x0C44,0x0C82,0x0C83,0x0CBE,0x0CC0,0x0CC1,0x0CC2,0x0CC3,0x0CC4,0x0CC7,0x0CC8,0x0CCA,0x0CCB,0x0CD5,0x0CD6,0x0CF3,0x0D02,0x0D03,0x0D3E,0x0D3F,0x0D40,0x0D46,0x0D47,0x0D48,0x0D4A,0x0D4B,0x0D4C,0x0D57,0x0D82,0x0D83,0x0DCF,0x0DD0,0x0DD1,0x0DD8,0x0DD9,0x0DDA,0x0DDB,0x0DDC,0x0DDD,0x0DDE,0x0DDF,0x0DF2,0x0DF3,0x0F3E,0x0F3F,0x0F7F,0x102B,0x102C,0x1031,0x1038,0x103B,0x103C,0x1056,0x1057,0x1062,0x1063,0x1064,0x1067,0x1068,0x1069,0x106A,0x106B,0x106C,0x106D,0x1083,0x1084,0x1087,0x1088,0x1089,0x108A,0x108B,0x108C,0x108F,0x109A,0x109B,0x109C,0x1100,0x1100,0x1108,0x110B,0x110B,0x110B,0x110B,0x110B,0x1112,0x1114,0x1114,0x1118,0x111B,0x111B,0x111B,0x111B,0x111C,0x111C,0x1122,0x1122,0x1122,0x1123,0x1123,0x1123,0x112E,0x112E,0x112E,0x1130,0x1130,0x1133,0x1133,0x1134,0x1134,0x1134,0x1134,0x1134,0x1134,0x1134,0x1134,0x1134,0x1135,0x1136,0x1136,0x113B,0x113B,0x113B,0x113C,0x113C,0x113C,0x113C,0x113C,0x113C,0x113C,0x113C,0x113C,0x1143,0x1143,0x1143,0x1144,0x1144,0x1144,0x114B,0x114B,0x114B,0x114B,0x114B,0x114B,0x114B,0x114B,0x114C,0x115A,0x115B,0x115B,0x115B,0x115B,0x115B,0x115B,0x115B,0x1163,0x1163,0x1163,0x1163,0x1163,0x1163,0x116A,0x116A,0x116A,0x116B,0x1171,0x1172,0x1172,0x1172,0x1182,0x1182,0x1182,0x1183,0x1193,0x1193,0x1193,0x1193,0x1193,0x1193,0x1193,0x1193,0x1193,0x1194,0x1194,0x119D,0x119D,0x119D,0x119D,0x119D,0x119D,0x119D,0x119E,0x11A3,0x11A5,0x11A5,0x11A9,0x11C2,0x11C3,0x11CA,0x11CB,0x11CB,0x11D8,0x11D8,0x11D8,0x11D8,0x11D8,0x11D9,0x11D9,0x11D9,0x11EF,0x11EF,0x11F0,0x11F3,0x11F3,0x11F3,0x11F3,0x11F4,0x1612,0x1612,0x1612,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F5,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F6,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F7,0x16F8,0x16F8,0x16F8,0x16F8,0x16F8,0x16F8,0x16F8,0x16F8,0x16FF,0x16FF,0x1715,0x1734,0x17B6,0x17BE,0x17BF,0x17C0,0x17C1,0x17C2,0x17C3,0x17C4,0x17C5,0x17C7,0x17C8,0x1923,0x1924,0x1925,0x1926,0x1929,0x192A,0x192B,0x1930,0x1931,0x1933,0x1934,0x1935,0x1936,0x1937,0x1938,0x1A19,0x1A1A,0x1A55,0x1A57,0x1A61,0x1A63,0x1A64,0x1A6D,0x1A6E,0x1A6F,0x1A70,0x1A71,0x1A72,0x1B04,0x1B35,0x1B3B,0x1B3D,0x1B3E,0x1B3F,0x1B40,0x1B41,0x1B43,0x1B44,0x1B82,0x1BA1,0x1BA6,0x1BA7,0x1BAA,0x1BE7,0x1BEA,0x1BEB,0x1BEC,0x1BEE,0x1BF2,0x1BF3,0x1C24,0x1C25,0x1C26,0x1C27,0x1C28,0x1C29,0x1C2A,0x1C2B,0x1C34,0x1C35,0x1CE1,0x1CF7,0x1D16,0x1D16,0x1D16,0x1D16,0x1D16,0x1D17,0x1D17,0x1D17,0x302E,0x302F,0xA823,0xA824,0xA827,0xA880,0xA881,0xA8B4,0xA8B5,0xA8B6,0xA8B7,0xA8B8,0xA8B9,0xA8BA,0xA8BB,0xA8BC,0xA8BD,0xA8BE,0xA8BF,0xA8C0,0xA8C1,0xA8C2,0xA8C3,0xA952,0xA953,0xA983,0xA9B4,0xA9B5,0xA9BA,0xA9BB,0xA9BE,0xA9BF,0xA9C0,0xAA2F,0xAA30,0xAA33,0xAA34,0xAA4D,0xAA7B,0xAA7D,0xAAEB,0xAAEE,0xAAEF,0xAAF5,0xABE3,0xABE4,0xABE6,0xABE7,0xABE9,0xABEA,0xABEC};

size_t unicode_len_Mc = sizeof(unicode_Mc)/sizeof(unicode_Mc[0]);

int unicode_Me[] = {0x0488,0x0489,0x1ABE,0x20DD,0x20DE,0x20DF,0x20E0,0x20E2,0x20E3,0x20E4,0xA670,0xA671,0xA672};

size_t unicode_len_Me = sizeof(unicode_Me)/sizeof(unicode_Me[0]);

int unicode_M[] = {-1};
size_t unicode_len_M = sizeof(unicode_M)/sizeof(unicode_M[0]);

int unicode_Nd[] = {0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x0660,0x0661,0x0662,0x0663,0x0664,0x0665,0x0666,0x0667,0x0668,0x0669,0x06F0,0x06F1,0x06F2,0x06F3,0x06F4,0x06F5,0x06F6,0x06F7,0x06F8,0x06F9,0x07C0,0x07C1,0x07C2,0x07C3,0x07C4,0x07C5,0x07C6,0x07C7,0x07C8,0x07C9,0x0966,0x0967,0x0968,0x0969,0x096A,0x096B,0x096C,0x096D,0x096E,0x096F,0x09E6,0x09E7,0x09E8,0x09E9,0x09EA,0x09EB,0x09EC,0x09ED,0x09EE,0x09EF,0x0A66,0x0A67,0x0A68,0x0A69,0x0A6A,0x0A6B,0x0A6C,0x0A6D,0x0A6E,0x0A6F,0x0AE6,0x0AE7,0x0AE8,0x0AE9,0x0AEA,0x0AEB,0x0AEC,0x0AED,0x0AEE,0x0AEF,0x0B66,0x0B67,0x0B68,0x0B69,0x0B6A,0x0B6B,0x0B6C,0x0B6D,0x0B6E,0x0B6F,0x0BE6,0x0BE7,0x0BE8,0x0BE9,0x0BEA,0x0BEB,0x0BEC,0x0BED,0x0BEE,0x0BEF,0x0C66,0x0C67,0x0C68,0x0C69,0x0C6A,0x0C6B,0x0C6C,0x0C6D,0x0C6E,0x0C6F,0x0CE6,0x0CE7,0x0CE8,0x0CE9,0x0CEA,0x0CEB,0x0CEC,0x0CED,0x0CEE,0x0CEF,0x0D66,0x0D67,0x0D68,0x0D69,0x0D6A,0x0D6B,0x0D6C,0x0D6D,0x0D6E,0x0D6F,0x0DE6,0x0DE7,0x0DE8,0x0DE9,0x0DEA,0x0DEB,0x0DEC,0x0DED,0x0DEE,0x0DEF,0x0E50,0x0E51,0x0E52,0x0E53,0x0E54,0x0E55,0x0E56,0x0E57,0x0E58,0x0E59,0x0ED0,0x0ED1,0x0ED2,0x0ED3,0x0ED4,0x0ED5,0x0ED6,0x0ED7,0x0ED8,0x0ED9,0x0F20,0x0F21,0x0F22,0x0F23,0x0F24,0x0F25,0x0F26,0x0F27,0x0F28,0x0F29,0x1040,0x1041,0x1042,0x1043,0x1044,0x1045,0x1046,0x1047,0x1048,0x1049,0x104A,0x104A,0x104A,0x104A,0x104A,0x104A,0x104A,0x104A,0x104A,0x104A,0x1090,0x1091,0x1092,0x1093,0x1094,0x1095,0x1096,0x1097,0x1098,0x1099,0x10D3,0x10D3,0x10D3,0x10D3,0x10D3,0x10D3,0x10D3,0x10D3,0x10D3,0x10D3,0x10D4,0x10D4,0x10D4,0x10D4,0x10D4,0x10D4,0x10D4,0x10D4,0x10D4,0x10D4,0x1106,0x1106,0x1106,0x1106,0x1106,0x1106,0x1106,0x1106,0x1106,0x1106,0x110F,0x110F,0x110F,0x110F,0x110F,0x110F,0x110F,0x110F,0x110F,0x110F,0x1113,0x1113,0x1113,0x1113,0x1113,0x1113,0x1113,0x1113,0x1113,0x1113,0x111D,0x111D,0x111D,0x111D,0x111D,0x111D,0x111D,0x111D,0x111D,0x111D,0x112F,0x112F,0x112F,0x112F,0x112F,0x112F,0x112F,0x112F,0x112F,0x112F,0x1145,0x1145,0x1145,0x1145,0x1145,0x1145,0x1145,0x1145,0x1145,0x1145,0x114D,0x114D,0x114D,0x114D,0x114D,0x114D,0x114D,0x114D,0x114D,0x114D,0x1165,0x1165,0x1165,0x1165,0x1165,0x1165,0x1165,0x1165,0x1165,0x1165,0x116C,0x116C,0x116C,0x116C,0x116C,0x116C,0x116C,0x116C,0x116C,0x116C,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116D,0x116E,0x116E,0x116E,0x116E,0x1173,0x1173,0x1173,0x1173,0x1173,0x1173,0x1173,0x1173,0x1173,0x1173,0x118E,0x118E,0x118E,0x118E,0x118E,0x118E,0x118E,0x118E,0x118E,0x118E,0x1195,0x1195,0x1195,0x1195,0x1195,0x1195,0x1195,0x1195,0x1195,0x1195,0x11BF,0x11BF,0x11BF,0x11BF,0x11BF,0x11BF,0x11BF,0x11BF,0x11BF,0x11BF,0x11C5,0x11C5,0x11C5,0x11C5,0x11C5,0x11C5,0x11C5,0x11C5,0x11C5,0x11C5,0x11D5,0x11D5,0x11D5,0x11D5,0x11D5,0x11D5,0x11D5,0x11D5,0x11D5,0x11D5,0x11DA,0x11DA,0x11DA,0x11DA,0x11DA,0x11DA,0x11DA,0x11DA,0x11DA,0x11DA,0x11F5,0x11F5,0x11F5,0x11F5,0x11F5,0x11F5,0x11F5,0x11F5,0x11F5,0x11F5,0x1613,0x1613,0x1613,0x1613,0x1613,0x1613,0x1613,0x1613,0x1613,0x1613,0x16A6,0x16A6,0x16A6,0x16A6,0x16A6,0x16A6,0x16A6,0x16A6,0x16A6,0x16A6,0x16AC,0x16AC,0x16AC,0x16AC,0x16AC,0x16AC,0x16AC,0x16AC,0x16AC,0x16AC,0x16B5,0x16B5,0x16B5,0x16B5,0x16B5,0x16B5,0x16B5,0x16B5,0x16B5,0x16B5,0x16D7,0x16D7,0x16D7,0x16D7,0x16D7,0x16D7,0x16D7,0x16D7,0x16D7,0x16D7,0x17E0,0x17E1,0x17E2,0x17E3,0x17E4,0x17E5,0x17E6,0x17E7,0x17E8,0x17E9,0x1810,0x1811,0x1812,0x1813,0x1814,0x1815,0x1816,0x1817,0x1818,0x1819,0x1946,0x1947,0x1948,0x1949,0x194A,0x194B,0x194C,0x194D,0x194E,0x194F,0x19D0,0x19D1,0x19D2,0x19D3,0x19D4,0x19D5,0x19D6,0x19D7,0x19D8,0x19D9,0x1A80,0x1A81,0x1A82,0x1A83,0x1A84,0x1A85,0x1A86,0x1A87,0x1A88,0x1A89,0x1A90,0x1A91,0x1A92,0x1A93,0x1A94,0x1A95,0x1A96,0x1A97,0x1A98,0x1A99,0x1B50,0x1B51,0x1B52,0x1B53,0x1B54,0x1B55,0x1B56,0x1B57,0x1B58,0x1B59,0x1BB0,0x1BB1,0x1BB2,0x1BB3,0x1BB4,0x1BB5,0x1BB6,0x1BB7,0x1BB8,0x1BB9,0x1C40,0x1C41,0x1C42,0x1C43,0x1C44,0x1C45,0x1C46,0x1C47,0x1C48,0x1C49,0x1C50,0x1C51,0x1C52,0x1C53,0x1C54,0x1C55,0x1C56,0x1C57,0x1C58,0x1C59,0x1CCF,0x1CCF,0x1CCF,0x1CCF,0x1CCF,0x1CCF,0x1CCF,0x1CCF,0x1CCF,0x1CCF,0x1D7C,0x1D7C,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7D,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7E,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1D7F,0x1E14,0x1E14,0x1E14,0x1E14,0x1E14,0x1E14,0x1E14,0x1E14,0x1E14,0x1E14,0x1E2F,0x1E2F,0x1E2F,0x1E2F,0x1E2F,0x1E2F,0x1E2F,0x1E2F,0x1E2F,0x1E2F,0x1E4F,0x1E4F,0x1E4F,0x1E4F,0x1E4F,0x1E4F,0x1E4F,0x1E4F,0x1E4F,0x1E4F,0x1E5F,0x1E5F,0x1E5F,0x1E5F,0x1E5F,0x1E5F,0x1E5F,0x1E5F,0x1E5F,0x1E5F,0x1E95,0x1E95,0x1E95,0x1E95,0x1E95,0x1E95,0x1E95,0x1E95,0x1E95,0x1E95,0x1FBF,0x1FBF,0x1FBF,0x1FBF,0x1FBF,0x1FBF,0x1FBF,0x1FBF,0x1FBF,0x1FBF,0xA620,0xA621,0xA622,0xA623,0xA624,0xA625,0xA626,0xA627,0xA628,0xA629,0xA8D0,0xA8D1,0xA8D2,0xA8D3,0xA8D4,0xA8D5,0xA8D6,0xA8D7,0xA8D8,0xA8D9,0xA900,0xA901,0xA902,0xA903,0xA904,0xA905,0xA906,0xA907,0xA908,0xA909,0xA9D0,0xA9D1,0xA9D2,0xA9D3,0xA9D4,0xA9D5,0xA9D6,0xA9D7,0xA9D8,0xA9D9,0xA9F0,0xA9F1,0xA9F2,0xA9F3,0xA9F4,0xA9F5,0xA9F6,0xA9F7,0xA9F8,0xA9F9,0xAA50,0xAA51,0xAA52,0xAA53,0xAA54,0xAA55,0xAA56,0xAA57,0xAA58,0xAA59,0xABF0,0xABF1,0xABF2,0xABF3,0xABF4,0xABF5,0xABF6,0xABF7,0xABF8,0xABF9,0xFF10,0xFF11,0xFF12,0xFF13,0xFF14,0xFF15,0xFF16,0xFF17,0xFF18,0xFF19};

size_t unicode_len_Nd = sizeof(unicode_Nd)/sizeof(unicode_Nd[0]);

int unicode_Nl[] = {0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1014,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1015,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1016,0x1017,0x1017,0x1017,0x1017,0x1017,0x1034,0x1034,0x103D,0x103D,0x103D,0x103D,0x103D,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1240,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1241,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1242,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1243,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1244,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1245,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x1246,0x16EE,0x16EF,0x16F0,0x2160,0x2161,0x2162,0x2163,0x2164,0x2165,0x2166,0x2167,0x2168,0x2169,0x216A,0x216B,0x216C,0x216D,0x216E,0x216F,0x2170,0x2171,0x2172,0x2173,0x2174,0x2175,0x2176,0x2177,0x2178,0x2179,0x217A,0x217B,0x217C,0x217D,0x217E,0x217F,0x2180,0x2181,0x2182,0x2185,0x2186,0x2187,0x2188,0x3007,0x3021,0x3022,0x3023,0x3024,0x3025,0x3026,0x3027,0x3028,0x3029,0x3038,0x3039,0x303A,0xA6E6,0xA6E7,0xA6E8,0xA6E9,0xA6EA,0xA6EB,0xA6EC,0xA6ED,0xA6EE,0xA6EF};

size_t unicode_len_Nl = sizeof(unicode_Nl)/sizeof(unicode_Nl[0]);

int unicode_No[] =  {0x00B2,0x00B3,0x00B9,0x00BC,0x00BD,0x00BE,0x09F4,0x09F5,0x09F6,0x09F7,0x09F8,0x09F9,0x0B72,0x0B73,0x0B74,0x0B75,0x0B76,0x0B77,0x0BF0,0x0BF1,0x0BF2,0x0C78,0x0C79,0x0C7A,0x0C7B,0x0C7C,0x0C7D,0x0C7E,0x0D58,0x0D59,0x0D5A,0x0D5B,0x0D5C,0x0D5D,0x0D5E,0x0D70,0x0D71,0x0D72,0x0D73,0x0D74,0x0D75,0x0D76,0x0D77,0x0D78,0x0F2A,0x0F2B,0x0F2C,0x0F2D,0x0F2E,0x0F2F,0x0F30,0x0F31,0x0F32,0x0F33,0x1010,0x1010,0x1010,0x1010,0x1010,0x1010,0x1010,0x1010,0x1010,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1011,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1012,0x1013,0x1013,0x1013,0x1013,0x1017,0x1017,0x1017,0x1017,0x1018,0x1018,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102E,0x102F,0x102F,0x102F,0x102F,0x102F,0x102F,0x102F,0x102F,0x102F,0x102F,0x102F,0x102F,0x1032,0x1032,0x1032,0x1032,0x1085,0x1085,0x1085,0x1085,0x1085,0x1085,0x1085,0x1085,0x1087,0x1087,0x1087,0x1087,0x1087,0x1087,0x1087,0x108A,0x108A,0x108A,0x108A,0x108A,0x108A,0x108A,0x108A,0x108A,0x108F,0x108F,0x108F,0x108F,0x108F,0x1091,0x1091,0x1091,0x1091,0x1091,0x1091,0x109B,0x109B,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109C,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109D,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109E,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x109F,0x10A4,0x10A4,0x10A4,0x10A4,0x10A4,0x10A4,0x10A4,0x10A4,0x10A4,0x10A7,0x10A7,0x10A9,0x10A9,0x10A9,0x10AE,0x10AE,0x10AE,0x10AE,0x10AE,0x10B5,0x10B5,0x10B5,0x10B5,0x10B5,0x10B5,0x10B5,0x10B5,0x10B7,0x10B7,0x10B7,0x10B7,0x10B7,0x10B7,0x10B7,0x10B7,0x10BA,0x10BA,0x10BA,0x10BA,0x10BA,0x10BA,0x10BA,0x10CF,0x10CF,0x10CF,0x10CF,0x10CF,0x10CF,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E6,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10E7,0x10F1,0x10F1,0x10F1,0x10F2,0x10F2,0x10F2,0x10F2,0x10F2,0x10F2,0x10F2,0x10F5,0x10F5,0x10F5,0x10F5,0x10FC,0x10FC,0x10FC,0x10FC,0x10FC,0x10FC,0x10FC,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1105,0x1106,0x1106,0x1106,0x1106,0x1106,0x1106,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111E,0x111F,0x111F,0x111F,0x111F,0x111F,0x1173,0x1173,0x118E,0x118E,0x118E,0x118E,0x118E,0x118E,0x118F,0x118F,0x118F,0x11C5,0x11C5,0x11C5,0x11C5,0x11C5,0x11C5,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11C6,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FC,0x11FD,0x11FD,0x11FD,0x11FD,0x11FD,0x1369,0x136A,0x136B,0x136C,0x136D,0x136E,0x136F,0x1370,0x1371,0x1372,0x1373,0x1374,0x1375,0x1376,0x1377,0x1378,0x1379,0x137A,0x137B,0x137C,0x16B5,0x16B5,0x16B5,0x16B5,0x16B5,0x16B6,0x16B6,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E8,0x16E9,0x16E9,0x16E9,0x16E9,0x16E9,0x16E9,0x16E9,0x17F0,0x17F1,0x17F2,0x17F3,0x17F4,0x17F5,0x17F6,0x17F7,0x17F8,0x17F9,0x19DA,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2C,0x1D2D,0x1D2D,0x1D2D,0x1D2D,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2E,0x1D2F,0x1D2F,0x1D2F,0x1D2F,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D36,0x1D37,0x1D37,0x1D37,0x1D37,0x1D37,0x1D37,0x1D37,0x1D37,0x1D37,0x1E8C,0x1E8C,0x1E8C,0x1E8C,0x1E8C,0x1E8C,0x1E8C,0x1E8C,0x1E8C,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC7,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC8,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1EC9,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECA,0x1ECB,0x1ECB,0x1ECB,0x1ECB,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED0,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED1,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED2,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1ED3,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x1F10,0x2070,0x2074,0x2075,0x2076,0x2077,0x2078,0x2079,0x2080,0x2081,0x2082,0x2083,0x2084,0x2085,0x2086,0x2087,0x2088,0x2089,0x2150,0x2151,0x2152,0x2153,0x2154,0x2155,0x2156,0x2157,0x2158,0x2159,0x215A,0x215B,0x215C,0x215D,0x215E,0x215F,0x2189,0x2460,0x2461,0x2462,0x2463,0x2464,0x2465,0x2466,0x2467,0x2468,0x2469,0x246A,0x246B,0x246C,0x246D,0x246E,0x246F,0x2470,0x2471,0x2472,0x2473,0x2474,0x2475,0x2476,0x2477,0x2478,0x2479,0x247A,0x247B,0x247C,0x247D,0x247E,0x247F,0x2480,0x2481,0x2482,0x2483,0x2484,0x2485,0x2486,0x2487,0x2488,0x2489,0x248A,0x248B,0x248C,0x248D,0x248E,0x248F,0x2490,0x2491,0x2492,0x2493,0x2494,0x2495,0x2496,0x2497,0x2498,0x2499,0x249A,0x249B,0x24EA,0x24EB,0x24EC,0x24ED,0x24EE,0x24EF,0x24F0,0x24F1,0x24F2,0x24F3,0x24F4,0x24F5,0x24F6,0x24F7,0x24F8,0x24F9,0x24FA,0x24FB,0x24FC,0x24FD,0x24FE,0x24FF,0x2776,0x2777,0x2778,0x2779,0x277A,0x277B,0x277C,0x277D,0x277E,0x277F,0x2780,0x2781,0x2782,0x2783,0x2784,0x2785,0x2786,0x2787,0x2788,0x2789,0x278A,0x278B,0x278C,0x278D,0x278E,0x278F,0x2790,0x2791,0x2792,0x2793,0x2CFD,0x3192,0x3193,0x3194,0x3195,0x3220,0x3221,0x3222,0x3223,0x3224,0x3225,0x3226,0x3227,0x3228,0x3229,0x3248,0x3249,0x324A,0x324B,0x324C,0x324D,0x324E,0x324F,0x3251,0x3252,0x3253,0x3254,0x3255,0x3256,0x3257,0x3258,0x3259,0x325A,0x325B,0x325C,0x325D,0x325E,0x325F,0x3280,0x3281,0x3282,0x3283,0x3284,0x3285,0x3286,0x3287,0x3288,0x3289,0x32B1,0x32B2,0x32B3,0x32B4,0x32B5,0x32B6,0x32B7,0x32B8,0x32B9,0x32BA,0x32BB,0x32BC,0x32BD,0x32BE,0x32BF,0xA830,0xA831,0xA832,0xA833,0xA834,0xA835};

size_t unicode_len_No = sizeof(unicode_No)/sizeof(unicode_No[0]);

int unicode_N[] = {-1};
size_t unicode_len_N = sizeof(unicode_N)/sizeof(unicode_N[0]);

int unicode_Pc[] = {0x005F,0x203F,0x2040,0x2054,0xFE33,0xFE34,0xFE4D,0xFE4E,0xFE4F,0xFF3F};

size_t unicode_len_Pc = sizeof(unicode_Pc)/sizeof(unicode_Pc[0]);

int unicode_Pd[] = {0x002D,0x058A,0x05BE,0x10D6,0x10EA,0x1400,0x1806,0x2010,0x2011,0x2012,0x2013,0x2014,0x2015,0x2E17,0x2E1A,0x2E3A,0x2E3B,0x2E40,0x2E5D,0x301C,0x3030,0x30A0,0xFE31,0xFE32,0xFE58,0xFE63,0xFF0D};

size_t unicode_len_Pd = sizeof(unicode_Pd)/sizeof(unicode_Pd[0]);

int unicode_Ps[] = {0x0028,0x005B,0x007B,0x0F3A,0x0F3C,0x169B,0x201A,0x201E,0x2045,0x207D,0x208D,0x2308,0x230A,0x2329,0x2768,0x276A,0x276C,0x276E,0x2770,0x2772,0x2774,0x27C5,0x27E6,0x27E8,0x27EA,0x27EC,0x27EE,0x2983,0x2985,0x2987,0x2989,0x298B,0x298D,0x298F,0x2991,0x2993,0x2995,0x2997,0x29D8,0x29DA,0x29FC,0x2E22,0x2E24,0x2E26,0x2E28,0x2E42,0x2E55,0x2E57,0x2E59,0x2E5B,0x3008,0x300A,0x300C,0x300E,0x3010,0x3014,0x3016,0x3018,0x301A,0x301D,0xFD3F,0xFE17,0xFE35,0xFE37,0xFE39,0xFE3B,0xFE3D,0xFE3F,0xFE41,0xFE43,0xFE47,0xFE59,0xFE5B,0xFE5D,0xFF08,0xFF3B,0xFF5B,0xFF5F,0xFF62};

size_t unicode_len_Ps = sizeof(unicode_Ps)/sizeof(unicode_Ps[0]);

int unicode_Pe[] = {0x0029,0x005D,0x007D,0x0F3B,0x0F3D,0x169C,0x2046,0x207E,0x208E,0x2309,0x230B,0x232A,0x2769,0x276B,0x276D,0x276F,0x2771,0x2773,0x2775,0x27C6,0x27E7,0x27E9,0x27EB,0x27ED,0x27EF,0x2984,0x2986,0x2988,0x298A,0x298C,0x298E,0x2990,0x2992,0x2994,0x2996,0x2998,0x29D9,0x29DB,0x29FD,0x2E23,0x2E25,0x2E27,0x2E29,0x2E56,0x2E58,0x2E5A,0x2E5C,0x3009,0x300B,0x300D,0x300F,0x3011,0x3015,0x3017,0x3019,0x301B,0x301E,0x301F,0xFD3E,0xFE18,0xFE36,0xFE38,0xFE3A,0xFE3C,0xFE3E,0xFE40,0xFE42,0xFE44,0xFE48,0xFE5A,0xFE5C,0xFE5E,0xFF09,0xFF3D,0xFF5D,0xFF60,0xFF63};

size_t unicode_len_Pe = sizeof(unicode_Pe)/sizeof(unicode_Pe[0]);

int unicode_Pi[] = {0x00AB,0x2018,0x201B,0x201C,0x201F,0x2039,0x2E02,0x2E04,0x2E09,0x2E0C,0x2E1C,0x2E20};

size_t unicode_len_Pi = sizeof(unicode_Pi)/sizeof(unicode_Pi[0]);

int unicode_Pf[] = {0x00BB,0x2019,0x201D,0x203A,0x2E03,0x2E05,0x2E0A,0x2E0D,0x2E1D,0x2E21};

size_t unicode_len_Pf = sizeof(unicode_Pf)/sizeof(unicode_Pf[0]);

int unicode_Po[] = {0x0021,0x0022,0x0023,0x0025,0x0026,0x0027,0x002A,0x002C,0x002E,0x002F,0x003A,0x003B,0x003F,0x0040,0x005C,0x00A1,0x00A7,0x00B6,0x00B7,0x00BF,0x037E,0x0387,0x055A,0x055B,0x055C,0x055D,0x055E,0x055F,0x0589,0x05C0,0x05C3,0x05C6,0x05F3,0x05F4,0x0609,0x060A,0x060C,0x060D,0x061B,0x061D,0x061E,0x061F,0x066A,0x066B,0x066C,0x066D,0x06D4,0x0700,0x0701,0x0702,0x0703,0x0704,0x0705,0x0706,0x0707,0x0708,0x0709,0x070A,0x070B,0x070C,0x070D,0x07F7,0x07F8,0x07F9,0x0830,0x0831,0x0832,0x0833,0x0834,0x0835,0x0836,0x0837,0x0838,0x0839,0x083A,0x083B,0x083C,0x083D,0x083E,0x085E,0x0964,0x0965,0x0970,0x09FD,0x0A76,0x0AF0,0x0C77,0x0C84,0x0DF4,0x0E4F,0x0E5A,0x0E5B,0x0F04,0x0F05,0x0F06,0x0F07,0x0F08,0x0F09,0x0F0A,0x0F0B,0x0F0C,0x0F0D,0x0F0E,0x0F0F,0x0F10,0x0F11,0x0F12,0x0F14,0x0F85,0x0FD0,0x0FD1,0x0FD2,0x0FD3,0x0FD4,0x0FD9,0x0FDA,0x1010,0x1010,0x1010,0x1039,0x103D,0x104A,0x104B,0x104C,0x104D,0x104E,0x104F,0x1056,0x1085,0x1091,0x1093,0x10A5,0x10A5,0x10A5,0x10A5,0x10A5,0x10A5,0x10A5,0x10A5,0x10A5,0x10A7,0x10AF,0x10AF,0x10AF,0x10AF,0x10AF,0x10AF,0x10AF,0x10B3,0x10B3,0x10B3,0x10B3,0x10B3,0x10B3,0x10B3,0x10B9,0x10B9,0x10B9,0x10B9,0x10F5,0x10F5,0x10F5,0x10F5,0x10F5,0x10F8,0x10F8,0x10F8,0x10F8,0x10FB,0x1104,0x1104,0x1104,0x1104,0x1104,0x1104,0x1104,0x110B,0x110B,0x110B,0x110B,0x110C,0x110C,0x1114,0x1114,0x1114,0x1114,0x1117,0x1117,0x111C,0x111C,0x111C,0x111C,0x111C,0x111D,0x111D,0x111D,0x111D,0x1123,0x1123,0x1123,0x1123,0x1123,0x1123,0x112A,0x113D,0x113D,0x113D,0x113D,0x1144,0x1144,0x1144,0x1144,0x1144,0x1145,0x1145,0x1145,0x114C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115C,0x115D,0x115D,0x115D,0x115D,0x115D,0x115D,0x115D,0x115D,0x1164,0x1164,0x1164,0x1166,0x1166,0x1166,0x1166,0x1166,0x1166,0x1166,0x1166,0x1166,0x1166,0x1166,0x1166,0x1166,0x116B,0x1173,0x1173,0x1173,0x1183,0x1194,0x1194,0x1194,0x119E,0x11A3,0x11A4,0x11A4,0x11A4,0x11A4,0x11A4,0x11A4,0x11A4,0x11A9,0x11A9,0x11A9,0x11A9,0x11A9,0x11AA,0x11AA,0x11AA,0x11B0,0x11B0,0x11B0,0x11B0,0x11B0,0x11B0,0x11B0,0x11B0,0x11B0,0x11B0,0x11BE,0x11C4,0x11C4,0x11C4,0x11C4,0x11C4,0x11C7,0x11C7,0x11EF,0x11EF,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11F4,0x11FF,0x1247,0x1247,0x1247,0x1247,0x1247,0x12FF,0x12FF,0x1360,0x1361,0x1362,0x1363,0x1364,0x1365,0x1366,0x1367,0x1368,0x166E,0x16A6,0x16A6,0x16AF,0x16B3,0x16B3,0x16B3,0x16B3,0x16B3,0x16B4,0x16D6,0x16D6,0x16D6,0x16E9,0x16E9,0x16E9,0x16E9,0x16EB,0x16EC,0x16ED,0x16FE,0x1735,0x1736,0x17D4,0x17D5,0x17D6,0x17D8,0x17D9,0x17DA,0x1800,0x1801,0x1802,0x1803,0x1804,0x1805,0x1807,0x1808,0x1809,0x180A,0x1944,0x1945,0x1A1E,0x1A1F,0x1AA0,0x1AA1,0x1AA2,0x1AA3,0x1AA4,0x1AA5,0x1AA6,0x1AA8,0x1AA9,0x1AAA,0x1AAB,0x1AAC,0x1AAD,0x1B4E,0x1B4F,0x1B5A,0x1B5B,0x1B5C,0x1B5D,0x1B5E,0x1B5F,0x1B60,0x1B7D,0x1B7E,0x1B7F,0x1BC9,0x1BFC,0x1BFD,0x1BFE,0x1BFF,0x1C3B,0x1C3C,0x1C3D,0x1C3E,0x1C3F,0x1C7E,0x1C7F,0x1CC0,0x1CC1,0x1CC2,0x1CC3,0x1CC4,0x1CC5,0x1CC6,0x1CC7,0x1CD3,0x1DA8,0x1DA8,0x1DA8,0x1DA8,0x1DA8,0x1E5F,0x1E95,0x1E95,0x2016,0x2017,0x2020,0x2021,0x2022,0x2023,0x2024,0x2025,0x2026,0x2027,0x2030,0x2031,0x2032,0x2033,0x2034,0x2035,0x2036,0x2037,0x2038,0x203B,0x203C,0x203D,0x203E,0x2041,0x2042,0x2043,0x2047,0x2048,0x2049,0x204A,0x204B,0x204C,0x204D,0x204E,0x204F,0x2050,0x2051,0x2053,0x2055,0x2056,0x2057,0x2058,0x2059,0x205A,0x205B,0x205C,0x205D,0x205E,0x2CF9,0x2CFA,0x2CFB,0x2CFC,0x2CFE,0x2CFF,0x2D70,0x2E00,0x2E01,0x2E06,0x2E07,0x2E08,0x2E0B,0x2E0E,0x2E0F,0x2E10,0x2E11,0x2E12,0x2E13,0x2E14,0x2E15,0x2E16,0x2E18,0x2E19,0x2E1B,0x2E1E,0x2E1F,0x2E2A,0x2E2B,0x2E2C,0x2E2D,0x2E2E,0x2E30,0x2E31,0x2E32,0x2E33,0x2E34,0x2E35,0x2E36,0x2E37,0x2E38,0x2E39,0x2E3C,0x2E3D,0x2E3E,0x2E3F,0x2E41,0x2E43,0x2E44,0x2E45,0x2E46,0x2E47,0x2E48,0x2E49,0x2E4A,0x2E4B,0x2E4C,0x2E4D,0x2E4E,0x2E4F,0x2E52,0x2E53,0x2E54,0x3001,0x3002,0x3003,0x303D,0x30FB,0xA4FE,0xA4FF,0xA60D,0xA60E,0xA60F,0xA673,0xA67E,0xA6F2,0xA6F3,0xA6F4,0xA6F5,0xA6F6,0xA6F7,0xA874,0xA875,0xA876,0xA877,0xA8CE,0xA8CF,0xA8F8,0xA8F9,0xA8FA,0xA8FC,0xA92E,0xA92F,0xA95F,0xA9C1,0xA9C2,0xA9C3,0xA9C4,0xA9C5,0xA9C6,0xA9C7,0xA9C8,0xA9C9,0xA9CA,0xA9CB,0xA9CC,0xA9CD,0xA9DE,0xA9DF,0xAA5C,0xAA5D,0xAA5E,0xAA5F,0xAADE,0xAADF,0xAAF0,0xAAF1,0xABEB,0xFE10,0xFE11,0xFE12,0xFE13,0xFE14,0xFE15,0xFE16,0xFE19,0xFE30,0xFE45,0xFE46,0xFE49,0xFE4A,0xFE4B,0xFE4C,0xFE50,0xFE51,0xFE52,0xFE54,0xFE55,0xFE56,0xFE57,0xFE5F,0xFE60,0xFE61,0xFE68,0xFE6A,0xFE6B,0xFF01,0xFF02,0xFF03,0xFF05,0xFF06,0xFF07,0xFF0A,0xFF0C,0xFF0E,0xFF0F,0xFF1A,0xFF1B,0xFF1F,0xFF20,0xFF3C,0xFF61,0xFF64,0xFF65};

size_t unicode_len_Po = sizeof(unicode_Po)/sizeof(unicode_Po[0]);

int unicode_P[] = {-1};
size_t unicode_len_P = sizeof(unicode_P)/sizeof(unicode_P[0]);

int unicode_Sm[] = {0x002B,0x003C,0x003D,0x003E,0x007C,0x007E,0x00AC,0x00B1,0x00D7,0x00F7,0x03F6,0x0606,0x0607,0x0608,0x10D8,0x10D8,0x1D6C,0x1D6D,0x1D6F,0x1D71,0x1D73,0x1D74,0x1D76,0x1D78,0x1D7A,0x1D7C,0x1EEF,0x1EEF,0x2044,0x2052,0x207A,0x207B,0x207C,0x208A,0x208B,0x208C,0x2118,0x2140,0x2141,0x2142,0x2143,0x2144,0x214B,0x2190,0x2191,0x2192,0x2193,0x2194,0x219A,0x219B,0x21A0,0x21A3,0x21A6,0x21AE,0x21CE,0x21CF,0x21D2,0x21D4,0x21F4,0x21F5,0x21F6,0x21F7,0x21F8,0x21F9,0x21FA,0x21FB,0x21FC,0x21FD,0x21FE,0x21FF,0x2200,0x2201,0x2202,0x2203,0x2204,0x2205,0x2206,0x2207,0x2208,0x2209,0x220A,0x220B,0x220C,0x220D,0x220E,0x220F,0x2210,0x2211,0x2212,0x2213,0x2214,0x2215,0x2216,0x2217,0x2218,0x2219,0x221A,0x221B,0x221C,0x221D,0x221E,0x221F,0x2220,0x2221,0x2222,0x2223,0x2224,0x2225,0x2226,0x2227,0x2228,0x2229,0x222A,0x222B,0x222C,0x222D,0x222E,0x222F,0x2230,0x2231,0x2232,0x2233,0x2234,0x2235,0x2236,0x2237,0x2238,0x2239,0x223A,0x223B,0x223C,0x223D,0x223E,0x223F,0x2240,0x2241,0x2242,0x2243,0x2244,0x2245,0x2246,0x2247,0x2248,0x2249,0x224A,0x224B,0x224C,0x224D,0x224E,0x224F,0x2250,0x2251,0x2252,0x2253,0x2254,0x2255,0x2256,0x2257,0x2258,0x2259,0x225A,0x225B,0x225C,0x225D,0x225E,0x225F,0x2260,0x2261,0x2262,0x2263,0x2264,0x2265,0x2266,0x2267,0x2268,0x2269,0x226A,0x226B,0x226C,0x226D,0x226E,0x226F,0x2270,0x2271,0x2272,0x2273,0x2274,0x2275,0x2276,0x2277,0x2278,0x2279,0x227A,0x227B,0x227C,0x227D,0x227E,0x227F,0x2280,0x2281,0x2282,0x2283,0x2284,0x2285,0x2286,0x2287,0x2288,0x2289,0x228A,0x228B,0x228C,0x228D,0x228E,0x228F,0x2290,0x2291,0x2292,0x2293,0x2294,0x2295,0x2296,0x2297,0x2298,0x2299,0x229A,0x229B,0x229C,0x229D,0x229E,0x229F,0x22A0,0x22A1,0x22A2,0x22A3,0x22A4,0x22A5,0x22A6,0x22A7,0x22A8,0x22A9,0x22AA,0x22AB,0x22AC,0x22AD,0x22AE,0x22AF,0x22B0,0x22B1,0x22B2,0x22B3,0x22B4,0x22B5,0x22B6,0x22B7,0x22B8,0x22B9,0x22BA,0x22BB,0x22BC,0x22BD,0x22BE,0x22BF,0x22C0,0x22C1,0x22C2,0x22C3,0x22C4,0x22C5,0x22C6,0x22C7,0x22C8,0x22C9,0x22CA,0x22CB,0x22CC,0x22CD,0x22CE,0x22CF,0x22D0,0x22D1,0x22D2,0x22D3,0x22D4,0x22D5,0x22D6,0x22D7,0x22D8,0x22D9,0x22DA,0x22DB,0x22DC,0x22DD,0x22DE,0x22DF,0x22E0,0x22E1,0x22E2,0x22E3,0x22E4,0x22E5,0x22E6,0x22E7,0x22E8,0x22E9,0x22EA,0x22EB,0x22EC,0x22ED,0x22EE,0x22EF,0x22F0,0x22F1,0x22F2,0x22F3,0x22F4,0x22F5,0x22F6,0x22F7,0x22F8,0x22F9,0x22FA,0x22FB,0x22FC,0x22FD,0x22FE,0x22FF,0x2320,0x2321,0x237C,0x239B,0x239C,0x239D,0x239E,0x239F,0x23A0,0x23A1,0x23A2,0x23A3,0x23A4,0x23A5,0x23A6,0x23A7,0x23A8,0x23A9,0x23AA,0x23AB,0x23AC,0x23AD,0x23AE,0x23AF,0x23B0,0x23B1,0x23B2,0x23B3,0x23DC,0x23DD,0x23DE,0x23DF,0x23E0,0x23E1,0x25B7,0x25C1,0x25F8,0x25F9,0x25FA,0x25FB,0x25FC,0x25FD,0x25FE,0x25FF,0x266F,0x27C0,0x27C1,0x27C2,0x27C3,0x27C4,0x27C7,0x27C8,0x27C9,0x27CA,0x27CB,0x27CC,0x27CD,0x27CE,0x27CF,0x27D0,0x27D1,0x27D2,0x27D3,0x27D4,0x27D5,0x27D6,0x27D7,0x27D8,0x27D9,0x27DA,0x27DB,0x27DC,0x27DD,0x27DE,0x27DF,0x27E0,0x27E1,0x27E2,0x27E3,0x27E4,0x27E5,0x27F0,0x27F1,0x27F2,0x27F3,0x27F4,0x27F5,0x27F6,0x27F7,0x27F8,0x27F9,0x27FA,0x27FB,0x27FC,0x27FD,0x27FE,0x27FF,0x2900,0x2901,0x2902,0x2903,0x2904,0x2905,0x2906,0x2907,0x2908,0x2909,0x290A,0x290B,0x290C,0x290D,0x290E,0x290F,0x2910,0x2911,0x2912,0x2913,0x2914,0x2915,0x2916,0x2917,0x2918,0x2919,0x291A,0x291B,0x291C,0x291D,0x291E,0x291F,0x2920,0x2921,0x2922,0x2923,0x2924,0x2925,0x2926,0x2927,0x2928,0x2929,0x292A,0x292B,0x292C,0x292D,0x292E,0x292F,0x2930,0x2931,0x2932,0x2933,0x2934,0x2935,0x2936,0x2937,0x2938,0x2939,0x293A,0x293B,0x293C,0x293D,0x293E,0x293F,0x2940,0x2941,0x2942,0x2943,0x2944,0x2945,0x2946,0x2947,0x2948,0x2949,0x294A,0x294B,0x294C,0x294D,0x294E,0x294F,0x2950,0x2951,0x2952,0x2953,0x2954,0x2955,0x2956,0x2957,0x2958,0x2959,0x295A,0x295B,0x295C,0x295D,0x295E,0x295F,0x2960,0x2961,0x2962,0x2963,0x2964,0x2965,0x2966,0x2967,0x2968,0x2969,0x296A,0x296B,0x296C,0x296D,0x296E,0x296F,0x2970,0x2971,0x2972,0x2973,0x2974,0x2975,0x2976,0x2977,0x2978,0x2979,0x297A,0x297B,0x297C,0x297D,0x297E,0x297F,0x2980,0x2981,0x2982,0x2999,0x299A,0x299B,0x299C,0x299D,0x299E,0x299F,0x29A0,0x29A1,0x29A2,0x29A3,0x29A4,0x29A5,0x29A6,0x29A7,0x29A8,0x29A9,0x29AA,0x29AB,0x29AC,0x29AD,0x29AE,0x29AF,0x29B0,0x29B1,0x29B2,0x29B3,0x29B4,0x29B5,0x29B6,0x29B7,0x29B8,0x29B9,0x29BA,0x29BB,0x29BC,0x29BD,0x29BE,0x29BF,0x29C0,0x29C1,0x29C2,0x29C3,0x29C4,0x29C5,0x29C6,0x29C7,0x29C8,0x29C9,0x29CA,0x29CB,0x29CC,0x29CD,0x29CE,0x29CF,0x29D0,0x29D1,0x29D2,0x29D3,0x29D4,0x29D5,0x29D6,0x29D7,0x29DC,0x29DD,0x29DE,0x29DF,0x29E0,0x29E1,0x29E2,0x29E3,0x29E4,0x29E5,0x29E6,0x29E7,0x29E8,0x29E9,0x29EA,0x29EB,0x29EC,0x29ED,0x29EE,0x29EF,0x29F0,0x29F1,0x29F2,0x29F3,0x29F4,0x29F5,0x29F6,0x29F7,0x29F8,0x29F9,0x29FA,0x29FB,0x29FE,0x29FF,0x2A00,0x2A01,0x2A02,0x2A03,0x2A04,0x2A05,0x2A06,0x2A07,0x2A08,0x2A09,0x2A0A,0x2A0B,0x2A0C,0x2A0D,0x2A0E,0x2A0F,0x2A10,0x2A11,0x2A12,0x2A13,0x2A14,0x2A15,0x2A16,0x2A17,0x2A18,0x2A19,0x2A1A,0x2A1B,0x2A1C,0x2A1D,0x2A1E,0x2A1F,0x2A20,0x2A21,0x2A22,0x2A23,0x2A24,0x2A25,0x2A26,0x2A27,0x2A28,0x2A29,0x2A2A,0x2A2B,0x2A2C,0x2A2D,0x2A2E,0x2A2F,0x2A30,0x2A31,0x2A32,0x2A33,0x2A34,0x2A35,0x2A36,0x2A37,0x2A38,0x2A39,0x2A3A,0x2A3B,0x2A3C,0x2A3D,0x2A3E,0x2A3F,0x2A40,0x2A41,0x2A42,0x2A43,0x2A44,0x2A45,0x2A46,0x2A47,0x2A48,0x2A49,0x2A4A,0x2A4B,0x2A4C,0x2A4D,0x2A4E,0x2A4F,0x2A50,0x2A51,0x2A52,0x2A53,0x2A54,0x2A55,0x2A56,0x2A57,0x2A58,0x2A59,0x2A5A,0x2A5B,0x2A5C,0x2A5D,0x2A5E,0x2A5F,0x2A60,0x2A61,0x2A62,0x2A63,0x2A64,0x2A65,0x2A66,0x2A67,0x2A68,0x2A69,0x2A6A,0x2A6B,0x2A6C,0x2A6D,0x2A6E,0x2A6F,0x2A70,0x2A71,0x2A72,0x2A73,0x2A74,0x2A75,0x2A76,0x2A77,0x2A78,0x2A79,0x2A7A,0x2A7B,0x2A7C,0x2A7D,0x2A7E,0x2A7F,0x2A80,0x2A81,0x2A82,0x2A83,0x2A84,0x2A85,0x2A86,0x2A87,0x2A88,0x2A89,0x2A8A,0x2A8B,0x2A8C,0x2A8D,0x2A8E,0x2A8F,0x2A90,0x2A91,0x2A92,0x2A93,0x2A94,0x2A95,0x2A96,0x2A97,0x2A98,0x2A99,0x2A9A,0x2A9B,0x2A9C,0x2A9D,0x2A9E,0x2A9F,0x2AA0,0x2AA1,0x2AA2,0x2AA3,0x2AA4,0x2AA5,0x2AA6,0x2AA7,0x2AA8,0x2AA9,0x2AAA,0x2AAB,0x2AAC,0x2AAD,0x2AAE,0x2AAF,0x2AB0,0x2AB1,0x2AB2,0x2AB3,0x2AB4,0x2AB5,0x2AB6,0x2AB7,0x2AB8,0x2AB9,0x2ABA,0x2ABB,0x2ABC,0x2ABD,0x2ABE,0x2ABF,0x2AC0,0x2AC1,0x2AC2,0x2AC3,0x2AC4,0x2AC5,0x2AC6,0x2AC7,0x2AC8,0x2AC9,0x2ACA,0x2ACB,0x2ACC,0x2ACD,0x2ACE,0x2ACF,0x2AD0,0x2AD1,0x2AD2,0x2AD3,0x2AD4,0x2AD5,0x2AD6,0x2AD7,0x2AD8,0x2AD9,0x2ADA,0x2ADB,0x2ADC,0x2ADD,0x2ADE,0x2ADF,0x2AE0,0x2AE1,0x2AE2,0x2AE3,0x2AE4,0x2AE5,0x2AE6,0x2AE7,0x2AE8,0x2AE9,0x2AEA,0x2AEB,0x2AEC,0x2AED,0x2AEE,0x2AEF,0x2AF0,0x2AF1,0x2AF2,0x2AF3,0x2AF4,0x2AF5,0x2AF6,0x2AF7,0x2AF8,0x2AF9,0x2AFA,0x2AFB,0x2AFC,0x2AFD,0x2AFE,0x2AFF,0x2B30,0x2B31,0x2B32,0x2B33,0x2B34,0x2B35,0x2B36,0x2B37,0x2B38,0x2B39,0x2B3A,0x2B3B,0x2B3C,0x2B3D,0x2B3E,0x2B3F,0x2B40,0x2B41,0x2B42,0x2B43,0x2B44,0x2B47,0x2B48,0x2B49,0x2B4A,0x2B4B,0x2B4C,0xFB29,0xFE62,0xFE64,0xFE65,0xFE66,0xFF0B,0xFF1C,0xFF1D,0xFF1E,0xFF5C,0xFF5E,0xFFE2,0xFFE9,0xFFEA,0xFFEB,0xFFEC};

size_t unicode_len_Sm = sizeof(unicode_Sm)/sizeof(unicode_Sm[0]);

int unicode_Sc[] = {0x0024,0x00A2,0x00A3,0x00A4,0x00A5,0x058F,0x060B,0x07FE,0x07FF,0x09F2,0x09F3,0x09FB,0x0AF1,0x0BF9,0x0E3F,0x11FD,0x11FD,0x11FD,0x11FE,0x17DB,0x1E2F,0x1ECB,0x20A0,0x20A1,0x20A2,0x20A3,0x20A4,0x20A5,0x20A6,0x20A7,0x20A8,0x20A9,0x20AA,0x20AB,0x20AC,0x20AD,0x20AE,0x20AF,0x20B0,0x20B1,0x20B2,0x20B3,0x20B4,0x20B5,0x20B6,0x20B7,0x20B8,0x20B9,0x20BA,0x20BB,0x20BC,0x20BD,0x20BE,0x20BF,0x20C0,0xA838,0xFDFC,0xFE69,0xFF04,0xFFE0,0xFFE1,0xFFE5,0xFFE6};

size_t unicode_len_Sc = sizeof(unicode_Sc)/sizeof(unicode_Sc[0]);

int unicode_Sk[] = {0x005E,0x0060,0x00A8,0x00AF,0x00B4,0x00B8,0x02C2,0x02C3,0x02C4,0x02C5,0x02D2,0x02D3,0x02D4,0x02D5,0x02D6,0x02D7,0x02D8,0x02D9,0x02DA,0x02DB,0x02DC,0x02DD,0x02DE,0x02DF,0x02E5,0x02E6,0x02E7,0x02E8,0x02E9,0x02EA,0x02EB,0x02ED,0x02EF,0x02F0,0x02F1,0x02F2,0x02F3,0x02F4,0x02F5,0x02F6,0x02F7,0x02F8,0x02F9,0x02FA,0x02FB,0x02FC,0x02FD,0x02FE,0x02FF,0x0375,0x0384,0x0385,0x0888,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1FBD,0x1FBF,0x1FC0,0x1FC1,0x1FCD,0x1FCE,0x1FCF,0x1FDD,0x1FDE,0x1FDF,0x1FED,0x1FEE,0x1FEF,0x1FFD,0x1FFE,0x309B,0x309C,0xA700,0xA701,0xA702,0xA703,0xA704,0xA705,0xA706,0xA707,0xA708,0xA709,0xA70A,0xA70B,0xA70C,0xA70D,0xA70E,0xA70F,0xA710,0xA711,0xA712,0xA713,0xA714,0xA715,0xA716,0xA720,0xA721,0xA789,0xA78A,0xAB5B,0xAB6A,0xAB6B,0xFBB2,0xFBB3,0xFBB4,0xFBB5,0xFBB6,0xFBB7,0xFBB8,0xFBB9,0xFBBA,0xFBBB,0xFBBC,0xFBBD,0xFBBE,0xFBBF,0xFBC0,0xFBC1,0xFBC2,0xFF3E,0xFF40,0xFFE3};

size_t unicode_len_Sk = sizeof(unicode_Sk)/sizeof(unicode_Sk[0]);

int unicode_So[] = {0x00A6,0x00A9,0x00AE,0x00B0,0x0482,0x058D,0x058E,0x060E,0x060F,0x06DE,0x06E9,0x06FD,0x06FE,0x07F6,0x09FA,0x0B70,0x0BF3,0x0BF4,0x0BF5,0x0BF6,0x0BF7,0x0BF8,0x0BFA,0x0C7F,0x0D4F,0x0D79,0x0F01,0x0F02,0x0F03,0x0F13,0x0F15,0x0F16,0x0F17,0x0F1A,0x0F1B,0x0F1C,0x0F1D,0x0F1E,0x0F1F,0x0F34,0x0F36,0x0F38,0x0FBE,0x0FBF,0x0FC0,0x0FC1,0x0FC2,0x0FC3,0x0FC4,0x0FC5,0x0FC7,0x0FC8,0x0FC9,0x0FCA,0x0FCB,0x0FCC,0x0FCE,0x0FCF,0x0FD5,0x0FD6,0x0FD7,0x0FD8,0x1013,0x1013,0x1013,0x1013,0x1013,0x1013,0x1013,0x1013,0x1013,0x1017,0x1017,0x1017,0x1017,0x1017,0x1017,0x1017,0x1018,0x1018,0x1018,0x1018,0x1018,0x1018,0x1018,0x1018,0x1018,0x1018,0x1018,0x1018,0x1018,0x1019,0x1019,0x1019,0x1019,0x1019,0x1019,0x1019,0x1019,0x1019,0x1019,0x1019,0x1019,0x1019,0x101A,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101D,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101E,0x101F,0x101F,0x101F,0x101F,0x101F,0x101F,0x101F,0x101F,0x101F,0x101F,0x101F,0x101F,0x101F,0x1087,0x1087,0x109E,0x109F,0x10AC,0x1173,0x11FD,0x11FD,0x11FD,0x11FD,0x11FD,0x11FD,0x11FD,0x11FD,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FE,0x11FF,0x11FF,0x1390,0x1391,0x1392,0x1393,0x1394,0x1395,0x1396,0x1397,0x1398,0x1399,0x166D,0x16B3,0x16B3,0x16B3,0x16B3,0x16B4,0x1940,0x19DE,0x19DF,0x19E0,0x19E1,0x19E2,0x19E3,0x19E4,0x19E5,0x19E6,0x19E7,0x19E8,0x19E9,0x19EA,0x19EB,0x19EC,0x19ED,0x19EE,0x19EF,0x19F0,0x19F1,0x19F2,0x19F3,0x19F4,0x19F5,0x19F6,0x19F7,0x19F8,0x19F9,0x19FA,0x19FB,0x19FC,0x19FD,0x19FE,0x19FF,0x1B61,0x1B62,0x1B63,0x1B64,0x1B65,0x1B66,0x1B67,0x1B68,0x1B69,0x1B6A,0x1B74,0x1B75,0x1B76,0x1B77,0x1B78,0x1B79,0x1B7A,0x1B7B,0x1B7C,0x1BC9,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC0,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC1,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC2,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC3,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC4,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC5,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC6,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC7,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC8,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CC9,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCA,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCB,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCC,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCD,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CCE,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD0,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD1,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD2,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD3,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD4,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD5,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD6,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD7,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD8,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CD9,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDA,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDB,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDC,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDD,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDE,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CDF,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE0,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE1,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE2,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE3,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE4,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE5,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE6,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE7,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE8,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CE9,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEA,0x1CEB,0x1CEB,0x1CEB,0x1CEB,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF5,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF6,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF7,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF8,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CF9,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFA,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFB,0x1CFC,0x1CFC,0x1CFC,0x1CFC,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D00,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D01,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D02,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D03,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D04,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D05,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D06,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D07,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D08,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D09,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0A,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0B,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0C,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0D,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0E,0x1D0F,0x1D0F,0x1D0F,0x1D0F,0x1D0F,0x1D0F,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D10,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D11,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D12,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D13,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D14,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D15,0x1D16,0x1D16,0x1D16,0x1D16,0x1D16,0x1D16,0x1D16,0x1D16,0x1D18,0x1D18,0x1D18,0x1D18,0x1D18,0x1D18,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D19,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1A,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1B,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1C,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1D,0x1D1E,0x1D1E,0x1D1E,0x1D1E,0x1D1E,0x1D1E,0x1D1E,0x1D1E,0x1D1E,0x1D1E,0x1D1E,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D20,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D21,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D22,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D23,0x1D24,0x1D24,0x1D24,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D30,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D31,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D32,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D33,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D34,0x1D35,0x1D35,0x1D35,0x1D35,0x1D35,0x1D35,0x1D35,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D80,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D81,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D82,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D83,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D84,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D85,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D86,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D87,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D88,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D89,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8A,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8B,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8C,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8D,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8E,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D8F,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D90,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D91,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D92,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D93,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D94,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D95,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D96,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D97,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D98,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D99,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9A,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9B,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9C,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9D,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9E,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1D9F,0x1DA3,0x1DA3,0x1DA3,0x1DA3,0x1DA6,0x1DA6,0x1DA6,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA7,0x1DA8,0x1DA8,0x1DA8,0x1DA8,0x1DA8,0x1DA8,0x1E14,0x1ECA,0x1ED2,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F00,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F01,0x1F02,0x1F02,0x1F02,0x1F02,0x1F02,0x1F02,0x1F02,0x1F02,0x1F02,0x1F02,0x1F02,0x1F02,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F03,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F04,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F05,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F06,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F07,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F08,0x1F09,0x1F09,0x1F09,0x1F09,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0A,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0B,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0C,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0D,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0E,0x1F0F,0x1F0F,0x1F0F,0x1F0F,0x1F0F,0x1F0F,0x1F10,0x1F10,0x1F10,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F11,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F12,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F13,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F14,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F15,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F16,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F17,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F18,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F19,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1A,0x1F1E,0x1F1E,0x1F1E,0x1F1E,0x1F1E,0x1F1E,0x1F1E,0x1F1E,0x1F1E,0x1F1E,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F1F,0x1F20,0x1F20,0x1F20,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F21,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F22,0x1F23,0x1F23,0x1F23,0x1F23,0x1F23,0x1F23,0x1F23,0x1F23,0x1F23,0x1F23,0x1F23,0x1F23,0x1F24,0x1F24,0x1F24,0x1F24,0x1F24,0x1F24,0x1F24,0x1F24,0x1F24,0x1F25,0x1F25,0x1F26,0x1F26,0x1F26,0x1F26,0x1F26,0x1F26,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F30,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F31,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F32,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F33,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F34,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F35,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F36,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F37,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F38,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F39,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3A,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3B,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3C,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3D,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3E,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F3F,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F40,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F41,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F42,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F43,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F44,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F45,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F46,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F47,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F48,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F49,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4A,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4B,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4C,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4D,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4E,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F4F,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F50,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F51,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F52,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F53,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F54,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F55,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F56,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F57,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F58,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F59,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5A,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5B,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5C,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5D,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5E,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F5F,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F60,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F61,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F62,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F63,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F64,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F65,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F66,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F67,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F68,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F69,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6A,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6B,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6C,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6D,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6E,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F6F,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F70,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F71,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F72,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F73,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F74,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F75,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F76,0x1F77,0x1F77,0x1F77,0x1F77,0x1F77,0x1F77,0x1F77,0x1F77,0x1F77,0x1F77,0x1F77,0x1F77,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F78,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F79,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7A,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7B,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7C,0x1F7D,0x1F7D,0x1F7D,0x1F7D,0x1F7D,0x1F7D,0x1F7D,0x1F7D,0x1F7D,0x1F7D,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7E,0x1F7F,0x1F80,0x1F80,0x1F80,0x1F80,0x1F80,0x1F80,0x1F80,0x1F80,0x1F80,0x1F80,0x1F80,0x1F80,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F81,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F82,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F83,0x1F84,0x1F84,0x1F84,0x1F84,0x1F84,0x1F84,0x1F84,0x1F84,0x1F85,0x1F85,0x1F85,0x1F85,0x1F85,0x1F85,0x1F85,0x1F85,0x1F85,0x1F85,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F86,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F87,0x1F88,0x1F88,0x1F88,0x1F88,0x1F88,0x1F88,0x1F88,0x1F88,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F89,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8A,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8B,0x1F8C,0x1F8C,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F90,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F91,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F92,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F93,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F94,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F95,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F96,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F97,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F98,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F99,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9A,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9B,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9C,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9D,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9E,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1F9F,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA0,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA1,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA2,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA3,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA4,0x1FA5,0x1FA5,0x1FA5,0x1FA5,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA6,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA7,0x1FA8,0x1FA8,0x1FA8,0x1FA8,0x1FA8,0x1FA8,0x1FA8,0x1FA8,0x1FA8,0x1FA8,0x1FA8,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FA9,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAA,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAB,0x1FAC,0x1FAC,0x1FAC,0x1FAC,0x1FAC,0x1FAC,0x1FAC,0x1FAC,0x1FAC,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAD,0x1FAE,0x1FAE,0x1FAE,0x1FAE,0x1FAE,0x1FAE,0x1FAE,0x1FAE,0x1FAE,0x1FAE,0x1FAF,0x1FAF,0x1FAF,0x1FAF,0x1FAF,0x1FAF,0x1FAF,0x1FAF,0x1FAF,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB0,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB1,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB2,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB3,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB4,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB5,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB6,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB7,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB8,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FB9,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBA,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBB,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBC,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBD,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x1FBE,0x2100,0x2101,0x2103,0x2104,0x2105,0x2106,0x2108,0x2109,0x2114,0x2116,0x2117,0x211E,0x211F,0x2120,0x2121,0x2122,0x2123,0x2125,0x2127,0x2129,0x212E,0x213A,0x213B,0x214A,0x214C,0x214D,0x214F,0x218A,0x218B,0x2195,0x2196,0x2197,0x2198,0x2199,0x219C,0x219D,0x219E,0x219F,0x21A1,0x21A2,0x21A4,0x21A5,0x21A7,0x21A8,0x21A9,0x21AA,0x21AB,0x21AC,0x21AD,0x21AF,0x21B0,0x21B1,0x21B2,0x21B3,0x21B4,0x21B5,0x21B6,0x21B7,0x21B8,0x21B9,0x21BA,0x21BB,0x21BC,0x21BD,0x21BE,0x21BF,0x21C0,0x21C1,0x21C2,0x21C3,0x21C4,0x21C5,0x21C6,0x21C7,0x21C8,0x21C9,0x21CA,0x21CB,0x21CC,0x21CD,0x21D0,0x21D1,0x21D3,0x21D5,0x21D6,0x21D7,0x21D8,0x21D9,0x21DA,0x21DB,0x21DC,0x21DD,0x21DE,0x21DF,0x21E0,0x21E1,0x21E2,0x21E3,0x21E4,0x21E5,0x21E6,0x21E7,0x21E8,0x21E9,0x21EA,0x21EB,0x21EC,0x21ED,0x21EE,0x21EF,0x21F0,0x21F1,0x21F2,0x21F3,0x2300,0x2301,0x2302,0x2303,0x2304,0x2305,0x2306,0x2307,0x230C,0x230D,0x230E,0x230F,0x2310,0x2311,0x2312,0x2313,0x2314,0x2315,0x2316,0x2317,0x2318,0x2319,0x231A,0x231B,0x231C,0x231D,0x231E,0x231F,0x2322,0x2323,0x2324,0x2325,0x2326,0x2327,0x2328,0x232B,0x232C,0x232D,0x232E,0x232F,0x2330,0x2331,0x2332,0x2333,0x2334,0x2335,0x2336,0x2337,0x2338,0x2339,0x233A,0x233B,0x233C,0x233D,0x233E,0x233F,0x2340,0x2341,0x2342,0x2343,0x2344,0x2345,0x2346,0x2347,0x2348,0x2349,0x234A,0x234B,0x234C,0x234D,0x234E,0x234F,0x2350,0x2351,0x2352,0x2353,0x2354,0x2355,0x2356,0x2357,0x2358,0x2359,0x235A,0x235B,0x235C,0x235D,0x235E,0x235F,0x2360,0x2361,0x2362,0x2363,0x2364,0x2365,0x2366,0x2367,0x2368,0x2369,0x236A,0x236B,0x236C,0x236D,0x236E,0x236F,0x2370,0x2371,0x2372,0x2373,0x2374,0x2375,0x2376,0x2377,0x2378,0x2379,0x237A,0x237B,0x237D,0x237E,0x237F,0x2380,0x2381,0x2382,0x2383,0x2384,0x2385,0x2386,0x2387,0x2388,0x2389,0x238A,0x238B,0x238C,0x238D,0x238E,0x238F,0x2390,0x2391,0x2392,0x2393,0x2394,0x2395,0x2396,0x2397,0x2398,0x2399,0x239A,0x23B4,0x23B5,0x23B6,0x23B7,0x23B8,0x23B9,0x23BA,0x23BB,0x23BC,0x23BD,0x23BE,0x23BF,0x23C0,0x23C1,0x23C2,0x23C3,0x23C4,0x23C5,0x23C6,0x23C7,0x23C8,0x23C9,0x23CA,0x23CB,0x23CC,0x23CD,0x23CE,0x23CF,0x23D0,0x23D1,0x23D2,0x23D3,0x23D4,0x23D5,0x23D6,0x23D7,0x23D8,0x23D9,0x23DA,0x23DB,0x23E2,0x23E3,0x23E4,0x23E5,0x23E6,0x23E7,0x23E8,0x23E9,0x23EA,0x23EB,0x23EC,0x23ED,0x23EE,0x23EF,0x23F0,0x23F1,0x23F2,0x23F3,0x23F4,0x23F5,0x23F6,0x23F7,0x23F8,0x23F9,0x23FA,0x23FB,0x23FC,0x23FD,0x23FE,0x23FF,0x2400,0x2401,0x2402,0x2403,0x2404,0x2405,0x2406,0x2407,0x2408,0x2409,0x240A,0x240B,0x240C,0x240D,0x240E,0x240F,0x2410,0x2411,0x2412,0x2413,0x2414,0x2415,0x2416,0x2417,0x2418,0x2419,0x241A,0x241B,0x241C,0x241D,0x241E,0x241F,0x2420,0x2421,0x2422,0x2423,0x2424,0x2425,0x2426,0x2427,0x2428,0x2429,0x2440,0x2441,0x2442,0x2443,0x2444,0x2445,0x2446,0x2447,0x2448,0x2449,0x244A,0x249C,0x249D,0x249E,0x249F,0x24A0,0x24A1,0x24A2,0x24A3,0x24A4,0x24A5,0x24A6,0x24A7,0x24A8,0x24A9,0x24AA,0x24AB,0x24AC,0x24AD,0x24AE,0x24AF,0x24B0,0x24B1,0x24B2,0x24B3,0x24B4,0x24B5,0x24B6,0x24B7,0x24B8,0x24B9,0x24BA,0x24BB,0x24BC,0x24BD,0x24BE,0x24BF,0x24C0,0x24C1,0x24C2,0x24C3,0x24C4,0x24C5,0x24C6,0x24C7,0x24C8,0x24C9,0x24CA,0x24CB,0x24CC,0x24CD,0x24CE,0x24CF,0x24D0,0x24D1,0x24D2,0x24D3,0x24D4,0x24D5,0x24D6,0x24D7,0x24D8,0x24D9,0x24DA,0x24DB,0x24DC,0x24DD,0x24DE,0x24DF,0x24E0,0x24E1,0x24E2,0x24E3,0x24E4,0x24E5,0x24E6,0x24E7,0x24E8,0x24E9,0x2500,0x2501,0x2502,0x2503,0x2504,0x2505,0x2506,0x2507,0x2508,0x2509,0x250A,0x250B,0x250C,0x250D,0x250E,0x250F,0x2510,0x2511,0x2512,0x2513,0x2514,0x2515,0x2516,0x2517,0x2518,0x2519,0x251A,0x251B,0x251C,0x251D,0x251E,0x251F,0x2520,0x2521,0x2522,0x2523,0x2524,0x2525,0x2526,0x2527,0x2528,0x2529,0x252A,0x252B,0x252C,0x252D,0x252E,0x252F,0x2530,0x2531,0x2532,0x2533,0x2534,0x2535,0x2536,0x2537,0x2538,0x2539,0x253A,0x253B,0x253C,0x253D,0x253E,0x253F,0x2540,0x2541,0x2542,0x2543,0x2544,0x2545,0x2546,0x2547,0x2548,0x2549,0x254A,0x254B,0x254C,0x254D,0x254E,0x254F,0x2550,0x2551,0x2552,0x2553,0x2554,0x2555,0x2556,0x2557,0x2558,0x2559,0x255A,0x255B,0x255C,0x255D,0x255E,0x255F,0x2560,0x2561,0x2562,0x2563,0x2564,0x2565,0x2566,0x2567,0x2568,0x2569,0x256A,0x256B,0x256C,0x256D,0x256E,0x256F,0x2570,0x2571,0x2572,0x2573,0x2574,0x2575,0x2576,0x2577,0x2578,0x2579,0x257A,0x257B,0x257C,0x257D,0x257E,0x257F,0x2580,0x2581,0x2582,0x2583,0x2584,0x2585,0x2586,0x2587,0x2588,0x2589,0x258A,0x258B,0x258C,0x258D,0x258E,0x258F,0x2590,0x2591,0x2592,0x2593,0x2594,0x2595,0x2596,0x2597,0x2598,0x2599,0x259A,0x259B,0x259C,0x259D,0x259E,0x259F,0x25A0,0x25A1,0x25A2,0x25A3,0x25A4,0x25A5,0x25A6,0x25A7,0x25A8,0x25A9,0x25AA,0x25AB,0x25AC,0x25AD,0x25AE,0x25AF,0x25B0,0x25B1,0x25B2,0x25B3,0x25B4,0x25B5,0x25B6,0x25B8,0x25B9,0x25BA,0x25BB,0x25BC,0x25BD,0x25BE,0x25BF,0x25C0,0x25C2,0x25C3,0x25C4,0x25C5,0x25C6,0x25C7,0x25C8,0x25C9,0x25CA,0x25CB,0x25CC,0x25CD,0x25CE,0x25CF,0x25D0,0x25D1,0x25D2,0x25D3,0x25D4,0x25D5,0x25D6,0x25D7,0x25D8,0x25D9,0x25DA,0x25DB,0x25DC,0x25DD,0x25DE,0x25DF,0x25E0,0x25E1,0x25E2,0x25E3,0x25E4,0x25E5,0x25E6,0x25E7,0x25E8,0x25E9,0x25EA,0x25EB,0x25EC,0x25ED,0x25EE,0x25EF,0x25F0,0x25F1,0x25F2,0x25F3,0x25F4,0x25F5,0x25F6,0x25F7,0x2600,0x2601,0x2602,0x2603,0x2604,0x2605,0x2606,0x2607,0x2608,0x2609,0x260A,0x260B,0x260C,0x260D,0x260E,0x260F,0x2610,0x2611,0x2612,0x2613,0x2614,0x2615,0x2616,0x2617,0x2618,0x2619,0x261A,0x261B,0x261C,0x261D,0x261E,0x261F,0x2620,0x2621,0x2622,0x2623,0x2624,0x2625,0x2626,0x2627,0x2628,0x2629,0x262A,0x262B,0x262C,0x262D,0x262E,0x262F,0x2630,0x2631,0x2632,0x2633,0x2634,0x2635,0x2636,0x2637,0x2638,0x2639,0x263A,0x263B,0x263C,0x263D,0x263E,0x263F,0x2640,0x2641,0x2642,0x2643,0x2644,0x2645,0x2646,0x2647,0x2648,0x2649,0x264A,0x264B,0x264C,0x264D,0x264E,0x264F,0x2650,0x2651,0x2652,0x2653,0x2654,0x2655,0x2656,0x2657,0x2658,0x2659,0x265A,0x265B,0x265C,0x265D,0x265E,0x265F,0x2660,0x2661,0x2662,0x2663,0x2664,0x2665,0x2666,0x2667,0x2668,0x2669,0x266A,0x266B,0x266C,0x266D,0x266E,0x2670,0x2671,0x2672,0x2673,0x2674,0x2675,0x2676,0x2677,0x2678,0x2679,0x267A,0x267B,0x267C,0x267D,0x267E,0x267F,0x2680,0x2681,0x2682,0x2683,0x2684,0x2685,0x2686,0x2687,0x2688,0x2689,0x268A,0x268B,0x268C,0x268D,0x268E,0x268F,0x2690,0x2691,0x2692,0x2693,0x2694,0x2695,0x2696,0x2697,0x2698,0x2699,0x269A,0x269B,0x269C,0x269D,0x269E,0x269F,0x26A0,0x26A1,0x26A2,0x26A3,0x26A4,0x26A5,0x26A6,0x26A7,0x26A8,0x26A9,0x26AA,0x26AB,0x26AC,0x26AD,0x26AE,0x26AF,0x26B0,0x26B1,0x26B2,0x26B3,0x26B4,0x26B5,0x26B6,0x26B7,0x26B8,0x26B9,0x26BA,0x26BB,0x26BC,0x26BD,0x26BE,0x26BF,0x26C0,0x26C1,0x26C2,0x26C3,0x26C4,0x26C5,0x26C6,0x26C7,0x26C8,0x26C9,0x26CA,0x26CB,0x26CC,0x26CD,0x26CE,0x26CF,0x26D0,0x26D1,0x26D2,0x26D3,0x26D4,0x26D5,0x26D6,0x26D7,0x26D8,0x26D9,0x26DA,0x26DB,0x26DC,0x26DD,0x26DE,0x26DF,0x26E0,0x26E1,0x26E2,0x26E3,0x26E4,0x26E5,0x26E6,0x26E7,0x26E8,0x26E9,0x26EA,0x26EB,0x26EC,0x26ED,0x26EE,0x26EF,0x26F0,0x26F1,0x26F2,0x26F3,0x26F4,0x26F5,0x26F6,0x26F7,0x26F8,0x26F9,0x26FA,0x26FB,0x26FC,0x26FD,0x26FE,0x26FF,0x2700,0x2701,0x2702,0x2703,0x2704,0x2705,0x2706,0x2707,0x2708,0x2709,0x270A,0x270B,0x270C,0x270D,0x270E,0x270F,0x2710,0x2711,0x2712,0x2713,0x2714,0x2715,0x2716,0x2717,0x2718,0x2719,0x271A,0x271B,0x271C,0x271D,0x271E,0x271F,0x2720,0x2721,0x2722,0x2723,0x2724,0x2725,0x2726,0x2727,0x2728,0x2729,0x272A,0x272B,0x272C,0x272D,0x272E,0x272F,0x2730,0x2731,0x2732,0x2733,0x2734,0x2735,0x2736,0x2737,0x2738,0x2739,0x273A,0x273B,0x273C,0x273D,0x273E,0x273F,0x2740,0x2741,0x2742,0x2743,0x2744,0x2745,0x2746,0x2747,0x2748,0x2749,0x274A,0x274B,0x274C,0x274D,0x274E,0x274F,0x2750,0x2751,0x2752,0x2753,0x2754,0x2755,0x2756,0x2757,0x2758,0x2759,0x275A,0x275B,0x275C,0x275D,0x275E,0x275F,0x2760,0x2761,0x2762,0x2763,0x2764,0x2765,0x2766,0x2767,0x2794,0x2795,0x2796,0x2797,0x2798,0x2799,0x279A,0x279B,0x279C,0x279D,0x279E,0x279F,0x27A0,0x27A1,0x27A2,0x27A3,0x27A4,0x27A5,0x27A6,0x27A7,0x27A8,0x27A9,0x27AA,0x27AB,0x27AC,0x27AD,0x27AE,0x27AF,0x27B0,0x27B1,0x27B2,0x27B3,0x27B4,0x27B5,0x27B6,0x27B7,0x27B8,0x27B9,0x27BA,0x27BB,0x27BC,0x27BD,0x27BE,0x27BF,0x2800,0x2801,0x2802,0x2803,0x2804,0x2805,0x2806,0x2807,0x2808,0x2809,0x280A,0x280B,0x280C,0x280D,0x280E,0x280F,0x2810,0x2811,0x2812,0x2813,0x2814,0x2815,0x2816,0x2817,0x2818,0x2819,0x281A,0x281B,0x281C,0x281D,0x281E,0x281F,0x2820,0x2821,0x2822,0x2823,0x2824,0x2825,0x2826,0x2827,0x2828,0x2829,0x282A,0x282B,0x282C,0x282D,0x282E,0x282F,0x2830,0x2831,0x2832,0x2833,0x2834,0x2835,0x2836,0x2837,0x2838,0x2839,0x283A,0x283B,0x283C,0x283D,0x283E,0x283F,0x2840,0x2841,0x2842,0x2843,0x2844,0x2845,0x2846,0x2847,0x2848,0x2849,0x284A,0x284B,0x284C,0x284D,0x284E,0x284F,0x2850,0x2851,0x2852,0x2853,0x2854,0x2855,0x2856,0x2857,0x2858,0x2859,0x285A,0x285B,0x285C,0x285D,0x285E,0x285F,0x2860,0x2861,0x2862,0x2863,0x2864,0x2865,0x2866,0x2867,0x2868,0x2869,0x286A,0x286B,0x286C,0x286D,0x286E,0x286F,0x2870,0x2871,0x2872,0x2873,0x2874,0x2875,0x2876,0x2877,0x2878,0x2879,0x287A,0x287B,0x287C,0x287D,0x287E,0x287F,0x2880,0x2881,0x2882,0x2883,0x2884,0x2885,0x2886,0x2887,0x2888,0x2889,0x288A,0x288B,0x288C,0x288D,0x288E,0x288F,0x2890,0x2891,0x2892,0x2893,0x2894,0x2895,0x2896,0x2897,0x2898,0x2899,0x289A,0x289B,0x289C,0x289D,0x289E,0x289F,0x28A0,0x28A1,0x28A2,0x28A3,0x28A4,0x28A5,0x28A6,0x28A7,0x28A8,0x28A9,0x28AA,0x28AB,0x28AC,0x28AD,0x28AE,0x28AF,0x28B0,0x28B1,0x28B2,0x28B3,0x28B4,0x28B5,0x28B6,0x28B7,0x28B8,0x28B9,0x28BA,0x28BB,0x28BC,0x28BD,0x28BE,0x28BF,0x28C0,0x28C1,0x28C2,0x28C3,0x28C4,0x28C5,0x28C6,0x28C7,0x28C8,0x28C9,0x28CA,0x28CB,0x28CC,0x28CD,0x28CE,0x28CF,0x28D0,0x28D1,0x28D2,0x28D3,0x28D4,0x28D5,0x28D6,0x28D7,0x28D8,0x28D9,0x28DA,0x28DB,0x28DC,0x28DD,0x28DE,0x28DF,0x28E0,0x28E1,0x28E2,0x28E3,0x28E4,0x28E5,0x28E6,0x28E7,0x28E8,0x28E9,0x28EA,0x28EB,0x28EC,0x28ED,0x28EE,0x28EF,0x28F0,0x28F1,0x28F2,0x28F3,0x28F4,0x28F5,0x28F6,0x28F7,0x28F8,0x28F9,0x28FA,0x28FB,0x28FC,0x28FD,0x28FE,0x28FF,0x2B00,0x2B01,0x2B02,0x2B03,0x2B04,0x2B05,0x2B06,0x2B07,0x2B08,0x2B09,0x2B0A,0x2B0B,0x2B0C,0x2B0D,0x2B0E,0x2B0F,0x2B10,0x2B11,0x2B12,0x2B13,0x2B14,0x2B15,0x2B16,0x2B17,0x2B18,0x2B19,0x2B1A,0x2B1B,0x2B1C,0x2B1D,0x2B1E,0x2B1F,0x2B20,0x2B21,0x2B22,0x2B23,0x2B24,0x2B25,0x2B26,0x2B27,0x2B28,0x2B29,0x2B2A,0x2B2B,0x2B2C,0x2B2D,0x2B2E,0x2B2F,0x2B45,0x2B46,0x2B4D,0x2B4E,0x2B4F,0x2B50,0x2B51,0x2B52,0x2B53,0x2B54,0x2B55,0x2B56,0x2B57,0x2B58,0x2B59,0x2B5A,0x2B5B,0x2B5C,0x2B5D,0x2B5E,0x2B5F,0x2B60,0x2B61,0x2B62,0x2B63,0x2B64,0x2B65,0x2B66,0x2B67,0x2B68,0x2B69,0x2B6A,0x2B6B,0x2B6C,0x2B6D,0x2B6E,0x2B6F,0x2B70,0x2B71,0x2B72,0x2B73,0x2B76,0x2B77,0x2B78,0x2B79,0x2B7A,0x2B7B,0x2B7C,0x2B7D,0x2B7E,0x2B7F,0x2B80,0x2B81,0x2B82,0x2B83,0x2B84,0x2B85,0x2B86,0x2B87,0x2B88,0x2B89,0x2B8A,0x2B8B,0x2B8C,0x2B8D,0x2B8E,0x2B8F,0x2B90,0x2B91,0x2B92,0x2B93,0x2B94,0x2B95,0x2B97,0x2B98,0x2B99,0x2B9A,0x2B9B,0x2B9C,0x2B9D,0x2B9E,0x2B9F,0x2BA0,0x2BA1,0x2BA2,0x2BA3,0x2BA4,0x2BA5,0x2BA6,0x2BA7,0x2BA8,0x2BA9,0x2BAA,0x2BAB,0x2BAC,0x2BAD,0x2BAE,0x2BAF,0x2BB0,0x2BB1,0x2BB2,0x2BB3,0x2BB4,0x2BB5,0x2BB6,0x2BB7,0x2BB8,0x2BB9,0x2BBA,0x2BBB,0x2BBC,0x2BBD,0x2BBE,0x2BBF,0x2BC0,0x2BC1,0x2BC2,0x2BC3,0x2BC4,0x2BC5,0x2BC6,0x2BC7,0x2BC8,0x2BC9,0x2BCA,0x2BCB,0x2BCC,0x2BCD,0x2BCE,0x2BCF,0x2BD0,0x2BD1,0x2BD2,0x2BD3,0x2BD4,0x2BD5,0x2BD6,0x2BD7,0x2BD8,0x2BD9,0x2BDA,0x2BDB,0x2BDC,0x2BDD,0x2BDE,0x2BDF,0x2BE0,0x2BE1,0x2BE2,0x2BE3,0x2BE4,0x2BE5,0x2BE6,0x2BE7,0x2BE8,0x2BE9,0x2BEA,0x2BEB,0x2BEC,0x2BED,0x2BEE,0x2BEF,0x2BF0,0x2BF1,0x2BF2,0x2BF3,0x2BF4,0x2BF5,0x2BF6,0x2BF7,0x2BF8,0x2BF9,0x2BFA,0x2BFB,0x2BFC,0x2BFD,0x2BFE,0x2BFF,0x2CE5,0x2CE6,0x2CE7,0x2CE8,0x2CE9,0x2CEA,0x2E50,0x2E51,0x2E80,0x2E81,0x2E82,0x2E83,0x2E84,0x2E85,0x2E86,0x2E87,0x2E88,0x2E89,0x2E8A,0x2E8B,0x2E8C,0x2E8D,0x2E8E,0x2E8F,0x2E90,0x2E91,0x2E92,0x2E93,0x2E94,0x2E95,0x2E96,0x2E97,0x2E98,0x2E99,0x2E9B,0x2E9C,0x2E9D,0x2E9E,0x2E9F,0x2EA0,0x2EA1,0x2EA2,0x2EA3,0x2EA4,0x2EA5,0x2EA6,0x2EA7,0x2EA8,0x2EA9,0x2EAA,0x2EAB,0x2EAC,0x2EAD,0x2EAE,0x2EAF,0x2EB0,0x2EB1,0x2EB2,0x2EB3,0x2EB4,0x2EB5,0x2EB6,0x2EB7,0x2EB8,0x2EB9,0x2EBA,0x2EBB,0x2EBC,0x2EBD,0x2EBE,0x2EBF,0x2EC0,0x2EC1,0x2EC2,0x2EC3,0x2EC4,0x2EC5,0x2EC6,0x2EC7,0x2EC8,0x2EC9,0x2ECA,0x2ECB,0x2ECC,0x2ECD,0x2ECE,0x2ECF,0x2ED0,0x2ED1,0x2ED2,0x2ED3,0x2ED4,0x2ED5,0x2ED6,0x2ED7,0x2ED8,0x2ED9,0x2EDA,0x2EDB,0x2EDC,0x2EDD,0x2EDE,0x2EDF,0x2EE0,0x2EE1,0x2EE2,0x2EE3,0x2EE4,0x2EE5,0x2EE6,0x2EE7,0x2EE8,0x2EE9,0x2EEA,0x2EEB,0x2EEC,0x2EED,0x2EEE,0x2EEF,0x2EF0,0x2EF1,0x2EF2,0x2EF3,0x2F00,0x2F01,0x2F02,0x2F03,0x2F04,0x2F05,0x2F06,0x2F07,0x2F08,0x2F09,0x2F0A,0x2F0B,0x2F0C,0x2F0D,0x2F0E,0x2F0F,0x2F10,0x2F11,0x2F12,0x2F13,0x2F14,0x2F15,0x2F16,0x2F17,0x2F18,0x2F19,0x2F1A,0x2F1B,0x2F1C,0x2F1D,0x2F1E,0x2F1F,0x2F20,0x2F21,0x2F22,0x2F23,0x2F24,0x2F25,0x2F26,0x2F27,0x2F28,0x2F29,0x2F2A,0x2F2B,0x2F2C,0x2F2D,0x2F2E,0x2F2F,0x2F30,0x2F31,0x2F32,0x2F33,0x2F34,0x2F35,0x2F36,0x2F37,0x2F38,0x2F39,0x2F3A,0x2F3B,0x2F3C,0x2F3D,0x2F3E,0x2F3F,0x2F40,0x2F41,0x2F42,0x2F43,0x2F44,0x2F45,0x2F46,0x2F47,0x2F48,0x2F49,0x2F4A,0x2F4B,0x2F4C,0x2F4D,0x2F4E,0x2F4F,0x2F50,0x2F51,0x2F52,0x2F53,0x2F54,0x2F55,0x2F56,0x2F57,0x2F58,0x2F59,0x2F5A,0x2F5B,0x2F5C,0x2F5D,0x2F5E,0x2F5F,0x2F60,0x2F61,0x2F62,0x2F63,0x2F64,0x2F65,0x2F66,0x2F67,0x2F68,0x2F69,0x2F6A,0x2F6B,0x2F6C,0x2F6D,0x2F6E,0x2F6F,0x2F70,0x2F71,0x2F72,0x2F73,0x2F74,0x2F75,0x2F76,0x2F77,0x2F78,0x2F79,0x2F7A,0x2F7B,0x2F7C,0x2F7D,0x2F7E,0x2F7F,0x2F80,0x2F81,0x2F82,0x2F83,0x2F84,0x2F85,0x2F86,0x2F87,0x2F88,0x2F89,0x2F8A,0x2F8B,0x2F8C,0x2F8D,0x2F8E,0x2F8F,0x2F90,0x2F91,0x2F92,0x2F93,0x2F94,0x2F95,0x2F96,0x2F97,0x2F98,0x2F99,0x2F9A,0x2F9B,0x2F9C,0x2F9D,0x2F9E,0x2F9F,0x2FA0,0x2FA1,0x2FA2,0x2FA3,0x2FA4,0x2FA5,0x2FA6,0x2FA7,0x2FA8,0x2FA9,0x2FAA,0x2FAB,0x2FAC,0x2FAD,0x2FAE,0x2FAF,0x2FB0,0x2FB1,0x2FB2,0x2FB3,0x2FB4,0x2FB5,0x2FB6,0x2FB7,0x2FB8,0x2FB9,0x2FBA,0x2FBB,0x2FBC,0x2FBD,0x2FBE,0x2FBF,0x2FC0,0x2FC1,0x2FC2,0x2FC3,0x2FC4,0x2FC5,0x2FC6,0x2FC7,0x2FC8,0x2FC9,0x2FCA,0x2FCB,0x2FCC,0x2FCD,0x2FCE,0x2FCF,0x2FD0,0x2FD1,0x2FD2,0x2FD3,0x2FD4,0x2FD5,0x2FF0,0x2FF1,0x2FF2,0x2FF3,0x2FF4,0x2FF5,0x2FF6,0x2FF7,0x2FF8,0x2FF9,0x2FFA,0x2FFB,0x2FFC,0x2FFD,0x2FFE,0x2FFF,0x3004,0x3012,0x3013,0x3020,0x3036,0x3037,0x303E,0x303F,0x3190,0x3191,0x3196,0x3197,0x3198,0x3199,0x319A,0x319B,0x319C,0x319D,0x319E,0x319F,0x31C0,0x31C1,0x31C2,0x31C3,0x31C4,0x31C5,0x31C6,0x31C7,0x31C8,0x31C9,0x31CA,0x31CB,0x31CC,0x31CD,0x31CE,0x31CF,0x31D0,0x31D1,0x31D2,0x31D3,0x31D4,0x31D5,0x31D6,0x31D7,0x31D8,0x31D9,0x31DA,0x31DB,0x31DC,0x31DD,0x31DE,0x31DF,0x31E0,0x31E1,0x31E2,0x31E3,0x31E4,0x31E5,0x31EF,0x3200,0x3201,0x3202,0x3203,0x3204,0x3205,0x3206,0x3207,0x3208,0x3209,0x320A,0x320B,0x320C,0x320D,0x320E,0x320F,0x3210,0x3211,0x3212,0x3213,0x3214,0x3215,0x3216,0x3217,0x3218,0x3219,0x321A,0x321B,0x321C,0x321D,0x321E,0x322A,0x322B,0x322C,0x322D,0x322E,0x322F,0x3230,0x3231,0x3232,0x3233,0x3234,0x3235,0x3236,0x3237,0x3238,0x3239,0x323A,0x323B,0x323C,0x323D,0x323E,0x323F,0x3240,0x3241,0x3242,0x3243,0x3244,0x3245,0x3246,0x3247,0x3250,0x3260,0x3261,0x3262,0x3263,0x3264,0x3265,0x3266,0x3267,0x3268,0x3269,0x326A,0x326B,0x326C,0x326D,0x326E,0x326F,0x3270,0x3271,0x3272,0x3273,0x3274,0x3275,0x3276,0x3277,0x3278,0x3279,0x327A,0x327B,0x327C,0x327D,0x327E,0x327F,0x328A,0x328B,0x328C,0x328D,0x328E,0x328F,0x3290,0x3291,0x3292,0x3293,0x3294,0x3295,0x3296,0x3297,0x3298,0x3299,0x329A,0x329B,0x329C,0x329D,0x329E,0x329F,0x32A0,0x32A1,0x32A2,0x32A3,0x32A4,0x32A5,0x32A6,0x32A7,0x32A8,0x32A9,0x32AA,0x32AB,0x32AC,0x32AD,0x32AE,0x32AF,0x32B0,0x32C0,0x32C1,0x32C2,0x32C3,0x32C4,0x32C5,0x32C6,0x32C7,0x32C8,0x32C9,0x32CA,0x32CB,0x32CC,0x32CD,0x32CE,0x32CF,0x32D0,0x32D1,0x32D2,0x32D3,0x32D4,0x32D5,0x32D6,0x32D7,0x32D8,0x32D9,0x32DA,0x32DB,0x32DC,0x32DD,0x32DE,0x32DF,0x32E0,0x32E1,0x32E2,0x32E3,0x32E4,0x32E5,0x32E6,0x32E7,0x32E8,0x32E9,0x32EA,0x32EB,0x32EC,0x32ED,0x32EE,0x32EF,0x32F0,0x32F1,0x32F2,0x32F3,0x32F4,0x32F5,0x32F6,0x32F7,0x32F8,0x32F9,0x32FA,0x32FB,0x32FC,0x32FD,0x32FE,0x32FF,0x3300,0x3301,0x3302,0x3303,0x3304,0x3305,0x3306,0x3307,0x3308,0x3309,0x330A,0x330B,0x330C,0x330D,0x330E,0x330F,0x3310,0x3311,0x3312,0x3313,0x3314,0x3315,0x3316,0x3317,0x3318,0x3319,0x331A,0x331B,0x331C,0x331D,0x331E,0x331F,0x3320,0x3321,0x3322,0x3323,0x3324,0x3325,0x3326,0x3327,0x3328,0x3329,0x332A,0x332B,0x332C,0x332D,0x332E,0x332F,0x3330,0x3331,0x3332,0x3333,0x3334,0x3335,0x3336,0x3337,0x3338,0x3339,0x333A,0x333B,0x333C,0x333D,0x333E,0x333F,0x3340,0x3341,0x3342,0x3343,0x3344,0x3345,0x3346,0x3347,0x3348,0x3349,0x334A,0x334B,0x334C,0x334D,0x334E,0x334F,0x3350,0x3351,0x3352,0x3353,0x3354,0x3355,0x3356,0x3357,0x3358,0x3359,0x335A,0x335B,0x335C,0x335D,0x335E,0x335F,0x3360,0x3361,0x3362,0x3363,0x3364,0x3365,0x3366,0x3367,0x3368,0x3369,0x336A,0x336B,0x336C,0x336D,0x336E,0x336F,0x3370,0x3371,0x3372,0x3373,0x3374,0x3375,0x3376,0x3377,0x3378,0x3379,0x337A,0x337B,0x337C,0x337D,0x337E,0x337F,0x3380,0x3381,0x3382,0x3383,0x3384,0x3385,0x3386,0x3387,0x3388,0x3389,0x338A,0x338B,0x338C,0x338D,0x338E,0x338F,0x3390,0x3391,0x3392,0x3393,0x3394,0x3395,0x3396,0x3397,0x3398,0x3399,0x339A,0x339B,0x339C,0x339D,0x339E,0x339F,0x33A0,0x33A1,0x33A2,0x33A3,0x33A4,0x33A5,0x33A6,0x33A7,0x33A8,0x33A9,0x33AA,0x33AB,0x33AC,0x33AD,0x33AE,0x33AF,0x33B0,0x33B1,0x33B2,0x33B3,0x33B4,0x33B5,0x33B6,0x33B7,0x33B8,0x33B9,0x33BA,0x33BB,0x33BC,0x33BD,0x33BE,0x33BF,0x33C0,0x33C1,0x33C2,0x33C3,0x33C4,0x33C5,0x33C6,0x33C7,0x33C8,0x33C9,0x33CA,0x33CB,0x33CC,0x33CD,0x33CE,0x33CF,0x33D0,0x33D1,0x33D2,0x33D3,0x33D4,0x33D5,0x33D6,0x33D7,0x33D8,0x33D9,0x33DA,0x33DB,0x33DC,0x33DD,0x33DE,0x33DF,0x33E0,0x33E1,0x33E2,0x33E3,0x33E4,0x33E5,0x33E6,0x33E7,0x33E8,0x33E9,0x33EA,0x33EB,0x33EC,0x33ED,0x33EE,0x33EF,0x33F0,0x33F1,0x33F2,0x33F3,0x33F4,0x33F5,0x33F6,0x33F7,0x33F8,0x33F9,0x33FA,0x33FB,0x33FC,0x33FD,0x33FE,0x33FF,0x4DC0,0x4DC1,0x4DC2,0x4DC3,0x4DC4,0x4DC5,0x4DC6,0x4DC7,0x4DC8,0x4DC9,0x4DCA,0x4DCB,0x4DCC,0x4DCD,0x4DCE,0x4DCF,0x4DD0,0x4DD1,0x4DD2,0x4DD3,0x4DD4,0x4DD5,0x4DD6,0x4DD7,0x4DD8,0x4DD9,0x4DDA,0x4DDB,0x4DDC,0x4DDD,0x4DDE,0x4DDF,0x4DE0,0x4DE1,0x4DE2,0x4DE3,0x4DE4,0x4DE5,0x4DE6,0x4DE7,0x4DE8,0x4DE9,0x4DEA,0x4DEB,0x4DEC,0x4DED,0x4DEE,0x4DEF,0x4DF0,0x4DF1,0x4DF2,0x4DF3,0x4DF4,0x4DF5,0x4DF6,0x4DF7,0x4DF8,0x4DF9,0x4DFA,0x4DFB,0x4DFC,0x4DFD,0x4DFE,0x4DFF,0xA490,0xA491,0xA492,0xA493,0xA494,0xA495,0xA496,0xA497,0xA498,0xA499,0xA49A,0xA49B,0xA49C,0xA49D,0xA49E,0xA49F,0xA4A0,0xA4A1,0xA4A2,0xA4A3,0xA4A4,0xA4A5,0xA4A6,0xA4A7,0xA4A8,0xA4A9,0xA4AA,0xA4AB,0xA4AC,0xA4AD,0xA4AE,0xA4AF,0xA4B0,0xA4B1,0xA4B2,0xA4B3,0xA4B4,0xA4B5,0xA4B6,0xA4B7,0xA4B8,0xA4B9,0xA4BA,0xA4BB,0xA4BC,0xA4BD,0xA4BE,0xA4BF,0xA4C0,0xA4C1,0xA4C2,0xA4C3,0xA4C4,0xA4C5,0xA4C6,0xA828,0xA829,0xA82A,0xA82B,0xA836,0xA837,0xA839,0xAA77,0xAA78,0xAA79,0xFD40,0xFD41,0xFD42,0xFD43,0xFD44,0xFD45,0xFD46,0xFD47,0xFD48,0xFD49,0xFD4A,0xFD4B,0xFD4C,0xFD4D,0xFD4E,0xFD4F,0xFDCF,0xFDFD,0xFDFE,0xFDFF,0xFFE4,0xFFE8,0xFFED,0xFFEE,0xFFFC,0xFFFD};

size_t unicode_len_So = sizeof(unicode_So)/sizeof(unicode_So[0]);

int unicode_S[] = {-1};
size_t unicode_len_S = sizeof(unicode_S)/sizeof(unicode_S[0]);

int unicode_Zs[] = { 0x0020, 0x00A0, 0x1680, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000 };

size_t unicode_len_Zs = sizeof(unicode_Zs)/sizeof(unicode_Zs[0]);

int unicode_Zl[] = {0x2028};

size_t unicode_len_Zl = sizeof(unicode_Zl)/sizeof(unicode_Zl[0]);

int unicode_Zp[] = {0x2029};

size_t unicode_len_Zp = sizeof(unicode_Zp)/sizeof(unicode_Zp[0]);

int unicode_Z[] = {-1};
size_t unicode_len_Z = sizeof(unicode_Z)/sizeof(unicode_Z[0]);

int unicode_Cc[] = {0x0000,0x0001,0x0002,0x0003,0x0004,0x0005,0x0006,0x0007,0x0008,0x0009,0x000A,0x000B,0x000C,0x000D,0x000E,0x000F,0x0010,0x0011,0x0012,0x0013,0x0014,0x0015,0x0016,0x0017,0x0018,0x0019,0x001A,0x001B,0x001C,0x001D,0x001E,0x001F,0x007F,0x0080,0x0081,0x0082,0x0083,0x0084,0x0085,0x0086,0x0087,0x0088,0x0089,0x008A,0x008B,0x008C,0x008D,0x008E,0x008F,0x0090,0x0091,0x0092,0x0093,0x0094,0x0095,0x0096,0x0097,0x0098,0x0099,0x009A,0x009B,0x009C,0x009D,0x009E,0x009F};

size_t unicode_len_Cc = sizeof(unicode_Cc)/sizeof(unicode_Cc[0]);

int unicode_Cf[] = {0x00AD,0x0600,0x0601,0x0602,0x0603,0x0604,0x0605,0x061C,0x06DD,0x070F,0x0890,0x0891,0x08E2,0x110B,0x110C,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x1343,0x180E,0x1BCA,0x1BCA,0x1BCA,0x1BCA,0x1D17,0x1D17,0x1D17,0x1D17,0x1D17,0x1D17,0x1D17,0x1D17,0x200B,0x200C,0x200D,0x200E,0x200F,0x202A,0x202B,0x202C,0x202D,0x202E,0x2060,0x2061,0x2062,0x2063,0x2064,0x2066,0x2067,0x2068,0x2069,0x206A,0x206B,0x206C,0x206D,0x206E,0x206F,0xE000,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE002,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE003,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE004,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE005,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE006,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xE007,0xFEFF,0xFFF9,0xFFFA,0xFFFB};

size_t unicode_len_Cf = sizeof(unicode_Cf)/sizeof(unicode_Cf[0]);

int unicode_Cs[] = { -1 };
size_t unicode_len_Cs = sizeof(unicode_Cs)/sizeof(unicode_Cs[0]);

int unicode_Co[] = { -1 };
size_t unicode_len_Co = sizeof(unicode_Co)/sizeof(unicode_Co[0]);

int unicode_Cn[] = { -1 };
size_t unicode_len_Cn = sizeof(unicode_Cn)/sizeof(unicode_Cn[0]);

int unicode_C[] = { -1 };
size_t unicode_len_C = sizeof(unicode_C)/sizeof(unicode_C[0]);

const char **unicode_lookup_category_parts(const char *category)
{
#define X(cat,name,strings,explanation) \
    if (!strcmp(category, #cat)) return unicode_category_##cat;
UNICODE_CATEGORIES
#undef X
    return NULL;
}

bool unicode_get_category_part(const char *part, int **out, size_t *out_len)
{
#define X(cat,name,strings,explanation) \
    if (!strcmp(part, #cat)) \
    { \
        *out = unicode_##cat; \
        *out_len = unicode_len_##cat; \
        return true; \
    }
UNICODE_CATEGORIES
#undef X
    return false;
}

bool category_has_code(int code, int *cat, size_t cat_len)
{
    if (cat_len == 0) return false;
    if (cat_len == 1) return *cat == code;

    // Points to first item.
    int *start = cat;
    // Points to last item.
    int *stop = cat+cat_len-1;

    for (;;)
    {
        if (start == stop) return *start == code;
        if (start+1 == stop) return *start == code || *stop == code;

        // Take the middle.
        int *i = start+((stop-start)>>1);
        if (*i == code) return true;
        if (*i < code)
        {
            // Move to upper half.
            start = i+1;
        }
        else
        {
            // Move to lower half.
            stop = i-1;
        }
    }
}

const char *find_eol_or_stop(const char *start, const char *stop)
{
    const char *i = start;

    while (i < stop)
    {
        if (*i == '\n') return i;
        i++;
    }
    return stop;
}

bool is_hex(char c)
{
    return
        (c >= '0' && c <= '9') ||
        (c >= 'a' && c <= 'f') ||
        (c >= 'A' && c <= 'F');
}

unsigned char hex_value(char c)
{
    if (c >= '0' && c <= '9') return c-'0';
    if (c >= 'a' && c <= 'f') return 10+c-'a';
    if (c >= 'A' && c <= 'F') return 10+c-'A';
    assert(false);
    return 0;
}

bool is_unicode_whitespace(const char *start, const char *stop)
{
    size_t n = count_whitespace(start, stop);

    // Single char whitespace is ' ' '\t' '\n' '\r'
    // First unicode whitespace is 160 nbsp require two or more chars.
    return n > 1;
}

#endif // TEXT_MODULE

// PART C UTF8_C ////////////////////////////////////////

#ifdef UTF8_MODULE

size_t print_utf8_char(XMQPrintState *ps, const char *start, const char *stop)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    const char *i = start;

    // Find next utf8 char....
    const char *j = i+1;
    while (j < stop && (*j & 0xc0) == 0x80) j++;

    // Is the utf8 char a unicode whitespace and not space,tab,cr,nl?
    bool uw = is_unicode_whitespace(i, j);
    //bool tw = *i == '\t';

    // If so, then color it. This will typically red underline the non-breakable space.
    if (uw) print_color_pre(ps, COLOR_unicode_whitespace);

    if (*i == ' ')
    {
        write(writer_state, os->explicit_space, NULL);
    }
    else
    if (*i == '\t')
    {
        write(writer_state, os->explicit_tab, NULL);
    }
    else
    {
        const char *e = needs_escape(ps->output_settings->render_to, i, j);
        if (!e)
        {
            write(writer_state, i, j);
        }
        else
        {
            write(writer_state, e, NULL);
        }
    }
    if (uw) print_color_post(ps, COLOR_unicode_whitespace);

    ps->last_char = *i;
    ps->current_indent++;

    return j-start;
}

/**
   print_utf8_internal: Print a single string
   ps: The print state.
   start: Points to bytes to be printed.
   stop: Points to byte after last byte to be printed. If NULL then assume start is null-terminated.

   Returns number of bytes printed.
*/
size_t print_utf8_internal(XMQPrintState *ps, const char *start, const char *stop)
{
    if (stop == start || *start == 0) return 0;

    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    size_t u_len = 0;

    const char *i = start;
    while (*i && (!stop || i < stop))
    {
        // Find next utf8 char....
        const char *j = i+1;
        while (j < stop && (*j & 0xc0) == 0x80) j++;

        // Is the utf8 char a unicode whitespace and not space,tab,cr,nl?
        bool uw = is_unicode_whitespace(i, j);
        //bool tw = *i == '\t';

        // If so, then color it. This will typically red underline the non-breakable space.
        if (uw) print_color_pre(ps, COLOR_unicode_whitespace);

        if (*i == ' ')
        {
            write(writer_state, os->explicit_space, NULL);
        }
        else if (*i == '\t')
        {
            write(writer_state, os->explicit_tab, NULL);
        }
        else
        {
            const char *e = needs_escape(ps->output_settings->render_to, i, j);
            if (!e)
            {
                write(writer_state, i, j);
            }
            else
            {
                write(writer_state, e, NULL);
            }
        }
        if (uw) print_color_post(ps, COLOR_unicode_whitespace);
        u_len++;
        i = j;
    }

    ps->last_char = *(i-1);
    ps->current_indent += u_len;
    return i-start;
}

/**
   print_utf8:
   @ps: The print state.
   @c:  The color.
   @num_pairs:  Number of start, stop pairs.
   @start: First utf8 byte to print.
   @stop: Points to byte after the last utf8 content.

   Returns the number of bytes used after start.
*/
size_t print_utf8(XMQPrintState *ps, XMQColor color, size_t num_pairs, ...)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    const char *pre, *post;
    getThemeStrings(os, color, &pre, &post);
    const char *previous_color = NULL;

    if (pre)
    {
        write(writer_state, pre, NULL);
        previous_color = ps->replay_active_color_pre;
        ps->replay_active_color_pre = pre;
    }

    size_t b_len = 0;

    va_list ap;
    va_start(ap, num_pairs);
    for (size_t x = 0; x < num_pairs; ++x)
    {
        const char *start = va_arg(ap, const char *);
        const char *stop = va_arg(ap, const char *);
        b_len += print_utf8_internal(ps, start, stop);
    }
    va_end(ap);

    if (post)
    {
        write(writer_state, post, NULL);
    }
    if (previous_color)
    {
        ps->replay_active_color_pre = previous_color;
    }

    return b_len;
}

#endif // UTF8_MODULE

// PART C VECTOR_C ////////////////////////////////////////

#ifdef VECTOR_MODULE

Vector *vector_create()
{
    Vector *s = (Vector*)malloc(sizeof(Vector));
    memset(s, 0, sizeof(Vector));
    s->elements_size = 16;
    s->elements = (void**)calloc(16, sizeof(void*));
    return s;
}

void vector_free(Vector *vector)
{
    if (!vector) return;

    vector->size = 0;
    vector->elements_size = 0;
    if (vector->elements) free(vector->elements);
    vector->elements = NULL;
    free(vector);
}

void vector_free_and_values(Vector *v, FreeFuncPtr freefunc)
{
    for (size_t i = 0; i < v->size; ++i)
    {
        void *e = v->elements[i];
        if (e)
        {
            freefunc(e);
            v->elements[i] = 0;
        }
    }
    vector_free(v);
}

void vector_push_back(Vector *vector, void *data)
{
    assert(vector);

    vector->size++;
    if (vector->size > vector->elements_size)
    {
        if (vector->elements_size >= 1024)
        {
            vector->elements_size += 1024;
        }
        else
        {
            vector->elements_size *= 2;
        }
        vector->elements = (void**)realloc(vector->elements, vector->elements_size * sizeof(void*));
        assert(vector->elements);
    }
    vector->elements[vector->size-1] = data;
}

void *vector_pop_back(Vector *v)
{
    assert(v->size > 0);

    v->size--;
    void *r = v->elements[v->size];
    v->elements[v->size] = NULL;
    return r;
}

void *vector_element_at(Vector *v, size_t i)
{
    assert(i < v->size);

    return v->elements[i];
}

#endif // VECTOR_MODULE

// PART C XML_C ////////////////////////////////////////

#ifdef XML_MODULE

xmlNode *xml_first_child(xmlNode *node)
{
    return node->children;
}

xmlNode *xml_last_child(xmlNode *node)
{
    return node->last;
}

xmlNode *xml_next_sibling(xmlNode *node)
{
    return node->next;
}

xmlNode *xml_prev_sibling(xmlNode *node)
{
    return node->prev;
}

xmlAttr *xml_first_attribute(xmlNode *node)
{
    return node->properties;
}

xmlAttr *xml_next_attribute(xmlAttr *attr)
{
    return attr->next;
}

xmlAttr *xml_get_attribute(xmlNode *node, const char *name)
{
    return xmlHasProp(node, (const xmlChar*)name);
}

xmlNs *xml_first_namespace_def(xmlNode *node)
{
    return node->nsDef;
}

bool xml_non_empty_namespace(xmlNs *ns)
{
    const char *prefix = (const char*)ns->prefix;
    const char *href = (const char*)ns->href;

    // These three are non_empty.
    //   xmlns = "something"
    //   xmlns:something = ""
    //   xmlns:something = "something"
    return (href && href[0]) || (prefix && prefix[0]);
}

bool xml_has_non_empty_namespace_defs(xmlNode *node)
{
    xmlNs *ns = node->nsDef;
    if (ns) return true;
    /*!= NULL)
    while (ns)
    {
        //if (xml_non_empty_namespace(ns)) return true;
        ns = xml_next_namespace_def(ns);
    }
    */
    return false;
}

xmlNs *xml_next_namespace_def(xmlNs *ns)
{
    return ns->next;
}

const char*xml_element_name(xmlNode *node)
{
    return (const char*)node->name;
}

const char*xml_element_content(xmlNode *node)
{
    return (const char*)node->content;
}

const char *xml_element_ns_prefix(const xmlNode *node)
{
    if (!node->ns) return NULL;
    return (const char*)node->ns->prefix;
}

const char *xml_attr_key(xmlAttr *attr)
{
    return (const char*)attr->name;
}

const char* xml_namespace_href(xmlNs *ns)
{
    return (const char*)ns->href;
}

bool is_entity_node(const xmlNode *node)
{
    return node->type == XML_ENTITY_NODE ||
        node->type == XML_ENTITY_REF_NODE;
}

bool is_content_node(const xmlNode *node)
{
    return node->type == XML_TEXT_NODE ||
        node->type == XML_CDATA_SECTION_NODE;
}

bool is_comment_node(const xmlNode *node)
{
    return node->type == XML_COMMENT_NODE;
}

bool is_pi_node(const xmlNode *node)
{
    return node->type == XML_PI_NODE;
}

bool is_doctype_node(const xmlNode *node)
{
    return node->type == XML_DTD_NODE;
}

bool is_element_node(const xmlNode *node)
{
    return node->type == XML_ELEMENT_NODE;
}

bool is_attribute_node(const xmlNode *node)
{
    return node->type == XML_ATTRIBUTE_NODE;
}

bool is_text_node(const xmlNode *node)
{
    return node->type == XML_TEXT_NODE;
}

bool is_key_value_node(xmlNodePtr node)
{
    void *from = xml_first_child(node);
    void *to = xml_last_child(node);

    // Single content or entity node.
    bool yes = from && from == to && (is_content_node((xmlNode*)from) || is_entity_node((xmlNode*)from));
    if (yes) return true;

    if (!from) return false;

    // Multiple text or entity nodes.
    xmlNode *i = node->children;
    while (i)
    {
        xmlNode *next = i->next;
        if (i->type != XML_TEXT_NODE &&
            i->type != XML_ENTITY_REF_NODE)
        {
            // Found something other than text or character entities.
            return false;
        }
        i = next;
    }
    return true;
}

bool is_single_empty_text_node(xmlNodePtr node)
{
    if (is_entity_node(node)) return false;
    if (node->type != XML_TEXT_NODE) return false;
    const char *c = (const char*)node->content;
    if (c != NULL && *c != 0) return false;
    return true;
}

bool is_leaf_node(xmlNode *node)
{
    return xml_first_child(node) == NULL;
}

bool has_attributes(xmlNodePtr node)
{
    return NULL != xml_first_attribute(node);
}

void free_xml(xmlNode * node)
{
    while(node)
    {
        xmlNode *next = node->next;
        free_xml(node->children);
        xmlFreeNode(node);
        node = next;
    }
}

char *xml_collapse_text(xmlNode *node)
{
    xmlNode *i = node->children;
    size_t len = 0;
    size_t num_text = 0;
    size_t num_entities = 0;

    while (i)
    {
        xmlNode *next = i->next;
        if (i->type != XML_TEXT_NODE &&
            i->type != XML_ENTITY_REF_NODE)
        {
            // Found something other than text or character entities.
            // Cannot collapse.
            return NULL;
        }
        if (i->type == XML_TEXT_NODE)
        {
            len += strlen((const char*)i->content);
            num_text++;
        }
        else
        {
            // &apos;
            len += 2 + strlen((const char*)i->name);
            num_entities++;
        }
        i = next;
    }

    // It is already collapsed.
    if (num_text <= 1 && num_entities == 0) return NULL;

    char *buf = (char*)malloc(len+1);
    char *out = buf;
    i = node->children;
    while (i)
    {
        xmlNode *next = i->next;
        if (i->type == XML_TEXT_NODE)
        {
            size_t l = strlen((const char *)i->content);
            memcpy(out, i->content, l);
            out += l;
        }
        else
        {
            int uc = decode_entity_ref((const char *)i->name);
            UTF8Char utf8;
            size_t n = encode_utf8(uc, &utf8);
            for (size_t j = 0; j < n; ++j)
            {
                *out++ = utf8.bytes[j];
            }
        }
        i = next;
    }
    *out = 0;
    out++;
    buf = (char*)realloc(buf, out-buf);
    return buf;
}

int decode_entity_ref(const char *name)
{
    if (!strcmp(name, "apos")) return '\'';
    if (!strcmp(name, "gt")) return '>';
    if (!strcmp(name, "lt")) return '<';
    if (!strcmp(name, "quot")) return '"';
    if (!strcmp(name, "nbsp")) return 160;
    if (name[0] != '#') return 0;
    if (name[1] == 'x') {
        long v = strtol((const char*)name, NULL, 16);
        return (int)v;
    }
    return atoi(name+1);
}

void xml_add_root_child(xmlDoc *doc, xmlNode *node)
{
    if (doc->children == NULL)
    {
        doc->children = node;
        doc->last = node;
    }
    else
    {
        xmlNode *prev = doc->last;
        prev->next = node;
        node->prev = prev;
        doc->last = node;
    }
}

#endif // XML_MODULE

// PART C XMQ_INTERNALS_C ////////////////////////////////////////

#ifdef XMQ_INTERNALS_MODULE

void generate_state_error_message(XMQParseState *state, XMQParseError error_nr, const char *start, const char *stop)
{
    // Error detected during parsing and this is where the longjmp will end up!
    if (!state->generating_error_msg)
    {
        state->generating_error_msg = new_membuffer();
    }
    else
    {
        membuffer_drop_last_null(state->generating_error_msg);
        membuffer_append(state->generating_error_msg, "\n");
    }

    const char *error = xmqParseErrorToString(error_nr);

    const char *statei = state->i;
    size_t line = state->line;
    size_t col = state->col;

    // For certain errors we have a better point where the error triggered.
    if (error_nr == XMQ_ERROR_BODY_NOT_CLOSED)
    {
        statei = state->last_body_start;
        line = state->last_body_start_line;
        col = state->last_body_start_col;
    }
    else if (error_nr == XMQ_ERROR_ATTRIBUTES_NOT_CLOSED)
    {
        statei = state->last_attr_start;
        line = state->last_attr_start_line;
        col = state->last_attr_start_col;
    }
    else if (error_nr == XMQ_ERROR_QUOTE_NOT_CLOSED)
    {
        statei = state->last_quote_start;
        line = state->last_quote_start_line;
        col = state->last_quote_start_col;
    }
    else if (error_nr == XMQ_ERROR_EXPECTED_CONTENT_AFTER_EQUALS)
    {
        statei = state->last_equals_start;
        line = state->last_equals_start_line;
        col = state->last_equals_start_col;
    }
    else if (error_nr == XMQ_WARNING_QUOTES_NEEDED)
    {
        statei = state->last_suspicios_quote_end;
        line = state->last_suspicios_quote_end_line;
        col = state->last_suspicios_quote_end_col;
    }

    // Move pointer to the beginning of the line and
    // calculate the indent for the line so that we
    // can position the ^ marker under the problematic character.
    // Give up on lines longer than 1024 characters.
    size_t line_length = 0;
    size_t indent = 0;

    const char *line_start = statei;
    while (line_start > start && *(line_start-1) != '\n' && line_length < 1024)
    {
        line_length++;
        line_start--;
        indent++;
    }

    // Now find the end of the line.
    const char *line_stop = statei;
    while (line_stop < stop && *line_stop && *line_stop != '\n' && line_length < 1024)
    {
        line_length++;
        line_stop++;
    }

    const char *char_error = "";
    char buf[32];

    if (error_nr == XMQ_ERROR_INVALID_CHAR ||
        error_nr == XMQ_ERROR_JSON_INVALID_CHAR)
    {
        UTF8Char utf8_char;
        peek_utf8_char(statei, stop, &utf8_char);
        char utf8_codepoint[8];
        utf8_char_to_codepoint_string(&utf8_char, utf8_codepoint);

        snprintf(buf, 32, " \"%s\" %s", utf8_char.bytes, utf8_codepoint);
        char_error = buf;
    }

    char line_error[1024];
    line_error[0] = 0;
    if (statei < stop)
    {
        snprintf(line_error, 1024, "\n%.*s\n %*s",
                 (int)line_length,
                 line_start,
                 (int)indent,
                 "^");
    }

    const char *e_or_w = "error";
    if (error_nr >= XMQ_WARNING_QUOTES_NEEDED)
    {
        e_or_w = "warning";
    }
    char error_msg[2048];
    memset(error_msg, 0, sizeof(error_msg));
    snprintf(error_msg, 2048,
             "%s:%zu:%zu: %s: %s%s%s%s",
             state->source_name,
             line, col,
             e_or_w,
             error,
             state->error_info ? state->error_info : "",
             char_error,
             line_error
        );
    error_msg[2047] = 0;
    membuffer_append(state->generating_error_msg, error_msg);
    membuffer_append_null(state->generating_error_msg);
}

const char *xmqParseErrorToString(XMQParseError e)
{
    switch (e)
    {
    case XMQ_ERROR_NONE: return "no warning, no error";
    case XMQ_ERROR_CANNOT_READ_FILE: return "cannot read file";
    case XMQ_ERROR_OOM: return "out of memory";
    case XMQ_ERROR_NOT_XMQ: return "input file is not xmq";
    case XMQ_ERROR_QUOTE_NOT_CLOSED: return "quote is not closed";
    case XMQ_ERROR_ENTITY_NOT_CLOSED: return "entity is not closed";
    case XMQ_ERROR_COMMENT_NOT_CLOSED: return "comment is not closed";
    case XMQ_ERROR_COMMENT_CLOSED_WITH_TOO_MANY_SLASHES: return "comment closed with too many slashes";
    case XMQ_ERROR_BODY_NOT_CLOSED: return "body is not closed";
    case XMQ_ERROR_ATTRIBUTES_NOT_CLOSED: return "attributes are not closed";
    case XMQ_ERROR_COMPOUND_NOT_CLOSED: return "compound is not closed";
    case XMQ_ERROR_COMPOUND_MAY_NOT_CONTAIN: return "compound may only contain quotes and entities";
    case XMQ_ERROR_QUOTE_CLOSED_WITH_TOO_MANY_QUOTES: return "quote closed with too many quotes";
    case XMQ_ERROR_UNEXPECTED_CLOSING_BRACE: return "unexpected closing brace";
    case XMQ_ERROR_EXPECTED_CONTENT_AFTER_EQUALS: return "expected content after equals";
    case XMQ_ERROR_UNEXPECTED_TAB: return "invalid tab character found, remember that tab is not allowed as a field separator, to store tab as content it must be quoted";
    case XMQ_ERROR_INVALID_CHAR: return "unexpected character";
    case XMQ_ERROR_BAD_DOCTYPE: return "doctype could not be parsed";
    case XMQ_ERROR_CANNOT_HANDLE_XML: return "cannot handle xml use libxmq-all for this!";
    case XMQ_ERROR_CANNOT_HANDLE_HTML: return "cannot handle html use libxmq-all for this!";
    case XMQ_ERROR_CANNOT_HANDLE_JSON: return "cannot handle json use libxmq-all for this!";
    case XMQ_ERROR_JSON_INVALID_ESCAPE: return "invalid json escape";
    case XMQ_ERROR_JSON_INVALID_CHAR: return "unexpected json character";
    case XMQ_ERROR_EXPECTED_XMQ: return "expected xmq source";
    case XMQ_ERROR_EXPECTED_HTMQ: return "expected htmlq source";
    case XMQ_ERROR_EXPECTED_XML: return "expected xml source";
    case XMQ_ERROR_EXPECTED_HTML: return "expected html source";
    case XMQ_ERROR_EXPECTED_JSON: return "expected json source";
    case XMQ_ERROR_PARSING_XML: return "error parsing xml";
    case XMQ_ERROR_PARSING_HTML: return "error parsing html";
    case XMQ_ERROR_VALUE_CANNOT_START_WITH: return "value cannot start with = /* or //";
    case XMQ_ERROR_IXML_SYNTAX_ERROR: return "syntax ";
    case XMQ_WARNING_QUOTES_NEEDED: return "perhaps you need more quotes to quote this quote";
    }
    assert(false);
    return "unknown error";
}

const char *needs_escape(XMQRenderFormat f, const char *start, const char *stop)
{
    if (f == XMQ_RENDER_HTML)
    {
        char c = *start;
        if (c == '&') return "&amp;";
        if (c == '<') return "&lt;";
        if (c == '>') return "&gt;";
        return NULL;
    }
    else if (f == XMQ_RENDER_TEX)
    {
        char c = *start;
        if (c == '\\') return "\\backslash;";
        if (c == '&') return "\\&";
        if (c == '#') return "\\#";
        if (c == '{') return "\\{";
        if (c == '}') return "\\}";
        if (c == '_') return "\\_";
        if (c == '\'') return "{'}";

        return NULL;
    }

    return NULL;
}

/**
    enter_compound_level:

    When parsing a value and the parser has entered a compound value,
    the level for the child quotes/entities are now handled as
    tokens inside the compound.

    LEVEL_ELEMENT_VALUE -> LEVEL_ELEMENT_VALUE_COMPOUND
    LEVEL_ATTR_VALUE -> LEVEL_ATTR_VALUE_COMPOUND
*/
Level enter_compound_level(Level l)
{
    assert(l != 0);
    return (Level)(((int)l)+1);
}

XMQColor level_to_quote_color(Level level)
{
    switch (level)
    {
    case LEVEL_XMQ: return COLOR_quote;
    case LEVEL_ELEMENT_VALUE: return COLOR_element_value_quote;
    case LEVEL_ELEMENT_VALUE_COMPOUND: return COLOR_element_value_compound_quote;
    case LEVEL_ATTR_VALUE: return COLOR_attr_value_quote;
    case LEVEL_ATTR_VALUE_COMPOUND: return COLOR_attr_value_compound_quote;
    }
    assert(false);
    return COLOR_none;
}

XMQColor level_to_entity_color(Level level)
{
    switch (level)
    {
    case LEVEL_XMQ: return COLOR_entity;
    case LEVEL_ELEMENT_VALUE: return COLOR_element_value_entity;
    case LEVEL_ELEMENT_VALUE_COMPOUND: return COLOR_element_value_compound_entity;
    case LEVEL_ATTR_VALUE: return COLOR_attr_value_entity;
    case LEVEL_ATTR_VALUE_COMPOUND: return COLOR_attr_value_compound_entity;
    }
    assert(false);
    return COLOR_none;
}

bool load_stdin(XMQDoc *doq, size_t *out_fsize, const char **out_buffer)
{
    bool rc = true;
    int blocksize = 1024;
    char block[blocksize];

    MemBuffer *mb = new_membuffer();

    int fd = 0;
    while (true) {
        ssize_t n = read(fd, block, sizeof(block));
        if (n == 0) {
            break;
        }
        if (n == -1) {
            if (errno == EINTR) {
                continue;
            }
            PRINT_ERROR("Could not read stdin errno=%d\n", errno);
            close(fd);

            return false;
        }
        membuffer_append_region(mb, block, block + n);
    }
    close(fd);

    membuffer_append_null(mb);

    *out_fsize = mb->used_-1;
    *out_buffer = free_membuffer_but_return_trimmed_content(mb);

    return rc;
}

bool load_file(XMQDoc *doq, const char *file, size_t *out_fsize, const char **out_buffer)
{
    if (file == NULL || (file[0] == '-' && file[1] == 0))
    {
        return load_stdin(doq, out_fsize, out_buffer);
    }

    bool rc = false;
    char *buffer = NULL;
    size_t block_size = 0;
    size_t n = 0;

    FILE *f = fopen(file, "rb");
    if (!f) {
        doq->errno_ = XMQ_ERROR_CANNOT_READ_FILE;
        doq->error_ = build_error_message("xmq: %s: No such file or directory\n", file);
        return false;
    }

    fseek(f, 0, SEEK_END);
    size_t fsize = ftell(f);
    fseek(f, 0, SEEK_SET);

    debug("xmq=", "file size %zu", fsize);

    buffer = (char*)malloc(fsize + 1);
    if (!buffer)
    {
        doq->errno_ = XMQ_ERROR_OOM;
        doq->error_ = build_error_message("xmq: %s: File too big, out of memory\n", file);
        goto exit;
    }

    block_size = fsize;
    if (block_size > 10000) block_size = 10000;
    n = 0;
    do {
        if (n + block_size > fsize) block_size = fsize - n;
        size_t r = fread(buffer+n, 1, block_size, f);
        debug("xmq=", "read %zu bytes total %zu", r, n);
        if (!r) break;
        n += r;
    } while (n < fsize);

    debug("xmq=", "read total %zu bytes fsize %zu bytes", n, fsize);

    if (n != fsize) {
        rc = false;
        doq->errno_ = XMQ_ERROR_CANNOT_READ_FILE;
        doq->error_ = build_error_message("xmq: %s: Cannot read file\n", file);
        goto exit;
    }
    fclose(f);
    buffer[fsize] = 0;
    rc = true;

exit:

    *out_fsize = fsize;
    *out_buffer = buffer;
    return rc;
}

const char *build_error_message(const char* fmt, ...)
{
    char *buf = (char*)malloc(4096);
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, 4096, fmt, args);
    va_end(args);
    buf[4095] = 0;
    buf = (char*)realloc(buf, strlen(buf)+1);
    return buf;
}

#endif // XMQ_INTERNALS_MODULE

// PART C XMQ_PARSER_C ////////////////////////////////////////

#ifdef XMQ_PARSER_MODULE

size_t count_xmq_slashes(const char *i, const char *stop, bool *found_asterisk);
void eat_json_quote(XMQParseState *state, char **content_start, char **content_stop);
void eat_xmq_comment_to_close(XMQParseState *state, const char **content_start, const char **content_stop, size_t n, bool *found_asterisk);
void eat_xmq_comment_to_eol(XMQParseState *state, const char **content_start, const char **content_stop);
void eat_xmq_doctype(XMQParseState *state, const char **text_start, const char **text_stop);
void eat_xmq_entity(XMQParseState *state);
void eat_xmq_pi(XMQParseState *state, const char **text_start, const char **text_stop);
void eat_xmq_text_name(XMQParseState *state, const char **content_start, const char **content_stop, const char **namespace_start, const char **namespace_stop);
void eat_xmq_text_value(XMQParseState *state);
void eat_xmq_token_whitespace(XMQParseState *state, const char **start, const char **stop);
bool is_xmq_attribute_key_start(char c);
bool is_xmq_comment_start(char c, char cc);
bool is_xmq_compound_start(char c);
bool is_xmq_doctype_start(const char *start, const char *stop);
bool is_xmq_entity_start(char c);
bool is_xmq_json_quote_start(char c);
bool is_xmq_pi_start(const char *start, const char *stop);
bool is_xmq_quote_start(char c);
void parse_xmq_attribute(XMQParseState *state);
void parse_xmq_attributes(XMQParseState *state);
void parse_xmq_comment(XMQParseState *state, char cc);
void parse_xmq_compound(XMQParseState *state, Level level);
void parse_xmq_compound_children(XMQParseState *state, Level level);
void parse_xmq_doctype(XMQParseState *state);
void parse_xmq_element(XMQParseState *state);
void parse_xmq_element_internal(XMQParseState *state, bool doctype, bool pi);
void parse_xmq_entity(XMQParseState *state, Level level);
void parse_xmq_json_quote(XMQParseState *state, Level level);
void parse_xmq_pi(XMQParseState *state);
void parse_xmq_quote(XMQParseState *state, Level level);
void parse_xmq_text_any(XMQParseState *state);
void parse_xmq_text_name(XMQParseState *state);
void parse_xmq_text_value(XMQParseState *state, Level level);
void parse_xmq_value(XMQParseState *state, Level level);
void parse_xmq_whitespace(XMQParseState *state);
bool peek_xmq_next_is_equal(XMQParseState *state);
bool possibly_lost_content_after_equals(XMQParseState *state);
bool possibly_need_more_quotes(XMQParseState *state);

size_t count_xmq_quotes(const char *i, const char *stop)
{
    const char *start = i;
    char c = *i;

    assert(c == '\'' || c == '"');

    while (i < stop && *i == c) i++;

    return i-start;
}

void eat_xmq_quote(XMQParseState *state, const char **start, const char **stop)
{
    const char *i = state->i;
    const char *end = state->buffer_stop;
    const char q = *i;

    assert(q == '\'' || q == '"');

    size_t line = state->line;
    size_t col = state->col;

    size_t depth = count_xmq_quotes(i, end);
    size_t count = depth;

    state->last_quote_start = state->i;
    state->last_quote_start_line = state->line;
    state->last_quote_start_col = state->col;

    *start = i;

    while (count > 0)
    {
        increment(q, 1, &i, &line, &col);
        count--;
    }

    if (depth == 2)
    {
        // The empty quote ''
        state->i = i;
        state->line = line;
        state->col = col;
        *stop = i;
        return;
    }

    while (i < end)
    {
        char c = *i;
        if (c != q)
        {
            increment(c, 1, &i, &line, &col);
            continue;
        }
        size_t count = count_xmq_quotes(i, end);
        if (count > depth)
        {
            state->error_nr = XMQ_ERROR_QUOTE_CLOSED_WITH_TOO_MANY_QUOTES;
            longjmp(state->error_handler, 1);
        }
        else
        if (count < depth)
        {
            while (count > 0)
            {
                increment(q, 1, &i, &line, &col);
                count--;
            }
            continue;
        }
        else
        if (count == depth)
        {
            while (count > 0)
            {
                increment(q, 1, &i, &line, &col);
                count--;
            }
            depth = 0;
            *stop = i;
            break;
        }
    }
    if (depth != 0)
    {
        state->error_nr = XMQ_ERROR_QUOTE_NOT_CLOSED;
        longjmp(state->error_handler, 1);
    }
    state->i = i;
    state->line = line;
    state->col = col;

    if (possibly_need_more_quotes(state))
    {
        state->last_suspicios_quote_end = state->i-1;
        state->last_suspicios_quote_end_line = state->line;
        state->last_suspicios_quote_end_col = state->col-1;
    }
}

void eat_xml_whitespace(XMQParseState *state, const char **start, const char **stop)
{
    const char *i = state->i;
    const char *buffer_stop = state->buffer_stop;
    size_t line = state->line;
    size_t col = state->col;
    if (start) *start = i;

    size_t nw = count_whitespace(i, buffer_stop);
    if (!nw) return;

    while (i < buffer_stop)
    {
        size_t nw = count_whitespace(i, buffer_stop);
        if (!nw) break;
        // Pass the first char, needed to detect '\n' which increments line and set cols to 1.
        increment(*i, nw, &i, &line, &col);
    }

    if (stop) *stop = i;
    state->i = i;
    state->line = line;
    state->col = col;
}

void eat_xmq_token_whitespace(XMQParseState *state, const char **start, const char **stop)
{
    const char *i = state->i;
    const char *buffer_stop = state->buffer_stop;
    size_t line = state->line;
    size_t col = state->col;
    if (start) *start = i;

    size_t nw = count_whitespace(i, buffer_stop);
    if (!nw) return;

    while (i < buffer_stop)
    {
        size_t nw = count_whitespace(i, buffer_stop);
        if (!nw) break;
        // Tabs are not permitted as xmq token whitespace.
        if (nw == 1 && *i == '\t') break;
        // Pass the first char, needed to detect '\n' which increments line and set cols to 1.
        increment(*i, nw, &i, &line, &col);
    }

    if (stop) *stop = i;
    state->i = i;
    state->line = line;
    state->col = col;
}

void increment(char c, size_t num_bytes, const char **i, size_t *line, size_t *col)
{
    if (c == 0) c = *(*i);
    if ((c & 0xc0) != 0x80) // Just ignore UTF8 parts since they do not change the line or col.
    {
        (*col)++;
        if (c == '\n')
        {
            (*line)++;
            (*col) = 1;
        }
    }
    assert(num_bytes > 0);
    (*i)+=num_bytes;
}

void eat_xmq_entity(XMQParseState *state)
{
    const char *i = state->i;
    const char *end = state->buffer_stop;

    size_t line = state->line;
    size_t col = state->col;
    increment('&', 1, &i, &line, &col);

    char c = 0;
    bool expect_semicolon = false;

    while (i < end)
    {
        c = *i;
        if (!is_xmq_text_name(c)) break;
        if (!is_lowercase_hex(c)) expect_semicolon = true;
        increment(c, 1, &i, &line, &col);
    }
    if (c == ';')
    {
        increment(c, 1, &i, &line, &col);
        c = *i;
        expect_semicolon = false;
    }
    if (expect_semicolon)
    {
        state->error_nr = XMQ_ERROR_ENTITY_NOT_CLOSED;
        longjmp(state->error_handler, 1);
    }

    state->i = i;
    state->line = line;
    state->col = col;
}

void eat_xmq_comment_to_eol(XMQParseState *state, const char **comment_start, const char **comment_stop)
{
    const char *i = state->i;
    const char *end = state->buffer_stop;

    size_t line = state->line;
    size_t col = state->col;
    increment('/', 1, &i, &line, &col);
    increment('/', 1, &i, &line, &col);

    *comment_start = i;

    char c = 0;
    while (i < end && c != '\n')
    {
        c = *i;
        increment(c, 1, &i, &line, &col);
    }
    if (c == '\n') *comment_stop = i-1;
    else *comment_stop = i;
    state->i = i;
    state->line = line;
    state->col = col;
}

void eat_xmq_comment_to_close(XMQParseState *state, const char **comment_start, const char **comment_stop,
                              size_t num_slashes,
                              bool *found_asterisk)
{
    const char *i = state->i;
    const char *end = state->buffer_stop;

    size_t line = state->line;
    size_t col = state->col;
    size_t n = num_slashes;

    if (*i == '/')
    {
        // Comment starts from the beginning ////* ....
        // Otherwise this is a continuation and *i == '*'
        while (n > 0)
        {
            assert(*i == '/');
            increment('/', 1, &i, &line, &col);
            n--;
        }
    }
    assert(*i == '*');
    increment('*', 1, &i, &line, &col);

    *comment_start = i;

    char c = 0;
    char cc = 0;
    n = 0;
    while (i < end)
    {
        cc = c;
        c = *i;
        if (cc != '*' || c != '/')
        {
            // Not a possible end marker */ or *///// continue eating.
            increment(c, 1, &i, &line, &col);
            continue;
        }
        // We have found */ or *//// not count the number of slashes.
        n = count_xmq_slashes(i, end, found_asterisk);

        if (n < num_slashes) continue; // Not a balanced end marker continue eating,

        if (n > num_slashes)
        {
            // Oups, too many slashes.
            state->error_nr = XMQ_ERROR_COMMENT_CLOSED_WITH_TOO_MANY_SLASHES;
            longjmp(state->error_handler, 1);
        }

        assert(n == num_slashes);
        // Found the ending slashes!
        *comment_stop = i-1;
        while (n > 0)
        {
            cc = c;
            c = *i;
            assert(*i == '/');
            increment(c, 1, &i, &line, &col);
            n--;
        }
        state->i = i;
        state->line = line;
        state->col = col;
        return;
    }
    // We reached the end of the xmq and no */ was found!
    state->error_nr = XMQ_ERROR_COMMENT_NOT_CLOSED;
    longjmp(state->error_handler, 1);
}

void eat_xmq_text_name(XMQParseState *state,
                       const char **text_start,
                       const char **text_stop,
                       const char **namespace_start,
                       const char **namespace_stop)
{
    const char *i = state->i;
    const char *end = state->buffer_stop;
    const char *colon = NULL;
    size_t line = state->line;
    size_t col = state->col;

    *text_start = i;

    while (i < end)
    {
        char c = *i;
        if (!is_xmq_text_name(c)) break;
        if (c == ':') colon = i;
        increment(c, 1, &i, &line, &col);
    }

    if (colon)
    {
        *namespace_start = *text_start;
        *namespace_stop = colon;
        *text_start = colon+1;
    }
    else
    {
        *namespace_start = NULL;
        *namespace_stop = NULL;
    }
    *text_stop = i;
    state->i = i;
    state->line = line;
    state->col = col;
}

void eat_xmq_text_value(XMQParseState *state)
{
    const char *i = state->i;
    const char *stop = state->buffer_stop;
    size_t line = state->line;
    size_t col = state->col;

    while (i < stop)
    {
        char c = *i;
        if (!is_safe_value_char(i, stop)) break;
        increment(c, 1, &i, &line, &col);
    }

    state->i = i;
    state->line = line;
    state->col = col;
}

void eat_xmq_doctype(XMQParseState *state, const char **text_start, const char **text_stop)
{
    const char *i = state->i;
    const char *end = state->buffer_stop;
    size_t line = state->line;
    size_t col = state->col;
    *text_start = i;

    assert(*i == '!');
    increment('!', 1, &i, &line, &col);
    while (i < end)
    {
        char c = *i;
        if (!is_xmq_text_name(c)) break;
        increment(c, 1, &i, &line, &col);
    }


    *text_stop = i;
    state->i = i;
    state->line = line;
    state->col = col;
}

void eat_xmq_pi(XMQParseState *state, const char **text_start, const char **text_stop)
{
    const char *i = state->i;
    const char *end = state->buffer_stop;
    size_t line = state->line;
    size_t col = state->col;
    *text_start = i;

    assert(*i == '?');
    increment('?', 1, &i, &line, &col);
    while (i < end)
    {
        char c = *i;
        if (!is_xmq_text_name(c)) break;
        increment(c, 1, &i, &line, &col);
    }

    *text_stop = i;
    state->i = i;
    state->line = line;
    state->col = col;
}

bool is_xmq_quote_start(char c)
{
    return c == '\'' || c == '"';
}

bool is_xmq_entity_start(char c)
{
    return c == '&';
}

bool is_xmq_attribute_key_start(char c)
{
    bool t =
        c == '\'' ||
        c == '"' ||
        c == '(' ||
        c == ')' ||
        c == '{' ||
        c == '}' ||
        c == '/' ||
        c == '=' ||
        c == '&';

    return !t;
}

bool is_xmq_compound_start(char c)
{
    return (c == '(');
}

bool is_xmq_comment_start(char c, char cc)
{
    return c == '/' && (cc == '/' || cc == '*');
}

bool is_xmq_pi_start(const char *start, const char *stop)
{
    if (*start != '?') return false;
    // We need at least one character, eg. ?x
    if (start+2 > stop) return false;
    return true;
}

bool is_xmq_doctype_start(const char *start, const char *stop)
{
    if (*start != '!') return false;
    // !DOCTYPE
    if (start+8 > stop) return false;
    if (strncmp(start, "!DOCTYPE", 8)) return false;
    // Ooups, !DOCTYPE must have some value.
    if (start+8 == stop) return false;
    char c = *(start+8);
    // !DOCTYPE= or !DOCTYPE = etc
    if (c != '=' && c != ' ' && c != '\t' && c != '\n' && c != '\r') return false;
    return true;
}

size_t count_xmq_slashes(const char *i, const char *stop, bool *found_asterisk)
{
    const char *start = i;

    while (i < stop && *i == '/') i++;

    if (*i == '*') *found_asterisk = true;
    else *found_asterisk = false;

    return i-start;
}

/** Check if a value can start with these two characters. */
bool unsafe_value_start(char c, char cc)
{
    return c == '&' || c == '=' || (c == '/' && (cc == '/' || cc == '*'));
}

bool is_safe_value_char(const char *i, const char *stop)
{
    char c = *i;
    bool is_ws = count_whitespace(i, stop) > 0 ||
        c == '\n' ||
        c == '\t' ||
        c == '\r' ||
        c == '(' ||
        c == ')' ||
        c == '{' ||
        c == '}' ||
        c == '\'' ||
        c == '"'
        ;
    return !is_ws;
}

bool is_xmq_text_value(const char *start, const char *stop)
{
    char c = *start;
    char cc = *(start+1);

    if (unsafe_value_start(c, cc)) return false;

    for (const char *i = start; i < stop; ++i)
    {
        if (!is_safe_value_char(i, stop))
        {
            return false;
        }
    }
    return true;
}

bool peek_xmq_next_is_equal(XMQParseState *state)
{
    const char *i = state->i;
    const char *stop = state->buffer_stop;
    char c = 0;

    while (i < stop)
    {
        c = *i;
        if (!is_xml_whitespace(c)) break;
        i++;
    }
    return c == '=';
}

void parse_xmq(XMQParseState *state)
{
    const char *end = state->buffer_stop;

    while (state->i < end)
    {
        char c = *(state->i);
        char cc = 0;
        if ((c == '/' || c == '(') && state->i+1 < end) cc = *(state->i+1);

        if (is_xmq_token_whitespace(c)) parse_xmq_whitespace(state);
        else if (is_xmq_quote_start(c)) parse_xmq_quote(state, LEVEL_XMQ);
        else if (is_xmq_entity_start(c)) parse_xmq_entity(state, LEVEL_XMQ);
        else if (is_xmq_comment_start(c, cc)) parse_xmq_comment(state, cc);
        else if (is_xmq_element_start(c)) parse_xmq_element(state);
        else if (is_xmq_doctype_start(state->i, end)) parse_xmq_doctype(state);
        else if (is_xmq_pi_start(state->i, end)) parse_xmq_pi(state);
        else if (c == '}') return;
        else
        {
            if (possibly_lost_content_after_equals(state))
            {
                state->error_nr = XMQ_ERROR_EXPECTED_CONTENT_AFTER_EQUALS;
            }
            else if (c == '\t')
            {
                state->error_nr = XMQ_ERROR_UNEXPECTED_TAB;
            }
            else
            {
                state->error_nr = XMQ_ERROR_INVALID_CHAR;
            }
            longjmp(state->error_handler, 1);
        }
    }
}

void parse_xmq_quote(XMQParseState *state, Level level)
{
    size_t start_line = state->line;
    size_t start_col = state->col;
    const char *start;
    const char *stop;

    eat_xmq_quote(state, &start, &stop);

    switch(level)
    {
    case LEVEL_XMQ:
       DO_CALLBACK(quote, state, start_line, start_col, start, stop, stop);
       break;
    case LEVEL_ELEMENT_VALUE:
        DO_CALLBACK(element_value_quote, state, start_line, start_col, start, stop, stop);
        break;
    case LEVEL_ELEMENT_VALUE_COMPOUND:
        DO_CALLBACK(element_value_compound_quote, state, start_line, start_col, start, stop, stop);
        break;
    case LEVEL_ATTR_VALUE:
        DO_CALLBACK(attr_value_quote, state, start_line, start_col, start, stop, stop);
        break;
    case LEVEL_ATTR_VALUE_COMPOUND:
        DO_CALLBACK(attr_value_compound_quote, state, start_line, start_col, start, stop, stop);
        break;
    default:
        assert(false);
    }
}

void parse_xmq_json_quote(XMQParseState *state, Level level)
{
    size_t start_line = state->line;
    size_t start_col = state->col;
    char *start;
    char *stop;

    eat_json_quote(state, &start, &stop);

    switch(level)
    {
    case LEVEL_XMQ:
       DO_CALLBACK(quote, state, start_line, start_col, start, stop, stop);
       break;
    case LEVEL_ELEMENT_VALUE:
        DO_CALLBACK(element_value_quote, state, start_line, start_col, start, stop, stop);
        break;
    case LEVEL_ELEMENT_VALUE_COMPOUND:
        DO_CALLBACK(element_value_compound_quote, state, start_line, start_col, start, stop, stop);
        break;
    case LEVEL_ATTR_VALUE:
        DO_CALLBACK(attr_value_quote, state, start_line, start_col, start, stop, stop);
        break;
    case LEVEL_ATTR_VALUE_COMPOUND:
        DO_CALLBACK(attr_value_compound_quote, state, start_line, start_col, start, stop, stop);
        break;
    default:
        assert(false);
    }

    free(start);
}

void parse_xmq_entity(XMQParseState *state, Level level)
{
    const char *start = state->i;
    int start_line = state->line;
    int start_col = state->col;

    eat_xmq_entity(state);
    const char *stop = state->i;

    switch (level) {
    case LEVEL_XMQ:
        DO_CALLBACK(entity, state, start_line, start_col, start,  stop, stop);
        break;
    case LEVEL_ELEMENT_VALUE:
        DO_CALLBACK(element_value_entity, state, start_line, start_col, start, stop, stop);
        break;
    case LEVEL_ELEMENT_VALUE_COMPOUND:
        DO_CALLBACK(element_value_compound_entity, state, start_line, start_col, start,  stop, stop);
        break;
    case LEVEL_ATTR_VALUE:
        DO_CALLBACK(attr_value_entity, state, start_line, start_col, start, stop, stop);
        break;
    case LEVEL_ATTR_VALUE_COMPOUND:
        DO_CALLBACK(attr_value_compound_entity, state, start_line, start_col, start, stop, stop);
        break;
    default:
        assert(false);
    }
}

void parse_xmq_comment(XMQParseState *state, char cc)
{
    const char *start = state->i;
    int start_line = state->line;
    int start_col = state->col;
    const char *comment_start;
    const char *comment_stop;
    bool found_asterisk = false;

    size_t n = count_xmq_slashes(start, state->buffer_stop, &found_asterisk);

    if (!found_asterisk)
    {
        // This is a single line asterisk.
        eat_xmq_comment_to_eol(state, &comment_start, &comment_stop);
        const char *stop = state->i;
        DO_CALLBACK(comment, state, start_line, start_col, start, stop, stop);
    }
    else
    {
        // This is a /* ... */ or ////*  ... *//// comment.
        eat_xmq_comment_to_close(state, &comment_start, &comment_stop, n, &found_asterisk);
        const char *stop = state->i;
        DO_CALLBACK(comment, state, start_line, start_col, start, stop, stop);

        while (found_asterisk)
        {
            // Aha, this is a comment continuation /* ... */* ...
            start = state->i;
            start_line = state->line;
            start_col = state->col;
            eat_xmq_comment_to_close(state, &comment_start, &comment_stop, n, &found_asterisk);
            stop = state->i;
            DO_CALLBACK(comment_continuation, state, start_line, start_col, start, stop, stop);
        }
    }
}

void parse_xmq_text_value(XMQParseState *state, Level level)
{
    const char *start = state->i;
    int start_line = state->line;
    int start_col = state->col;

    eat_xmq_text_value(state);
    const char *stop = state->i;

    assert(level != LEVEL_XMQ);
    if (level == LEVEL_ATTR_VALUE)
    {
        DO_CALLBACK(attr_value_text, state, start_line, start_col, start, stop, stop);
    }
    else
    {
        DO_CALLBACK(element_value_text, state, start_line, start_col, start, stop, stop);
    }
}

void parse_xmq_value(XMQParseState *state, Level level)
{
    char c = *state->i;

    if (is_xml_whitespace(c))
    {
        parse_xmq_whitespace(state);
        c = *state->i;
    }

    if (is_xmq_quote_start(c))
    {
        parse_xmq_quote(state, level);
    }
    else if (is_xmq_entity_start(c))
    {
        parse_xmq_entity(state, level);
    }
    else if (is_xmq_compound_start(c))
    {
        parse_xmq_compound(state, level);
    }
    else
    {
        char cc = *(state->i+1);
        if (unsafe_value_start(c, cc))
        {
            state->error_nr = XMQ_ERROR_VALUE_CANNOT_START_WITH;
            longjmp(state->error_handler, 1);
        }
        parse_xmq_text_value(state, level);
    }
}

void parse_xmq_element_internal(XMQParseState *state, bool doctype, bool pi)
{
    char c = 0;
    // Name
    const char *name_start = NULL;
    const char *name_stop = NULL;
    // Namespace
    const char *ns_start = NULL;
    const char *ns_stop = NULL;

    size_t start_line = state->line;
    size_t start_col = state->col;

    if (doctype)
    {
        eat_xmq_doctype(state, &name_start, &name_stop);
    }
    else if (pi)
    {
        eat_xmq_pi(state, &name_start, &name_stop);
    }
    else
    {
        eat_xmq_text_name(state, &name_start, &name_stop, &ns_start, &ns_stop);
    }
    const char *stop = state->i;

    // The only peek ahead in the whole grammar! And its only for syntax coloring. :-)
    // key = 123   vs    name { '123' }
    bool is_key = peek_xmq_next_is_equal(state);

    if (!ns_start)
    {
        // Normal key/name element.
        if (is_key)
        {
            DO_CALLBACK(element_key, state, start_line, start_col, name_start, name_stop, stop);
        }
        else
        {
            DO_CALLBACK(element_name, state, start_line, start_col, name_start, name_stop, stop);
        }
    }
    else
    {
        // We have a namespace prefixed to the element, eg: abc:working
        size_t ns_len = ns_stop - ns_start;
        DO_CALLBACK(element_ns, state, start_line, start_col, ns_start, ns_stop, ns_stop);
        DO_CALLBACK(ns_colon, state, start_line, start_col+ns_len, ns_stop, ns_stop+1, ns_stop+1);

        if (is_key)
        {
            DO_CALLBACK(element_key, state, start_line, start_col+ns_len+1, name_start, name_stop, stop);
        }
        else
        {
            DO_CALLBACK(element_name, state, start_line, start_col+ns_len+1, name_start, name_stop, stop);
        }
    }


    c = *state->i;
    if (is_xml_whitespace(c)) { parse_xmq_whitespace(state); c = *state->i; }

    if (c == '(')
    {
        const char *start = state->i;
        state->last_attr_start = state->i;
        state->last_attr_start_line = state->line;
        state->last_attr_start_col = state->col;
        start_line = state->line;
        start_col = state->col;
        increment('(', 1, &state->i, &state->line, &state->col);
        const char *stop = state->i;
        DO_CALLBACK(apar_left, state, start_line, start_col, start, stop, stop);

        parse_xmq_attributes(state);

        c = *state->i;
        if (is_xml_whitespace(c)) { parse_xmq_whitespace(state); c = *state->i; }
        if (c != ')')
        {
            state->error_nr = XMQ_ERROR_ATTRIBUTES_NOT_CLOSED;
            longjmp(state->error_handler, 1);
        }

        start = state->i;
        const char *parentheses_right_start = state->i;
        const char *parentheses_right_stop = state->i+1;

        start_line = state->line;
        start_col = state->col;
        increment(')', 1, &state->i, &state->line, &state->col);
        stop = state->i;
        DO_CALLBACK(apar_right, state, start_line, start_col, parentheses_right_start, parentheses_right_stop, stop);
    }

    c = *state->i;
    if (is_xml_whitespace(c)) { parse_xmq_whitespace(state); c = *state->i; }

    if (c == '=')
    {
        state->last_equals_start = state->i;
        state->last_equals_start_line = state->line;
        state->last_equals_start_col = state->col;
        const char *start = state->i;
        start_line = state->line;
        start_col = state->col;
        increment('=', 1, &state->i, &state->line, &state->col);
        const char *stop = state->i;

        DO_CALLBACK(equals, state, start_line, start_col, start, stop, stop);

        parse_xmq_value(state, LEVEL_ELEMENT_VALUE);
        return;
    }

    if (c == '{')
    {
        const char *start = state->i;
        state->last_body_start = state->i;
        state->last_body_start_line = state->line;
        state->last_body_start_col = state->col;
        start_line = state->line;
        start_col = state->col;
        increment('{', 1, &state->i, &state->line, &state->col);
        const char *stop = state->i;
        DO_CALLBACK(brace_left, state, start_line, start_col, start, stop, stop);

        parse_xmq(state);
        c = *state->i;
        if (is_xml_whitespace(c)) { parse_xmq_whitespace(state); c = *state->i; }
        if (c != '}')
        {
            state->error_nr = XMQ_ERROR_BODY_NOT_CLOSED;
            longjmp(state->error_handler, 1);
        }

        start = state->i;
        start_line = state->line;
        start_col = state->col;
        increment('}', 1, &state->i, &state->line, &state->col);
        stop = state->i;
        DO_CALLBACK(brace_right, state, start_line, start_col, start, stop, stop);
    }
}

void parse_xmq_element(XMQParseState *state)
{
    parse_xmq_element_internal(state, false, false);
}

void parse_xmq_doctype(XMQParseState *state)
{
    parse_xmq_element_internal(state, true, false);
}

void parse_xmq_pi(XMQParseState *state)
{
    parse_xmq_element_internal(state, false, true);
}

/** Parse a list of attribute key = value, or just key children until a ')' is found. */
void parse_xmq_attributes(XMQParseState *state)
{
    const char *end = state->buffer_stop;

    while (state->i < end)
    {
        char c = *(state->i);

        if (is_xml_whitespace(c)) parse_xmq_whitespace(state);
        else if (c == ')') return;
        else if (is_xmq_attribute_key_start(c)) parse_xmq_attribute(state);
        else break;
    }
}

void parse_xmq_attribute(XMQParseState *state)
{
    char c = 0;
    const char *name_start;
    const char *name_stop;
    const char *ns_start = NULL;
    const char *ns_stop = NULL;

    int start_line = state->line;
    int start_col = state->col;

    eat_xmq_text_name(state, &name_start, &name_stop, &ns_start, &ns_stop);
    const char *stop = state->i;

    if (!ns_start)
    {
        // No colon found, we have either a normal: key=123
        // or a default namespace declaration xmlns=...
        size_t len = name_stop - name_start;
        if (len == 5 && !strncmp(name_start, "xmlns", 5))
        {
            // A default namespace declaration, eg: xmlns=uri
            DO_CALLBACK(ns_declaration, state, start_line, start_col, name_start, name_stop, name_stop);
        }
        else
        {
            // A normal attribute key, eg: width=123
            DO_CALLBACK(attr_key, state, start_line, start_col, name_start, name_stop, stop);
        }
    }
    else
    {
        // We have a colon in the attribute key.
        // E.g. alfa:beta where alfa is attr_ns and beta is attr_key
        // However we can also have xmlns:xsl then it gets tokenized as ns_declaration and attr_ns.
        size_t ns_len = ns_stop - ns_start;
        if (ns_len == 5 && !strncmp(ns_start, "xmlns", 5))
        {
            // The xmlns signals a declaration of a namespace.
            DO_CALLBACK(ns_declaration, state, start_line, start_col, ns_start, ns_stop, name_stop);
            DO_CALLBACK(ns_colon, state, start_line, start_col+ns_len, ns_stop, ns_stop+1, ns_stop+1);
            DO_CALLBACK(attr_ns, state, start_line, start_col+ns_len+1, name_start, name_stop, stop);
        }
        else
        {
            // Normal namespaced attribute. Please try to avoid namespaced attributes
            // because you only need to attach the namespace to the element itself,
            // from that follows automatically the unique namespaced attributes.
            // But if you are adding attributes to an existing xml with schema, then you will need
            // use namespaced attributes to avoid tripping the xml validation.
            // An example of this is: xlink:href.
            DO_CALLBACK(attr_ns, state, start_line, start_col, ns_start, ns_stop, ns_stop);
            DO_CALLBACK(ns_colon, state, start_line, start_col+ns_len, ns_stop, ns_stop+1, ns_stop+1);
            DO_CALLBACK(attr_key, state, start_line, start_col+ns_len+1, name_start, name_stop, stop);
        }
    }

    c = *state->i;
    if (is_xml_whitespace(c)) { parse_xmq_whitespace(state); c = *state->i; }

    if (c == '=')
    {
        const char *start = state->i;
        start_line = state->line;
        start_col = state->col;
        increment('=', 1, &state->i, &state->line, &state->col);
        const char *stop = state->i;
        DO_CALLBACK(equals, state, start_line, start_col, start, stop, stop);
        parse_xmq_value(state, LEVEL_ATTR_VALUE);
        return;
    }
}

/** Parse a compound value, ie:  = ( '   ' &#10; '  info ' )
    a compound can only occur after an = (equals) character.
    The normal quoting with single quotes, is enough for all quotes except:
    1) An attribute value with leading/ending whitespace including leading/ending newlines.
    2) An attribute with a mix of quotes and referenced entities.
    3) Compact xmq where actual newlines have to be replaced with &#10;

    Note that an element key = ( ... ) can always  be replaced with key { ... }
    so compound values are not strictly necessary for element key values.
    However they are permitted for symmetry reasons.
*/
void parse_xmq_compound(XMQParseState *state, Level level)
{
    const char *start = state->i;
    int start_line = state->line;
    int start_col = state->col;
    increment('(', 1, &state->i, &state->line, &state->col);
    const char *stop = state->i;
    DO_CALLBACK(cpar_left, state, start_line, start_col, start, stop, stop);

    parse_xmq_compound_children(state, enter_compound_level(level));

    char c = *state->i;
    if (is_xml_whitespace(c)) { parse_xmq_whitespace(state); c = *state->i; }

    if (c != ')')
    {
        state->error_nr = XMQ_ERROR_COMPOUND_NOT_CLOSED;
        longjmp(state->error_handler, 1);
    }

    start = state->i;
    start_line = state->line;
    start_col = state->col;
    increment(')', 1, &state->i, &state->line, &state->col);
    stop = state->i;
    DO_CALLBACK(cpar_right, state, start_line, start_col, start, stop, stop);
}

/** Parse each compound child (quote or entity) until end of file or a ')' is found. */
void parse_xmq_compound_children(XMQParseState *state, Level level)
{
    const char *end = state->buffer_stop;

    while (state->i < end)
    {
        char c = *(state->i);

        if (is_xml_whitespace(c)) parse_xmq_whitespace(state);
        else if (c == ')') break;
        else if (is_xmq_quote_start(c)) parse_xmq_quote(state, level);
        else if (is_xmq_entity_start(c)) parse_xmq_entity(state, level);
        else
        {
            state->error_nr = XMQ_ERROR_COMPOUND_MAY_NOT_CONTAIN;
            longjmp(state->error_handler, 1);
        }
    }
}

bool possibly_lost_content_after_equals(XMQParseState *state)
{
    char c = *(state->i);

    // Look for unexpected = before 123 since beta was gobbled into being alfa:s value.
    // alfa = <newline>
    // beta = 123
    // Look for unexpected { since beta was gobbled into being alfa:s value.
    // alfa = <newline>
    // beta { 'more' }
    // Look for unexpected ( since beta was gobbled into being alfa:s value.
    // alfa = <newline>
    // beta(attr)

    // Not {(= then not lost content, probably.
    if (!(c == '{' || c == '(' || c == '=')) return false;

    const char *i = state->i-1;
    const char *start = state->buffer_start;

    // Scan backwards for newline accepting only texts and xml whitespace.
    while (i > start && *i != '\n' && (is_xmq_text_name(*i) || is_xml_whitespace(*i))) i--;
    if (i == start || *i != '\n') return false;

    // We found the newline lets see if the next character backwards is equals...
    while (i > start
           &&
           is_xml_whitespace(*i))
    {
        i--;
    }

    return *i == '=';
}

bool possibly_need_more_quotes(XMQParseState *state)
{
    if (state->i - 2 < state->buffer_start ||
        state->i >= state->buffer_stop)
    {
        return false;
    }
    // Should have triple quotes: 'There's a man.'
    // c0 = e
    // c1 = '
    // c2 = s
    char c0 = *(state->i-2);
    char c1 = *(state->i-1); // This is the apostrophe
    char c2 = *(state->i);

    // We have just parsed a quote. Check if this is a false ending and
    // there is a syntax error since we need more quotes. For example:
    // greeting = 'There's a man, a wolf and a boat.'
    // We get this error:
    // ../forgot.xmq:1:26: error: unexpected character "," U+2C
    // greeting = 'There's a man, a wolf and a boat.'
    //                          ^
    // The quote terminated too early, we need three quotes.
    //
    // This function detects a suspicious quote ending and remembers it,
    // but does not flag an error until the parser fails.

    // Any non-quote quote non-quote, is suspicios: for example: g's t's
    // or e'l or y'v etc....
    // But do not trigger on [space]'x since that is probably a valid quote start.
    if (c0 != '\'' &&
        c0 != ' ' &&
        c1 == '\'' &&
        c2 != '\'') return true;

    return false;

    // isn't doesn't shouldn't can't aren't won't
    // dog's it's
    // we'll
    // they've
    // he'd
    // she'd've
    // 'clock
    // Hallowe'en
    // fo'c's'le = forecastle
    // cat-o'-nine-tails = cat-of-nine-tails
    // ne'er-do-well = never-do-well
    // will-o'-the-wisp
    // 'tis = it is
    // o'er = over
    // 'twas = it was
    // e'en = even
    // 'Fraid so.'Nother drink?
    // I s'pose so.'S not funny.
    // O'Leary (Irish), d'Abbadie (French), D'Angelo (Italian), M'Tavish (Scots Gaelic)
    // Robert Burns poetry: gi' for give and a' for all
    // the generation of '98
    // James's shop (or James' shop)
    // a month's pay
    // For God's sake! (= exclamation of exasperation)
    // a stone's throw away (= very near)
    // at death's door (= very ill)
    // in my mind's eye (= in my imagination)
}

void parse_xmq_whitespace(XMQParseState *state)
{
    size_t start_line = state->line;
    size_t start_col = state->col;
    const char *start;
    const char *stop;
    eat_xmq_token_whitespace(state, &start, &stop);
    DO_CALLBACK(whitespace, state, start_line, start_col, start, stop, stop);
}

#endif // XMQ_PARSER_MODULE

// PART C XMQ_PRINTER_C ////////////////////////////////////////

#ifdef XMQ_PRINTER_MODULE

size_t find_attr_key_max_u_width(xmlAttr *a);
size_t find_namespace_max_u_width(size_t max, xmlNs *ns);
size_t find_element_key_max_width(xmlNodePtr node, xmlNodePtr *restart_find_at_node);
const char *toHtmlEntity(int uc);
void node_strlen_name_prefix(xmlNode *node, const char **name, size_t *name_len, const char **prefix, size_t *prefix_len, size_t *total_len);


/**
    count_necessary_quotes:
    @start: Points to first byte of memory buffer to scan for quotes.
    @stop:  Points to byte after memory buffer.
    @add_nls: Returns whether we need leading and ending newlines.
    @add_compound: Compounds ( ) is necessary.
    @prefer_double_quotes: Set to true, will change the default to double quotes, instead of single quotes.
    @use_double_quotese: Set to true if the quote should use double quotes.

    Scan the content to determine how it must be quoted, or if the content can
    remain as text without quotes. Return 0, nl_begin=nl_end=false for safe text.
    Return 1,3 or more for unsafe text with at most a single quote '.
    Return -1 if content only contains ' apostrophes and no newlines. Print using json string "..."
    If forbid_nl is true, then we are generating xmq on a single line.
    Set add_nls to true if content starts or end with a quote and forbid_nl==false.
    Set add_compound to true if content starts or ends with spaces/newlines or if forbid_nl==true and
    content starts/ends with quotes.
*/
int count_necessary_quotes(const char *start, const char *stop, bool *add_nls, bool *add_compound, bool prefer_double_quotes, bool *use_double_quotes)
{
    bool all_safe = true;

    assert(stop > start);

    if (unsafe_value_start(*start, start+1 < stop ? *(start+1):0))
    {
        // Content starts with = & // /* or < so it must be quoted.
        all_safe = false;
    }

    size_t only_prepended_newlines = 0;
    size_t only_appended_newlines = 0;
    const char *ls = has_leading_space_nl(start, stop, &only_prepended_newlines);
    const char *es = has_ending_nl_space(start, stop, &only_appended_newlines);

    // We do not need to add a compound, if there is no leading nl+space or if there is pure newlines.
    // Likewise for the ending. Test this.
    if ((ls != NULL && only_prepended_newlines == 0) || // We have leading nl and some non-newlines.
        (es != NULL && only_appended_newlines == 0))    // We have ending nl and some non-newlines.
    {
        // Leading ending ws + nl, nl + ws will be trimmed, so we need a compound and entities.
        *add_compound = true;
    }
    else
    {
        *add_compound = false;
    }

    size_t max_single = 0;
    size_t curr_single = 0;
    size_t max_double = 0;
    size_t curr_double = 0;

    for (const char *i = start; i < stop; ++i)
    {
        char c = *i;
        all_safe &= is_safe_value_char(i, stop);
        if (c == '\'')
        {
            curr_single++;
            if (curr_single > max_single) max_single = curr_single;
        }
        else
        {
            curr_single = 0;
            if (c == '"')
            {
                curr_double++;
                if (curr_double > max_double) max_double = curr_double;
            }
            else
            {
                curr_double = 0;
            }
        }
    }

    bool leading_ending_sqs = false;
    bool leading_ending_dqs = false;
    // We default to using single quotes. But prefer_double_quotes can be set with --prefer-double-quotes
    bool use_dqs = prefer_double_quotes;

    if (*start == '\'' || *(stop-1) == '\'') leading_ending_sqs = true;
    if (*start == '"' || *(stop-1) == '"') leading_ending_dqs = true;

    if (leading_ending_sqs && !leading_ending_dqs)
    {
        // If there is leading and ending single quotes, then always use double quotes.
        use_dqs = true;
    }
    else if (!leading_ending_sqs && leading_ending_dqs)
    {
        // If there are leading ending double quotes, then always use single quotes.
        use_dqs = false;
    }
    else if (max_double > max_single && max_double > 0) use_dqs = false; // We have more doubles than singles, use single quotes.
    else if (max_double < max_single) use_dqs = true;  // We have fewer doubles than singles, use double quotes.
    else // max_double == max_single
    {
        assert(max_double == max_single);
        if (max_double > 0)
        {
            // If more than one quote is needed, then always use single quotes.
            use_dqs = false;
        }
    }

    size_t max;
    if (use_dqs)
    {
        max = max_double;
    }
    else
    {
        max = max_single;
    }

    // We found x quotes, thus we need x+1 quotes to quote them.
    if (max > 0) max++;
    // Content contains no quotes ', but has unsafe chars, a single quote is enough.
    if (max == 0 && !all_safe) max = 1;
    // Content contains two sequential '' quotes, must bump nof required quotes to 3.
    // Since two quotes means the empty string.
    if (max == 2) max = 3;

    if ((use_dqs && leading_ending_dqs) || (!use_dqs && leading_ending_sqs))
    {
        // If leading or ending quote, then add newlines both at the beginning and at the end.
        // Strictly speaking, if only a leading quote, then only newline at beginning is needed.
        // However to reduce visual confusion, we add newlines at beginning and end.

        // We might quote this using:
        // '''
        // 'howdy'
        // '''
        *add_nls = true;
    }
    else
    {
        *add_nls = false;
    }

    *use_double_quotes = use_dqs;

    return max;
}

/**
    count_necessary_slashes:
    @start: Start of buffer in which to count the slashes.
    @stop: Points to byte after buffer.

    Scan the comment to determine how it must be commented.
    If the comment contains asterisk plus slashes, then find the max num
    slashes after an asterisk. The returned value is 1 + this max.
*/
size_t count_necessary_slashes(const char *start, const char *stop)
{
    int max = 0;
    int curr = 0;
    bool counting = false;

    for (const char *i = start; i < stop; ++i)
    {
        char c = *i;
        if (counting)
        {
            if (c == '/')
            {
                curr++;
                if (curr > max) max = curr;
            }
            else
            {
                counting = false;
            }
        }

        if (!counting)
        {
            if (c == '*')
            {
                counting = true;
                curr = 0;
            }
        }
    }
    return max+1;
}

/**
  Scan the attribute names and find the max unicode character width.
*/
size_t find_attr_key_max_u_width(xmlAttr *a)
{
    size_t max = 0;
    while (a)
    {
        const char *name;
        const char *prefix;
        size_t total_u_len;
        attr_strlen_name_prefix(a, &name, &prefix, &total_u_len);

        if (total_u_len > max) max = total_u_len;
        a = xml_next_attribute(a);
    }
    return max;
}

/**
  Scan nodes until there is a node which is not suitable for using the = sign.
  I.e. it has multiple children or no children. This node unsuitable node is stored in
  restart_find_at_node or NULL if all nodes were suitable.
*/
size_t find_element_key_max_width(xmlNodePtr element, xmlNodePtr *restart_find_at_node)
{
    size_t max = 0;
    xmlNodePtr i = element;
    while (i)
    {
        if (!is_key_value_node(i) || xml_first_attribute(i))
        {
            if (i == element) *restart_find_at_node = xml_next_sibling(i);
            else *restart_find_at_node = i;
            return max;
        }
        const char *name;
        const char *prefix;
        size_t total_u_len;
        element_strlen_name_prefix(i, &name, &prefix, &total_u_len);

        if (total_u_len > max) max = total_u_len;
        i = xml_next_sibling(i);
    }
    *restart_find_at_node = NULL;
    return max;
}

/**
  Scan the namespace links and find the max unicode character width.
*/
size_t find_namespace_max_u_width(size_t max, xmlNs *ns)
{
    while (ns)
    {
        const char *prefix;
        size_t total_u_len;
        namespace_strlen_prefix(ns, &prefix, &total_u_len);

        // Print only new/overriding namespaces.
        if (total_u_len > max) max = total_u_len;
        ns = ns->next;
    }

    return max;
}

void print_nodes(XMQPrintState *ps, xmlNode *from, xmlNode *to, size_t align)
{
    xmlNode *i = from;
    xmlNode *restart_find_at_node = from;
    size_t max = 0;

    while (i)
    {
        // We need to search ahead to find the max width of the node names so that we can align the equal signs.
        if (!ps->output_settings->compact && i == restart_find_at_node)
        {
            max = find_element_key_max_width(i, &restart_find_at_node);
        }

        print_node(ps, i, max);
        i = xml_next_sibling(i);
    }
}

void print_content_node(XMQPrintState *ps, xmlNode *node)
{
    print_value(ps, node, LEVEL_XMQ);
}

void print_entity_node(XMQPrintState *ps, xmlNode *node)
{
    check_space_before_entity_node(ps);

    XMQColor c = COLOR_entity;

    if (*(const char*)node->name == '_') c = COLOR_quote;

    print_utf8(ps, c, 1, "&", NULL);
    print_utf8(ps, c, 1, (const char*)node->name, NULL);
    print_utf8(ps, c, 1, ";", NULL);
}

void print_comment_line(XMQPrintState *ps, const char *start, const char *stop, bool compact)
{
    print_utf8(ps, COLOR_comment, 1, start, stop);
}

void print_comment_lines(XMQPrintState *ps, const char *start, const char *stop, bool compact)
{
    const char *i = start;
    const char *line = i;

    size_t num_slashes = count_necessary_slashes(start, stop);

    print_slashes(ps, NULL, "*", num_slashes);
    size_t add_spaces = ps->current_indent + 1 + num_slashes;
    if (!compact) {
        if (*i != '\n') print_white_spaces(ps, 1);
        add_spaces++;
    }

    size_t prev_line_indent = ps->line_indent;
    ps->line_indent = add_spaces;

    for (; i < stop; ++i)
    {
        if (*i == '\n')
        {
            if (line > start) {
                if (compact) {
                    print_slashes(ps, "*", "*", num_slashes);
                }
                else
                {
                    if (*(i-1) == 10 && *(i+1) != 0)
                    {
                        // This is an empty line. Do not indent.
                        // Except the last line which must be indented.
                        print_nl(ps, NULL, NULL);
                    }
                    else
                    {
                        print_nl_and_indent(ps, NULL, NULL);
                    }
                }
            }
            print_comment_line(ps, line, i, compact);
            line = i+1;
        }
    }
    if (line == start)
    {
        // No newlines found.
        print_comment_line(ps, line, i, compact);
    }
    else if (line < stop)
    {
        // There is a remaining line that ends with stop and not newline.
        if (line > start) {
            if (compact) {
                print_slashes(ps, "*", "*", num_slashes);
            }
            else
            {
                print_nl_and_indent(ps, NULL, NULL);
            }
        }
        print_comment_line(ps, line, i, compact);
    }
    if (!compact) print_white_spaces(ps, 1);
    print_slashes(ps, "*", NULL, num_slashes);
    ps->last_char = '/';
    ps->line_indent = prev_line_indent;
}

void print_comment_node(XMQPrintState *ps, xmlNode *node)
{
    const char *comment = xml_element_content(node);
    const char *start = comment;
    const char *stop = comment+strlen(comment);

    check_space_before_comment(ps);

    bool has_newline = has_newlines(start, stop);
    if (!has_newline)
    {
        if (ps->output_settings->compact)
        {
            print_utf8(ps, COLOR_comment, 3, "/*", NULL, start, stop, "*/", NULL);
            ps->last_char = '/';
        }
        else
        {
            print_utf8(ps, COLOR_comment, 2, "// ", NULL, start, stop);
            ps->last_char = 1;
        }
    }
    else
    {
        print_comment_lines(ps, start, stop, ps->output_settings->compact);
        ps->last_char = '/';
    }
}

size_t print_element_name_and_attributes(XMQPrintState *ps, xmlNode *node)
{
    size_t name_len, prefix_len, total_u_len;
    const char *name;
    const char *prefix;

    XMQColor ns_color = COLOR_element_ns;
    XMQColor key_color = COLOR_element_key;
    XMQColor name_color = COLOR_element_name;

    check_space_before_key(ps);

    node_strlen_name_prefix(node, &name, &name_len, &prefix, &prefix_len, &total_u_len);

    if (prefix)
    {
        if (!strcmp(prefix, "xsl"))
        {
            //ns_color = COLOR_ns_override_xsl;
            key_color = COLOR_ns_override_xsl;
            name_color = COLOR_ns_override_xsl;
        }
        print_utf8(ps, ns_color, 1, prefix, NULL);
        print_utf8(ps, COLOR_ns_colon, 1, ":", NULL);
    }

    if (is_key_value_node(node) && !xml_first_attribute(node))
    {
        // Only print using key color if = and no attributes.
        // I.e. alfa=1
        print_utf8(ps, key_color, 1, name, NULL);
    }
    else
    {
        // All other cases print with node color.
        // I.e. alfa{a b} alfa(x=1)=1
        print_utf8(ps, name_color, 1, name, NULL);
    }

    bool has_non_empty_ns = xml_has_non_empty_namespace_defs(node);

    if (xml_first_attribute(node) || has_non_empty_ns)
    {
        print_utf8(ps, COLOR_apar_left, 1, "(", NULL);
        print_attributes(ps, node);
        print_utf8(ps, COLOR_apar_right, 1, ")", NULL);
    }

    return total_u_len;
}

void print_leaf_node(XMQPrintState *ps,
                     xmlNode *node)
{
    print_element_name_and_attributes(ps, node);
}

void print_key_node(XMQPrintState *ps,
                    xmlNode *node,
                    size_t align)
{
    print_element_name_and_attributes(ps, node);

    if (!ps->output_settings->compact)
    {
        size_t len = ps->current_indent - ps->line_indent;
        size_t pad = 1;
        if (len < align) pad = 1+align-len;
        print_white_spaces(ps, pad);
    }
    print_utf8(ps, COLOR_equals, 1, "=", NULL);
    if (!ps->output_settings->compact) print_white_spaces(ps, 1);

    print_value(ps, xml_first_child(node), LEVEL_ELEMENT_VALUE);
}

void print_element_with_children(XMQPrintState *ps,
                                 xmlNode *node,
                                 size_t align)
{
    print_element_name_and_attributes(ps, node);

    void *from = xml_first_child(node);
    void *to = xml_last_child(node);

    check_space_before_opening_brace(ps);
    print_utf8(ps, COLOR_brace_left, 1, "{", NULL);

    ps->line_indent += ps->output_settings->add_indent;

    while (xml_prev_sibling((xmlNode*)from)) from = xml_prev_sibling((xmlNode*)from);
    assert(from != NULL);

    print_nodes(ps, (xmlNode*)from, (xmlNode*)to, align);

    ps->line_indent -= ps->output_settings->add_indent;

    check_space_before_closing_brace(ps);
    print_utf8(ps, COLOR_brace_right, 1, "}", NULL);
}

void print_doctype(XMQPrintState *ps, xmlNode *node)
{
    if (!node) return;

    check_space_before_key(ps);
    print_utf8(ps, COLOR_element_key, 1, "!DOCTYPE", NULL);
    if (!ps->output_settings->compact) print_white_spaces(ps, 1);
    print_utf8(ps, COLOR_equals, 1, "=", NULL);
    if (!ps->output_settings->compact) print_white_spaces(ps, 1);

    xmlBuffer *buffer = xmlBufferCreate();
    xmlNodeDump(buffer, (xmlDocPtr)ps->doq->docptr_.xml, (xmlNodePtr)node, 0, 0);
    char *c = (char*)xmlBufferContent(buffer);
    if (ps->output_settings->compact)
    {
        size_t n = strlen(c);
        char *end = c+n;
        for (char *i = c; i < end; ++i)
        {
            if (*i == '\n') *i = ' ';
        }
    }
    print_value_internal_text(ps, c+10, c+strlen(c)-1, LEVEL_ELEMENT_VALUE);
    xmlBufferFree(buffer);
}

void print_pi_node(XMQPrintState *ps, xmlNode *node)
{
    if (!node) return;

    check_space_before_key(ps);
    size_t name_len = strlen((const char*)node->name);
    print_utf8(ps, COLOR_element_key, 2, "?", NULL, node->name, NULL);
    if (!ps->output_settings->compact) print_white_spaces(ps, 1);
    print_utf8(ps, COLOR_equals, 1, "=", NULL);
    if (!ps->output_settings->compact) print_white_spaces(ps, 1);

    xmlBuffer *buffer = xmlBufferCreate();
    xmlNodeDump(buffer, (xmlDocPtr)ps->doq->docptr_.xml, (xmlNodePtr)node, 0, 0);
    char *c = (char*)xmlBufferContent(buffer);
    size_t n = strlen(c);
    // now detect if we need to add a leading/ending space.
    if (c[n-1] == '>' && c[n-2] == '?')
    {
        n-=2;
    }
    char *content = potentially_add_leading_ending_space(c+name_len+3, c+n);
    n = strlen(content);
    char *end = content+n;

    if (ps->output_settings->compact)
    {
        for (char *i = content; i < end; ++i)
        {
            if (*i == '\n') *i = ' ';
        }
    }

    print_value_internal_text(ps, content, end, LEVEL_ELEMENT_VALUE);

    free(content);
    xmlBufferFree(buffer);
}

void print_node(XMQPrintState *ps, xmlNode *node, size_t align)
{
    // Standalone quote must be quoted: 'word' 'some words'
    if (is_content_node(node))
    {
        return print_content_node(ps, node);
    }

    // This is an entity reference node. &something;
    if (is_entity_node(node))
    {
        return print_entity_node(ps, node);
    }

    // This is a comment // or /* ... */
    if (is_comment_node(node))
    {
        return print_comment_node(ps, node);
    }

    // This is a pi node ?something
    if (is_pi_node(node))
    {
        return print_pi_node(ps, node);
    }

    // This is doctype node.
    if (is_doctype_node(node))
    {
        return print_doctype(ps, node);
    }

    // This is a node with no children, ie br
    if (is_leaf_node(node))
    {
        return print_leaf_node(ps, node);
    }

    // This is a key = value or key = 'value value' node and there are no attributes.
    if (is_key_value_node(node))
    {
        return print_key_node(ps, node, align);
    }

    // All other nodes are printed
    return print_element_with_children(ps, node, align);
}

void print_white_spaces(XMQPrintState *ps, int num)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQTheme *c = os->theme;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;
    if (c && c->whitespace.pre) write(writer_state, c->whitespace.pre, NULL);
    for (int i=0; i<num; ++i)
    {
        write(writer_state, os->indentation_space, NULL);
    }
    ps->current_indent += num;
    if (c && c->whitespace.post) write(writer_state, c->whitespace.post, NULL);
}

void print_all_whitespace(XMQPrintState *ps, const char *start, const char *stop, Level level)
{
    const char *i = start;
    while (true)
    {
        if (i >= stop) break;
        if (*i == ' ')
        {
            const char *j = i;
            while (*j == ' ' && j < stop) j++;
            check_space_before_quote(ps, level);
            print_quoted_spaces(ps, level_to_quote_color(level), (size_t)(j-i));
            i += j-i;
        }
        else
        {
            check_space_before_entity_node(ps);
            print_char_entity(ps, level_to_entity_color(level), i, stop);
            i++;
        }
    }
}

void print_explicit_spaces(XMQPrintState *ps, XMQColor c, int num)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    const char *pre = NULL;
    const char *post = NULL;
    getThemeStrings(os, c, &pre, &post);

    write(writer_state, pre, NULL);
    for (int i=0; i<num; ++i)
    {
        write(writer_state, os->explicit_space, NULL);
    }
    ps->current_indent += num;
    write(writer_state, post, NULL);
}

void print_quoted_spaces(XMQPrintState *ps, XMQColor color, int num)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQTheme *c = os->theme;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    if (c && c->quote.pre) write(writer_state, c->quote.pre, NULL);
    write(writer_state, "'", NULL);
    for (int i=0; i<num; ++i)
    {
        write(writer_state, os->explicit_space, NULL);
    }
    ps->current_indent += num;
    ps->last_char = '\'';
    write(writer_state, "'", NULL);
    if (c && c->quote.post) write(writer_state, c->quote.post, NULL);
}

void print_quotes(XMQPrintState *ps, int num, XMQColor color, bool use_double_quotes)
{
    assert(num > 0);
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    const char *pre = NULL;
    const char *post = NULL;
    getThemeStrings(os, color, &pre, &post);

    if (pre) write(writer_state, pre, NULL);
    const char *q = "'";
    if (use_double_quotes) q = "\"";
    for (int i=0; i<num; ++i)
    {
        write(writer_state, q, NULL);
    }
    ps->current_indent += num;
    ps->last_char = q[0];
    if (post) write(writer_state, post, NULL);
}

void print_double_quote(XMQPrintState *ps, XMQColor color)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    const char *pre = NULL;
    const char *post = NULL;
    getThemeStrings(os, color, &pre, &post);

    if (pre) write(writer_state, pre, NULL);
    write(writer_state, "\"", NULL);
    ps->current_indent += 1;
    ps->last_char = '"';
    if (post) write(writer_state, post, NULL);
}

void print_nl_and_indent(XMQPrintState *ps, const char *prefix, const char *postfix)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    if (postfix) write(writer_state, postfix, NULL);
    write(writer_state, os->explicit_nl, NULL);
    ps->current_indent = 0;
    ps->last_char = 0;
    print_white_spaces(ps, ps->line_indent);
    if (ps->restart_line) write(writer_state, ps->restart_line, NULL);
    if (prefix) write(writer_state, prefix, NULL);
}

void print_nl(XMQPrintState *ps, const char *prefix, const char *postfix)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    if (postfix) write(writer_state, postfix, NULL);
    write(writer_state, os->explicit_nl, NULL);
    ps->current_indent = 0;
    ps->last_char = 0;
    if (ps->restart_line) write(writer_state, ps->restart_line, NULL);
    if (prefix) write(writer_state, prefix, NULL);
}

size_t print_char_entity(XMQPrintState *ps, XMQColor color, const char *start, const char *stop)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;
    const char *pre, *post;
    getThemeStrings(os, color, &pre, &post);

    int uc = 0;
    size_t bytes = 0;
    if (decode_utf8(start, stop, &uc, &bytes))
    {
        // Max entity &#1114112; max buf is 11 bytes including terminating zero byte.
        char buf[32] = {};
        memset(buf, 0, sizeof(buf));

        const char *replacement = NULL;
        if (ps->output_settings->escape_non_7bit &&
            ps->output_settings->output_format == XMQ_CONTENT_HTMQ)
        {
            replacement = toHtmlEntity(uc);
        }

        if (replacement)
        {
            snprintf(buf, 32, "&%s;", replacement);
        }
        else
        {
            snprintf(buf, 32, "&#%d;", uc);
        }

        if (pre) write(writer_state, pre, NULL);
        print_utf8(ps, COLOR_none, 1, buf, NULL);
        if (post) write(writer_state, post, NULL);

        ps->last_char = ';';
        ps->current_indent += strlen(buf);
    }
    else
    {
        if (pre) write(writer_state, pre, NULL);
        write(writer_state, "&badutf8;", NULL);
        if (post) write(writer_state, post, NULL);
    }

    return bytes;
}

void print_slashes(XMQPrintState *ps, const char *pre, const char *post, size_t n)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;
    const char *cpre = NULL;
    const char *cpost = NULL;
    getThemeStrings(os, COLOR_comment, &cpre, &cpost);

    if (cpre) write(writer_state, cpre, NULL);
    if (pre) write(writer_state, pre, NULL);
    for (size_t i = 0; i < n; ++i) write(writer_state, "/", NULL);
    if (post) write(writer_state, post, NULL);
    if (cpost) write(writer_state, cpost, NULL);
}

bool need_separation_before_attribute_key(XMQPrintState *ps)
{
    // If the previous value was quoted, then no space is needed, ie.
    // 'x y z'key=
    // If the previous text was attribute start, then no space is needed, ie.
    // (key=
    // If the previous text was compound endt, then no space is needed, ie.
    // ))key=
    // If the previous text was entity, then no space is needed, ie.
    // &x10;key=
    // if previous value was text, then a space is necessary, ie.
    // xyz key=
    char c = ps->last_char;
    return c != 0 && c != '\'' && c != '"' && c != '(' && c != ')' && c != ';';
}

bool need_separation_before_entity(XMQPrintState *ps)
{
    // No space needed for:
    // 'x y z'&nbsp;
    // =&nbsp;
    // {&nbsp;
    // }&nbsp;
    // ;&nbsp;
    // Otherwise a space is needed:
    // xyz &nbsp;
    char c = ps->last_char;
    return c != 0 && c != '=' && c != '\'' && c != '"' && c != '{' && c != '}' && c != ';' && c != '(' && c != ')';
}

bool need_separation_before_element_name(XMQPrintState *ps)
{
    // No space needed for:
    // 'x y z'key=
    // {key=
    // }key=
    // ;key=
    // */key=
    // )key=
    // Otherwise a space is needed:
    // xyz key=
    char c = ps->last_char;
    return c != 0 && c != '\'' && c != '"' && c != '{' && c != '}' && c != ';' && c != ')' && c != '/';
}

bool need_separation_before_quote(XMQPrintState *ps)
{
    // If the previous node was quoted, then a space is necessary, ie
    // 'a b c' 'next quote'
    // for simplicity we also separate "a b c" this could be improved.
    // otherwise last char is the end of a text value, and no space is necessary, ie
    // key=value'next quote'
    char c = ps->last_char;
    return c == '\'' || c == '"';
}

bool need_separation_before_comment(XMQPrintState *ps)
{
    // If the previous value was quoted, then then no space is needed, ie.
    // 'x y z'/*comment*/
    // If the previous value was an entity &...; then then no space is needed, ie.
    // &nbsp;/*comment*/
    // if previous value was text, then a space is necessary, ie.
    // xyz /*comment*/
    // if previous value was } or )) then no space is is needed.
    // }/*comment*/   ((...))/*comment*/
    char c = ps->last_char;
    return c != 0 && c != '\'' && c != '"' && c != '{' && c != ')' && c != '}' && c != ';';
}

void check_space_before_attribute(XMQPrintState *ps)
{
    char c = ps->last_char;
    if (c == '(') return;
    if (!ps->output_settings->compact)
    {
        print_nl_and_indent(ps, NULL, NULL);
    }
    else if (need_separation_before_attribute_key(ps))
    {
        print_white_spaces(ps, 1);
    }
}

void check_space_before_entity_node(XMQPrintState *ps)
{
    char c = ps->last_char;
    if (c == '(') return;
    if (!ps->output_settings->compact && c != '=')
    {
        print_nl_and_indent(ps, NULL, NULL);
    }
    else if (need_separation_before_entity(ps))
    {
        print_white_spaces(ps, 1);
    }
}


void check_space_before_quote(XMQPrintState *ps, Level level)
{
    char c = ps->last_char;
    if (c == 0) return;
    if (!ps->output_settings->compact && (c != '=' || level == LEVEL_XMQ) && c != '(')
    {
        print_nl_and_indent(ps, NULL, NULL);
    }
    else if (need_separation_before_quote(ps))
    {
        print_white_spaces(ps, 1);
    }
}

void check_space_before_key(XMQPrintState *ps)
{
    char c = ps->last_char;
    if (c == 0) return;

    if (!ps->output_settings->compact)
    {
        print_nl_and_indent(ps, NULL, NULL);
    }
    else if (need_separation_before_element_name(ps))
    {
        print_white_spaces(ps, 1);
    }
}

void check_space_before_opening_brace(XMQPrintState *ps)
{
    char c = ps->last_char;

    if (!ps->output_settings->compact)
    {
        if (c == ')')
        {
            print_nl_and_indent(ps, NULL, NULL);
        }
        else
        {
            print_white_spaces(ps, 1);
        }
    }
}

void check_space_before_closing_brace(XMQPrintState *ps)
{
    if (!ps->output_settings->compact)
    {
        print_nl_and_indent(ps, NULL, NULL);
    }
}

void check_space_before_comment(XMQPrintState *ps)
{
    char c = ps->last_char;

    if (c == 0) return;
    if (!ps->output_settings->compact)
    {
        print_nl_and_indent(ps, NULL, NULL);
    }
    else if (need_separation_before_comment(ps))
    {
        print_white_spaces(ps, 1);
    }
}


void print_attribute(XMQPrintState *ps, xmlAttr *a, size_t align)
{
    check_space_before_attribute(ps);

    const char *key;
    const char *prefix;
    size_t total_u_len;

    attr_strlen_name_prefix(a, &key, &prefix, &total_u_len);

    if (prefix)
    {
        print_utf8(ps, COLOR_attr_ns, 1, prefix, NULL);
        print_utf8(ps, COLOR_ns_colon, 1, ":", NULL);
    }
    print_utf8(ps, COLOR_attr_key, 1, key, NULL);

    if (a->children != NULL && !is_single_empty_text_node(a->children))
    {
        if (!ps->output_settings->compact) print_white_spaces(ps, 1+align-total_u_len);

        print_utf8(ps, COLOR_equals, 1, "=", NULL);

        if (!ps->output_settings->compact) print_white_spaces(ps, 1);

        print_value(ps, a->children, LEVEL_ATTR_VALUE);
    }
}

void print_namespace_declaration(XMQPrintState *ps, xmlNs *ns, size_t align)
{
    //if (!xml_non_empty_namespace(ns)) return;

    check_space_before_attribute(ps);

    const char *prefix;
    size_t total_u_len;

    namespace_strlen_prefix(ns, &prefix, &total_u_len);

    print_utf8(ps, COLOR_ns_declaration, 1, "xmlns", NULL);

    if (prefix)
    {
        print_utf8(ps, COLOR_ns_colon, 1, ":", NULL);
        XMQColor ns_color = COLOR_attr_ns;
        if (!strcmp(prefix, "xsl")) ns_color = COLOR_ns_override_xsl;
        print_utf8(ps, ns_color, 1, prefix, NULL);
    }

    const char *v = xml_namespace_href(ns);

    if (v != NULL)
    {
        if (!ps->output_settings->compact) print_white_spaces(ps, 1+align-total_u_len);

        print_utf8(ps, COLOR_equals, 1, "=", NULL);

        if (!ps->output_settings->compact) print_white_spaces(ps, 1);

        print_value_internal_text(ps, v, NULL, LEVEL_ATTR_VALUE);
    }
}


void print_attributes(XMQPrintState *ps,
                      xmlNodePtr node)
{
    xmlAttr *a = xml_first_attribute(node);

    size_t max = 0;
    if (!ps->output_settings->compact) max = find_attr_key_max_u_width(a);

    xmlNs *ns = xml_first_namespace_def(node);
    if (!ps->output_settings->compact) max = find_namespace_max_u_width(max, ns);

    size_t line_indent = ps->line_indent;
    ps->line_indent = ps->current_indent;
    while (a)
    {
        print_attribute(ps, a, max);
        a = xml_next_attribute(a);
    }

    while (ns)
    {
        print_namespace_declaration(ps, ns, max);
        ns = xml_next_namespace_def(ns);
    }

    ps->line_indent = line_indent;
}

void print_quote_lines_and_color_uwhitespace(XMQPrintState *ps,
                                             XMQColor color,
                                             const char *start,
                                             const char *stop)
{
    XMQOutputSettings *os = ps->output_settings;
    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    const char *pre, *post;
    getThemeStrings(os, color, &pre, &post);

    if (pre) write(writer_state, pre, NULL);

    const char *old_restart_line = ps->restart_line;
    if (!post) ps->restart_line = pre;
    else ps->restart_line = NULL;

    // We are leading with a newline, print an extra into the quote, which will be trimmed away during parse.
    if (*start == '\n')
    {
        print_nl(ps, pre, post);
    }

    bool all_newlines = true;
    for (const char *i = start; i < stop;)
    {
        if (*i == '\n')
        {
            if (i+1 < stop && *(i+1) != '\n')
            {
                print_nl_and_indent(ps, pre, post);
            }
            else
            {
                print_nl(ps, pre, post);
            }
            i++;
        }
        else
        {
            i += print_utf8_char(ps, i, stop);
            all_newlines = false;
        }
    }
    // We are ending with a newline, print an extra into the quote, which will be trimmed away during parse.
    if (*(stop-1) == '\n')
    {
        ps->line_indent--;
        if (!all_newlines)
        {
            print_nl_and_indent(ps, NULL, post);
        }
        else
        {
            ps->current_indent = 0;
            ps->last_char = 0;
            print_white_spaces(ps, ps->line_indent);
        }
        ps->line_indent++;
    }
    if (*(stop-1) != '\n' && post) write(writer_state, post, NULL);
    ps->restart_line = old_restart_line;
}

void print_safe_leaf_quote(XMQPrintState *ps,
                           XMQColor c,
                           const char *start,
                           const char *stop)
{
    bool compact = ps->output_settings->compact;
    bool force = true;
    bool add_nls = false;
    bool add_compound = false;
    bool use_double_quotes = false;
    int numq = count_necessary_quotes(start, stop, &add_nls, &add_compound, ps->output_settings->prefer_double_quotes, &use_double_quotes);
    size_t indent = ps->current_indent;

    if (numq > 0)
    {
        // If nl_begin is true and we have quotes, then we have to forced newline already due to quotes at
        // the beginning or end, therefore we use indent as is, however if
        if (add_nls == false) // then we might have to adjust the indent, or even introduce a nl_begin/nl_end.
        {
            if (indent == (size_t)-1)
            {
                // Special case, -1 indentation requested this means the quote should be on its own line.
                // then we must insert newlines.
                // Otherwise the indentation will be 1.
                // e.g.
                // |'
                // |alfa beta
                // |gamma delta
                // |'
                add_nls = true;
                indent = 0;
            }
            else
            {
                // We have a nonzero indentation and number of quotes is 1 or 3.
                // Then the actual source indentation will be +1 or +3.
                if (numq < 4 || compact)
                {
                    // e.g. quote at 4 will have source at 5.
                    // |    'alfa beta
                    // |     gamma delta'
                    // e.g. quote at 4 will have source at 7.
                    // |    '''alfa beta
                    // |       gamma delta'
                    indent += numq;
                }
                else
                {
                    // More than 3 quotes and not compact, then we add newlines.
                    // e.g. quote at 4 will have source at 4.
                    // |    ''''
                    // |    alfa beta '''
                    // |    gamma delta
                    // |    ''''
                    add_nls = true;
                }
            }
        }
    }

    if (numq == 0 && force) numq = 1;

    size_t old_line_indent = 0;

    if (add_nls)
    {
        old_line_indent = ps->line_indent;
        ps->line_indent = ps->current_indent;
    }

    print_quotes(ps, numq, c, use_double_quotes);

    if (!add_nls)
    {
        old_line_indent = ps->line_indent;
        ps->line_indent = ps->current_indent;
    }

    if (add_nls)
    {
        print_nl_and_indent(ps, NULL, NULL);
    }

    print_quote_lines_and_color_uwhitespace(ps,
                                            c,
                                            start,
                                            stop);

    if (!add_nls)
    {
        ps->line_indent = old_line_indent;
    }

    if (add_nls)
    {
        print_nl_and_indent(ps, NULL, NULL);
    }

    print_quotes(ps, numq, c, use_double_quotes);

    if (add_nls)
    {
        ps->line_indent = old_line_indent;
    }
}

const char *find_next_line_end(XMQPrintState *ps, const char *start, const char *stop)
{
    const char *i = start;

    while (i < stop)
    {
        int c = (int)((unsigned char)*i);
        if (c == '\n') break;
        i++;
    }

    return i;
}

const char *find_next_char_that_needs_escape(XMQPrintState *ps, const char *start, const char *stop)
{
    bool compact = ps->output_settings->compact;
    bool newlines = ps->output_settings->escape_newlines;
    bool escape_tabs = ps->output_settings->escape_tabs;
    bool non7bit = ps->output_settings->escape_non_7bit;

    const char *i = start;

    if (*i == '\'' && compact)
    {
        return i;
    }
    const char *pre_stop = stop-1;
    if (compact && *pre_stop == '\'')
    {
        while (pre_stop > start && *pre_stop == '\'') pre_stop--;
        pre_stop++;
    }

    while (i < stop)
    {
        int c = (int)((unsigned char)*i);
        if (compact && c == '\'' && i == pre_stop) break;
        if (newlines && c == '\n') break;
        if (non7bit && c > 126) break;
        if (c < 32 && c != '\t' && c != '\n') break;
        if (c == '\t' && escape_tabs) break;
        i++;
    }

    // Now move backwards, perhaps there was newlines before this problematic character...
    // Then we have to escape those as well since they are ending the previous quote.
    /*
    const char *j = i-1;
    while (j > start)
    {
        int c = (int)((unsigned char)*j);
        if (c != '\n') break;
        j--;
        }*/
    return i; // j+1;
}

void print_value_internal_text(XMQPrintState *ps, const char *start, const char *stop, Level level)
{
    if (!stop) stop = start+strlen(start);
    if (!start || start >= stop || start[0] == 0)
    {
        // This is for empty attribute values.
        // Empty elements do not have print_value invoked so there is no equal char printed here (eg = '')
        check_space_before_quote(ps, level);
        print_utf8(ps, level_to_quote_color(level), 1, "''", NULL);
        return;
    }

    if (has_all_quotes(start, stop))
    {
        // A text with all single quotes or all double quotes.
        // "''''''''" or '"""""""'
        check_space_before_quote(ps, level);
        bool is_dq = *start == '"';
        print_quotes(ps, 1, level_to_quote_color(level), !is_dq);
        print_quotes(ps, stop-start, level_to_quote_color(level), is_dq);
        print_quotes(ps, 1, level_to_quote_color(level), !is_dq);
        return;
    }

    bool all_space = false;
    bool only_newlines = false;
    bool all_whitespace = has_all_whitespace(start, stop, &all_space, &only_newlines);

    if (all_space)
    {
        // This are all normal ascii 32 spaces. Print like: '     '
        check_space_before_quote(ps, level);
        print_quoted_spaces(ps, level_to_quote_color(level), (size_t)(stop-start));
        return;
    }

    if (all_whitespace)
    {
        if (only_newlines && !ps->output_settings->compact && ((size_t)(stop-start)) > 1)
        {
            // All newlines and more than 1 newline. This is printed further on.
        }
        else
        {
            // All whitespace, but more than just normal spaces, ie newlines!
            // This is often the case with trimmed whitespace, lets print using
            // entities, which makes this content be easy to spot when --trim=none is used.
            // Also works both for normal and compact mode.
            print_all_whitespace(ps, start, stop, level);
            return;
        }
    }

    if (is_xmq_text_value(start, stop) && (level == LEVEL_ELEMENT_VALUE || level == LEVEL_ATTR_VALUE))
    {
        // This is a key_node text value or an attribute text value, ie key = 123 or color=blue, ie no quoting needed.
        print_utf8(ps, level_to_quote_color(level), 1, start, stop);
        return;
    }

    size_t only_prepended_newlines = 0;
    const char *new_start = has_leading_space_nl(start, stop, &only_prepended_newlines);
    if (new_start && only_prepended_newlines == 0)
    {
        // We have a leading mix of newlines and whitespace.
        print_all_whitespace(ps, start, new_start, level);
        start = new_start;
    }

    size_t only_appended_newlines = 0;
    const char *new_stop = has_ending_nl_space(start, stop, &only_appended_newlines);
    const char *old_stop = stop;
    if (new_stop && only_appended_newlines == 0)
    {
        // We have an ending mix of newlines and whitespace.
        stop = new_stop;
        // Move forward over normal spaces.
        while (stop < old_stop && *stop == ' ') stop++;
    }

    // Ok, normal content to be quoted. However we might need to split the content
    // at chars that need to be replaced with character entities. Normally no
    // chars need to be replaced. But in compact mode, the \n newlines are replaced with &#10;
    // If content contains CR LF its replaced with &#13;&#10;
    // Also one can replace all non-ascii chars with their entities if so desired.
    for (const char *from = start; from < stop; )
    {
        const char *to = find_next_char_that_needs_escape(ps, from, stop);
        if (from == to)
        {
            check_space_before_entity_node(ps);
            to += print_char_entity(ps, level_to_entity_color(level), from, stop);
            while (from+1 < stop && *(from+1) == '\n')
            {
                // Special case, we have escaped something right before newline(s).
                // Escape the newline(s) as well. This is important for CR LF.
                // If not then we have to loop around detecting that the newline
                // is leading a quote and needs to be escaped. Escape it here already.
                from++;
                check_space_before_entity_node(ps);
                to += print_char_entity(ps, level_to_entity_color(level), from, stop);
            }
        }
        else
        {
            bool add_nls = false;
            bool add_compound = false;
            bool compact = ps->output_settings->compact;
            bool use_double_quotes = false;
            count_necessary_quotes(from, to, &add_nls, &add_compound, ps->output_settings->prefer_double_quotes, &use_double_quotes);
            if (!add_compound && (!add_nls || !compact))
            {
                check_space_before_quote(ps, level);
                print_safe_leaf_quote(ps, level_to_quote_color(level), from, to);
            }
            else
            {
                print_value_internal_text(ps, from, to, level);
            }
        }
        from = to;
    }

    if (new_stop && only_appended_newlines == 0)
    {
        // This trailing whitespace could not be printed inside the quote.
        print_all_whitespace(ps, stop, old_stop, level);
    }
}

void print_color_pre(XMQPrintState *ps, XMQColor color)
{
    XMQOutputSettings *os = ps->output_settings;
    const char *pre = NULL;
    const char *post = NULL;
    getThemeStrings(os, color, &pre, &post);

    if (pre)
    {
        XMQWrite write = os->content.write;
        void *writer_state = os->content.writer_state;
        write(writer_state, pre, NULL);
    }
}

void print_color_post(XMQPrintState *ps, XMQColor color)
{
    XMQOutputSettings *os = ps->output_settings;
    const char *pre = NULL;
    const char *post = NULL;
    getThemeStrings(os, color, &pre, &post);

    XMQWrite write = os->content.write;
    void *writer_state = os->content.writer_state;

    if (post)
    {
        write(writer_state, post, NULL);
    }
    else
    {
        write(writer_state, ps->replay_active_color_pre, NULL);
    }
}


/**
   print_value_internal:
   @ps: Print state.
   @node: Text node to be printed.
   @level: Printing node, key_value, kv_compound, attr_value, av_compound

   Print content as:
   EMPTY_: ''
   ENTITY: &#10;
   QUOTES: ( &apos;&apos; )
   WHITSP: ( &#32;&#32;&#10;&#32;&#32; )
   SPACES: '      '
   TEXT  : /root/home/foo&123
   QUOTE : 'x y z'
   QUOTEL: 'xxx
            yyy'
*/
void print_value_internal(XMQPrintState *ps, xmlNode *node, Level level)
{
    if (node->type == XML_ENTITY_REF_NODE ||
        node->type == XML_ENTITY_NODE)
    {
        print_entity_node(ps, node);
        return;
    }

    print_value_internal_text(ps, xml_element_content(node), NULL, level);
}

/**
   quote_needs_compounded:
   @ps: The print state.
   @start: Content buffer start.
   @stop: Points to after last buffer byte.

   Used to determine early if the quote needs to be compounded.
*/
bool quote_needs_compounded(XMQPrintState *ps, const char *start, const char *stop)
{
    bool compact = ps->output_settings->compact;
    bool escape_tabs = ps->output_settings->escape_tabs;
    if (stop == start+1)
    {
        // A single quote becomes &apos;
        // A single newline becomes &#10;
        // A single cr becomes &#13;
        // A single tab becomes &#9;
        if (*start == '\'') return false;
        if (*start == '\n') return false;
        if (*start == '\r') return false;
        if (*start == '\t') return false;
    }

    size_t only_leading_newlines = 0;
    const char *ls = has_leading_space_nl(start, stop, &only_leading_newlines);
    if (ls != NULL && only_leading_newlines == 0) return true;
    size_t only_ending_newlines = 0;
    const char *es = has_ending_nl_space(start, stop, &only_ending_newlines);
    if (es != NULL && only_ending_newlines == 0) return true;

    if (compact)
    {
        // In compact form newlines must be escaped: &#10;
        if (has_newlines(start, stop)) return true;
        // In compact form leading or ending single quotes triggers &#39; escapes
        // since we cannot use the multiline quote trick:
        // '''
        // 'alfa'
        // '''
        if (has_leading_ending_different_quotes(start, stop)) return true;
    }

    bool newlines = ps->output_settings->escape_newlines;
    bool non7bit = ps->output_settings->escape_non_7bit;

    for (const char *i = start; i < stop; ++i)
    {
        int c = (int)(unsigned char)(*i);
        if (newlines && c == '\n') return true;
        if (non7bit && c > 126) return true;
        if (c < 32 && c != '\t' && c != '\n') return true;
        if (c == '\t' && escape_tabs) return true;
    }
    return false;
}

void print_value(XMQPrintState *ps, xmlNode *node, Level level)
{
    // Check if there are more than one part, if so the value has to be compounded.
    bool is_compound = level != LEVEL_XMQ && node != NULL && node->next != NULL;

    // Check if the single part will split into multiple parts and therefore needs to be compounded.
    if (!is_compound && node && !is_entity_node(node) && level != LEVEL_XMQ)
    {
        // Check if there are leading ending quotes/whitespace. But also
        // if compact output and there are newlines inside.
        const char *start = xml_element_content(node);
        const char *stop = start+strlen(start);
        is_compound = quote_needs_compounded(ps, start, stop);
    }

    size_t old_line_indent = ps->line_indent;

    if (is_compound)
    {
        level = enter_compound_level(level);
        print_utf8(ps, COLOR_cpar_left, 1, "(", NULL);
        if (!ps->output_settings->compact) print_white_spaces(ps, 1);
        ps->line_indent = ps->current_indent;
    }

    for (xmlNode *i = node; i; i = xml_next_sibling(i))
    {
        print_value_internal(ps, i, level);
        if (level == LEVEL_XMQ) break;
    }

    if (is_compound)
    {
        if (!ps->output_settings->compact) print_white_spaces(ps, 1);
        print_utf8(ps, COLOR_cpar_right, 1, ")", NULL);
    }

    ps->line_indent = old_line_indent;
}

void node_strlen_name_prefix(xmlNode *node,
                        const char **name, size_t *name_len,
                        const char **prefix, size_t *prefix_len,
                        size_t *total_len)
{
    *name_len = strlen((const char*)node->name);
    *name = (const char*)node->name;

    if (node->ns && node->ns->prefix)
    {
        *prefix = (const char*)node->ns->prefix;
        *prefix_len = strlen((const char*)node->ns->prefix);
        *total_len = *name_len + *prefix_len +1;
    }
    else
    {
        *prefix = NULL;
        *prefix_len = 0;
        *total_len = *name_len;
    }
    assert(*name != NULL);
}

void attr_strlen_name_prefix(xmlAttr *attr, const char **name, const char **prefix, size_t *total_u_len)
{
    *name = (const char*)attr->name;
    size_t name_b_len;
    size_t name_u_len;
    size_t prefix_b_len;
    size_t prefix_u_len;
    str_b_u_len(*name, NULL, &name_b_len, &name_u_len);

    if (attr->ns && attr->ns->prefix)
    {
        *prefix = (const char*)attr->ns->prefix;
        str_b_u_len(*prefix, NULL, &prefix_b_len, &prefix_u_len);
        *total_u_len = name_u_len + prefix_u_len + 1;
    }
    else
    {
        *prefix = NULL;
        prefix_b_len = 0;
        prefix_u_len = 0;
        *total_u_len = name_u_len;
    }
    assert(*name != NULL);
}

void namespace_strlen_prefix(xmlNs *ns, const char **prefix, size_t *total_u_len)
{
    size_t prefix_b_len;
    size_t prefix_u_len;

    if (ns->prefix)
    {
        *prefix = (const char*)ns->prefix;
        str_b_u_len(*prefix, NULL, &prefix_b_len, &prefix_u_len);
        *total_u_len = /* xmlns */ 5  + prefix_u_len + 1;
    }
    else
    {
        *prefix = NULL;
        prefix_b_len = 0;
        prefix_u_len = 0;
        *total_u_len = /* xmlns */ 5;
    }
}

void element_strlen_name_prefix(xmlNode *element, const char **name, const char **prefix, size_t *total_u_len)
{
    *name = (const char*)element->name;
    if (!*name)
    {
        *name = "";
        *prefix = "";
        *total_u_len = 0;
        return;
    }
    size_t name_b_len;
    size_t name_u_len;
    size_t prefix_b_len;
    size_t prefix_u_len;
    str_b_u_len(*name, NULL, &name_b_len, &name_u_len);

    if (element->ns && element->ns->prefix)
    {
        *prefix = (const char*)element->ns->prefix;
        str_b_u_len(*prefix, NULL, &prefix_b_len, &prefix_u_len);
        *total_u_len = name_u_len + prefix_u_len + 1;
    }
    else
    {
        *prefix = NULL;
        prefix_b_len = 0;
        prefix_u_len = 0;
        *total_u_len = name_u_len;
    }
    assert(*name != NULL);
}

#endif // XMQ_PRINTER_MODULE

// PART C YAEP_ALLOCATE_C ////////////////////////////////////////

#ifdef YAEP_ALLOCATE_MODULE

struct YaepAllocator
{
  Yaep_malloc malloc;
  Yaep_calloc calloc;
  Yaep_realloc realloc;
  Yaep_free free;
  Yaep_alloc_error alloc_error;
  void *userptr;
};

void
yaep_alloc_defaulterrfunc (void *ignored)
{
  (void) ignored;

  fputs ("*** out of memory ***\n", stderr);
  exit (EXIT_FAILURE);
}

struct YaepAllocator *
yaep_alloc_new (Yaep_malloc mallocf, Yaep_calloc callocf,
		Yaep_realloc reallocf, Yaep_free freef)
{
  struct YaepAllocator *result;

  /* Sanity checks */
  if (mallocf == NULL)
    mallocf = malloc;
  if ((callocf == NULL) && (mallocf == malloc))
    callocf = calloc;
  if (reallocf == NULL)
    {
      if ((mallocf == malloc) && (callocf == calloc))
	reallocf = realloc;
      else
	return NULL;
    }
  if (freef == NULL)
    {
      if ((mallocf == malloc) && (callocf == calloc) && (reallocf == realloc))
	freef = free;
      else
	return NULL;
    }

  /* Allocate allocator */
  result = (struct YaepAllocator*)mallocf (sizeof (*result));
  if (result == NULL)
    return NULL;
  result->malloc = mallocf;
  result->calloc = callocf;
  result->realloc = reallocf;
  result->free = freef;
  result->alloc_error = yaep_alloc_defaulterrfunc;
  result->userptr = result;

  return result;
}

void
yaep_alloc_del (struct YaepAllocator *allocator)
{
  if (allocator != NULL)
    {
      Yaep_free freef = allocator->free;

      freef (allocator);
    }
}

void *
yaep_malloc (struct YaepAllocator *allocator, size_t size)
{
  void *result;

  if (allocator == NULL)
    return NULL;

  result = allocator->malloc (size);
  if ((result == NULL) && (size != 0))
    allocator->alloc_error (allocator->userptr);

  return result;
}

void *
yaep_calloc (struct YaepAllocator *allocator, size_t nmemb, size_t size)
{
  void *result;

  if (allocator == NULL)
    return NULL;

  if (allocator->calloc != NULL)
    {
      result = allocator->calloc (nmemb, size);
    }
  else if ((nmemb == 0) || (size == 0))
    {
      result = NULL;
    }
  else
    {
      size_t total = nmemb * size;
      if (total / nmemb != size)
	result = NULL;
      else
	{
	  result = allocator->malloc (total);
	  if (result != NULL)
	    memset (result, '\0', total);
	}
    }

  if ((result == NULL) && (nmemb != 0) && (size != 0))
    allocator->alloc_error (allocator->userptr);

  return result;
}

void *
yaep_realloc (struct YaepAllocator *allocator, void *ptr, size_t size)
{
  void *result;

  if (allocator == NULL)
    return NULL;

  result = allocator->realloc (ptr, size);
  if ((result == NULL) && (size != 0))
    allocator->alloc_error (allocator->userptr);

  return result;
}

void
yaep_free (struct YaepAllocator *allocator, void *ptr)
{
  if (allocator != NULL)
    allocator->free (ptr);
}

Yaep_alloc_error
yaep_alloc_geterrfunc (YaepAllocator * allocator)
{
  if (allocator != NULL)
    return allocator->alloc_error;
  else
    return NULL;
}

void *
yaep_alloc_getuserptr (YaepAllocator * allocator)
{
  if (allocator != NULL)
    return allocator->userptr;
  else
    return NULL;
}

void
yaep_alloc_seterr (YaepAllocator * allocator, Yaep_alloc_error errfunc,
		   void *userptr)
{
  if (allocator != NULL)
    {
      if (errfunc != NULL)
	allocator->alloc_error = errfunc;
      else
	allocator->alloc_error = yaep_alloc_defaulterrfunc;
      allocator->userptr = userptr;
    }
}

#endif // YAEP_ALLOCATE_MODULE

// PART C YAEP_CSPC_C ////////////////////////////////////////

#ifdef YAEP_CSPC_MODULE

/* Initialize work with array of vlos.*/
static void vlo_array_init(YaepParseState *ps)
{
    VLO_CREATE(ps->vlo_array, ps->run.grammar->alloc, 4096);
    ps->vlo_array_len = 0;
}

/* The function forms new empty vlo at the end of the array of vlos.*/
static int vlo_array_expand(YaepParseState *ps)
{
    vlo_t*vlo_ptr;

    if ((unsigned) ps->vlo_array_len >= VLO_LENGTH(ps->vlo_array) / sizeof(vlo_t))
    {
        VLO_EXPAND(ps->vlo_array, sizeof(vlo_t));
        vlo_ptr = &((vlo_t*) VLO_BEGIN(ps->vlo_array))[ps->vlo_array_len];
        VLO_CREATE(*vlo_ptr, ps->run.grammar->alloc, 64);
    }
    else
    {
        vlo_ptr = &((vlo_t*) VLO_BEGIN(ps->vlo_array))[ps->vlo_array_len];
        VLO_NULLIFY(*vlo_ptr);
    }
    return ps->vlo_array_len++;
}

/* The function purges the array of vlos.*/
static void vlo_array_nullify(YaepParseState *ps)
{
    ps->vlo_array_len = 0;
}

/* The following function returns pointer to vlo with INDEX.*/
static vlo_t *vlo_array_el(YaepParseState *ps, int index)
{
    assert(index >= 0 && ps->vlo_array_len > index);
    return &((vlo_t*) VLO_BEGIN(ps->vlo_array))[index];
}

static void free_vlo_array(YaepParseState *ps)
{
    vlo_t *vlo_ptr;

    for (vlo_ptr = (vlo_t*)VLO_BEGIN(ps->vlo_array); (char*) vlo_ptr < (char*) VLO_BOUND(ps->vlo_array); vlo_ptr++)
    {
        VLO_DELETE(*vlo_ptr);
    }
    VLO_DELETE(ps->vlo_array);
}


#ifdef USE_CORE_SYMB_HASH_TABLE
/* Hash of core_symb_to_predcomps.*/
static unsigned core_symb_to_predcomps_hash(YaepCoreSymbToPredComps *core_symb_to_predcomps)
{
    return (jauquet_prime_mod32* hash_shift+(size_t)/* was unsigned */core_symb_to_predcomps->core) * hash_shift
        +(size_t)/* was unsigned */core_symb_to_predcomps->symb;
}

/* Equality of core_symb_to_predcomps.*/
static bool core_symb_to_predcomps_eq(YaepCoreSymbToPredComps *core_symb_to_predcomps1, YaepCoreSymbToPredComps *core_symb_to_predcomps2)
{
    return core_symb_to_predcomps1->core == core_symb_to_predcomps2->core && core_symb_to_predcomps1->symb == core_symb_to_predcomps2->symb;
}
#endif

static unsigned vect_ids_hash(YaepVect*v)
{
    unsigned result = jauquet_prime_mod32;

    for (int i = 0; i < v->len; i++)
    {
        result = result* hash_shift + v->ids[i];
    }
    return result;
}

static bool vect_ids_eq(YaepVect *v1, YaepVect *v2)
{
    if (v1->len != v2->len) return false;

    for (int i = 0; i < v1->len; i++)
    {
        if (v1->ids[i] != v2->ids[i]) return false;
    }
    return true;
}

static unsigned prediction_ids_hash(hash_table_entry_t t)
{
    return vect_ids_hash(&((YaepCoreSymbToPredComps*)t)->predictions);
}

static bool prediction_ids_eq(hash_table_entry_t t1, hash_table_entry_t t2)
{
    return vect_ids_eq(&((YaepCoreSymbToPredComps*)t1)->predictions,
                       &((YaepCoreSymbToPredComps*)t2)->predictions);
}

static unsigned completion_ids_hash(hash_table_entry_t t)
{
    return vect_ids_hash(&((YaepCoreSymbToPredComps*)t)->completions);
}

static bool completion_ids_eq(hash_table_entry_t t1, hash_table_entry_t t2)
{
    return vect_ids_eq(&((YaepCoreSymbToPredComps*) t1)->completions,
                       &((YaepCoreSymbToPredComps*) t2)->completions);
}

/* Initialize work with the triples(set core, symbol, vector).*/
void core_symb_to_predcomps_init(YaepParseState *ps)
{
    OS_CREATE(ps->core_symb_to_predcomps_os, ps->run.grammar->alloc, 0);
    VLO_CREATE(ps->new_core_symb_to_predcomps_vlo, ps->run.grammar->alloc, 0);
    OS_CREATE(ps->vect_ids_os, ps->run.grammar->alloc, 0);

    vlo_array_init(ps);
#ifdef USE_CORE_SYMB_HASH_TABLE
    ps->map_core_symb_to_predcomps = create_hash_table(ps->run.grammar->alloc, 3000,
                                                       (hash_table_hash_function)core_symb_to_predcomps_hash,
                                                       (hash_table_eq_function)core_symb_to_predcomps_eq);
#else
    VLO_CREATE(ps->core_symb_table_vlo, ps->run.grammar->alloc, 4096);
    ps->core_symb_table = (YaepCoreSymbToPredComps***)VLO_BEGIN(ps->core_symb_table_vlo);
    OS_CREATE(ps->core_symb_tab_rows, ps->run.grammar->alloc, 8192);
#endif

    ps->map_transition_to_coresymbvect = create_hash_table(ps->run.grammar->alloc, 3000,
                                                           (hash_table_hash_function)prediction_ids_hash,
                                                           (hash_table_eq_function)prediction_ids_eq);

    ps->map_reduce_to_coresymbvect = create_hash_table(ps->run.grammar->alloc, 3000,
                                                       (hash_table_hash_function)completion_ids_hash,
                                                       (hash_table_eq_function)completion_ids_eq);

    ps->n_core_symb_pairs = ps->n_core_symb_to_predcomps_len = 0;
    ps->n_transition_vects = ps->n_transition_vect_len = 0;
    ps->n_reduce_vects = ps->n_reduce_vect_len = 0;
}


#ifdef USE_CORE_SYMB_HASH_TABLE

/* The following function returns entry in the table where pointer to
   corresponding triple with the same keys as TRIPLE ones is
   placed.*/
static YaepCoreSymbToPredComps **core_symb_to_pred_comps_addr_get(YaepParseState *ps, YaepCoreSymbToPredComps *triple, int reserv_p)
{
    YaepCoreSymbToPredComps **result;

    if (triple->symb->cached_core_symb_to_predcomps != NULL
        && triple->symb->cached_core_symb_to_predcomps->core == triple->core)
    {
        return &triple->symb->cached_core_symb_to_predcomps;
    }

    result = ((YaepCoreSymbToPredComps**)find_hash_table_entry(ps->map_core_symb_to_predcomps, triple, reserv_p));

    triple->symb->cached_core_symb_to_predcomps = *result;

    return result;
}

#else

/* The following function returns entry in the table where pointer to
   corresponding triple with SET_CORE and SYMB is placed. */
static YaepCoreSymbToPredComps **core_symb_to_predcomps_addr_get(YaepParseState *ps, YaepStateSetCore *set_core, YaepSymbol *symb)
{
    YaepCoreSymbToPredComps***core_symb_to_predcomps_ptr;

    core_symb_to_predcomps_ptr = ps->core_symb_table + set_core->id;

    if ((char*) core_symb_to_predcomps_ptr >=(char*) VLO_BOUND(ps->core_symb_table_vlo))
    {
        YaepCoreSymbToPredComps***ptr,***bound;
        int diff, i;

        diff =((char*) core_symb_to_predcomps_ptr
                -(char*) VLO_BOUND(ps->core_symb_table_vlo));
        diff += sizeof(YaepCoreSymbToPredComps**);
        if (diff == sizeof(YaepCoreSymbToPredComps**))
            diff*= 10;

        VLO_EXPAND(ps->core_symb_table_vlo, diff);
        ps->core_symb_table = (YaepCoreSymbToPredComps***) VLO_BEGIN(ps->core_symb_table_vlo);
        core_symb_to_predcomps_ptr = ps->core_symb_table + set_core->id;
        bound =(YaepCoreSymbToPredComps***) VLO_BOUND(ps->core_symb_table_vlo);

        ptr = bound - diff / sizeof(YaepCoreSymbToPredComps**);
        while(ptr < bound)
        {
            OS_TOP_EXPAND(ps->core_symb_tab_rows,
                          (ps->run.grammar->symbs_ptr->num_terminals + ps->run.grammar->symbs_ptr->num_nonterminals)
                          * sizeof(YaepCoreSymbToPredComps*));
           *ptr =(YaepCoreSymbToPredComps**) OS_TOP_BEGIN(ps->core_symb_tab_rows);
            OS_TOP_FINISH(ps->core_symb_tab_rows);
            for(i = 0; i < ps->run.grammar->symbs_ptr->num_terminals + ps->run.grammar->symbs_ptr->num_nonterminals; i++)
               (*ptr)[i] = NULL;
            ptr++;
        }
    }
    return &(*core_symb_to_predcomps_ptr)[symb->id];
}
#endif

/* The following function returns the triple(if any) for given SET_CORE and SYMB. */
YaepCoreSymbToPredComps *core_symb_to_predcomps_find(YaepParseState *ps, YaepStateSetCore *core, YaepSymbol *symb)
{
    YaepCoreSymbToPredComps *r;

#ifdef USE_CORE_SYMB_HASH_TABLE
    YaepCoreSymbToPredComps core_symb_to_predcomps;

    core_symb_to_predcomps.core = core;
    core_symb_to_predcomps.symb = symb;
    r = *core_symb_to_pred_comps_addr_get(ps, &core_symb_to_predcomps, false);
#else
    r = *core_symb_to_predcomps_addr_get(ps, core, symb);
#endif

    return r;
}

YaepCoreSymbToPredComps *core_symb_to_predcomps_new(YaepParseState *ps, YaepStateSetCore*core, YaepSymbol*symb)
{
    YaepCoreSymbToPredComps*core_symb_to;
    YaepCoreSymbToPredComps**addr;
    vlo_t*vlo_ptr;

    /* Create table element.*/
    OS_TOP_EXPAND(ps->core_symb_to_predcomps_os, sizeof(YaepCoreSymbToPredComps));
    core_symb_to = ((YaepCoreSymbToPredComps*) OS_TOP_BEGIN(ps->core_symb_to_predcomps_os));
    core_symb_to->id = ps->core_symb_to_pred_comps_counter++;
    core_symb_to->core = core;
    core_symb_to->symb = symb;
    OS_TOP_FINISH(ps->core_symb_to_predcomps_os);

#ifdef USE_CORE_SYMB_HASH_TABLE
    addr = core_symb_to_pred_comps_addr_get(ps, core_symb_to, true);
#else
    addr = core_symb_to_pred_comps_addr_get(ps, core, symb);
#endif
    assert(*addr == NULL);
   *addr = core_symb_to;

    core_symb_to->predictions.intern = vlo_array_expand(ps);
    vlo_ptr = vlo_array_el(ps, core_symb_to->predictions.intern);
    core_symb_to->predictions.len = 0;
    core_symb_to->predictions.ids =(int*) VLO_BEGIN(*vlo_ptr);

    core_symb_to->completions.intern = vlo_array_expand(ps);
    vlo_ptr = vlo_array_el(ps, core_symb_to->completions.intern);
    core_symb_to->completions.len = 0;
    core_symb_to->completions.ids =(int*) VLO_BEGIN(*vlo_ptr);
    VLO_ADD_MEMORY(ps->new_core_symb_to_predcomps_vlo, &core_symb_to,
                    sizeof(YaepCoreSymbToPredComps*));
    ps->n_core_symb_pairs++;

    return core_symb_to;
}

static void vect_add_id(YaepParseState *ps, YaepVect *vec, int id)
{
    vec->len++;
    vlo_t *vlo_ptr = vlo_array_el(ps, vec->intern);
    VLO_ADD_MEMORY(*vlo_ptr, &id, sizeof(int));
    vec->ids =(int*) VLO_BEGIN(*vlo_ptr);
    ps->n_core_symb_to_predcomps_len++;
}

void core_symb_to_predcomps_add_predict(YaepParseState *ps,
                                      YaepCoreSymbToPredComps *core_symb_to_predcomps,
                                      int rule_index_in_core)
{
    vect_add_id(ps, &core_symb_to_predcomps->predictions, rule_index_in_core);

    YaepDottedRule *dotted_rule = core_symb_to_predcomps->core->dotted_rules[rule_index_in_core];
    yaep_trace(ps, "add prediction cspc%d[c%d %s] -> d%d",
               core_symb_to_predcomps->id,
               core_symb_to_predcomps->core->id,
               core_symb_to_predcomps->symb->hr,
               dotted_rule->id);
}

void core_symb_to_predcomps_add_complete(YaepParseState *ps,
                                       YaepCoreSymbToPredComps *core_symb_to_predcomps,
                                       int rule_index_in_core)
{
    vect_add_id(ps, &core_symb_to_predcomps->completions, rule_index_in_core);
    YaepDottedRule *dotted_rule = core_symb_to_predcomps->core->dotted_rules[rule_index_in_core];
    yaep_trace(ps, "completed d%d store in cspc%d[c%d %s]",
               dotted_rule->id,
               core_symb_to_predcomps->id,
               core_symb_to_predcomps->core->id,
               core_symb_to_predcomps->symb->hr
               );

}

/* Insert vector VEC from CORE_SYMB_TO_PREDCOMPS into table TAB.  Update
   *N_VECTS and INT*N_VECT_LEN if it is a new vector in the table. */
static void process_core_symb_to_predcomps_el(YaepParseState *ps,
                                      YaepCoreSymbToPredComps *core_symb_to_predcomps,
                                      YaepVect *vec,
                                      hash_table_t *tab,
                                      int *n_vects,
                                      int *n_vect_len)
{
    hash_table_entry_t*entry;

    if (vec->len == 0)
    {
        vec->ids = NULL;
    }
    else
    {
        entry = find_hash_table_entry(*tab, core_symb_to_predcomps, true);
        if (*entry != NULL)
        {
            vec->ids = (&core_symb_to_predcomps->predictions == vec
                        ?((YaepCoreSymbToPredComps*)*entry)->predictions.ids
                        :((YaepCoreSymbToPredComps*)*entry)->completions.ids);
        }
        else
        {
            *entry = (hash_table_entry_t)core_symb_to_predcomps;
            OS_TOP_ADD_MEMORY(ps->vect_ids_os, vec->ids, vec->len* sizeof(int));
            vec->ids =(int*) OS_TOP_BEGIN(ps->vect_ids_os);
            OS_TOP_FINISH(ps->vect_ids_os);
            (*n_vects)++;
            *n_vect_len += vec->len;
        }
    }
    vec->intern = -1;
}

/* Finish forming all new triples core_symb_to_predcomps.*/
void core_symb_to_predcomps_new_all_stop(YaepParseState *ps)
{
    YaepCoreSymbToPredComps**triple_ptr;

    for(triple_ptr =(YaepCoreSymbToPredComps**) VLO_BEGIN(ps->new_core_symb_to_predcomps_vlo);
        (char*) triple_ptr <(char*) VLO_BOUND(ps->new_core_symb_to_predcomps_vlo);
         triple_ptr++)
    {
        process_core_symb_to_predcomps_el(ps, *triple_ptr, &(*triple_ptr)->predictions,
                                  &ps->map_transition_to_coresymbvect, &ps->n_transition_vects,
                                  &ps->n_transition_vect_len);
        process_core_symb_to_predcomps_el(ps, *triple_ptr, &(*triple_ptr)->completions,
                                  &ps->map_reduce_to_coresymbvect, &ps->n_reduce_vects,
                                  &ps->n_reduce_vect_len);
    }
    vlo_array_nullify(ps);
    VLO_NULLIFY(ps->new_core_symb_to_predcomps_vlo);
}

/* Finalize work with all triples(set core, symbol, vector).*/
void free_core_symb_to_vect_lookup(YaepParseState *ps)
{
    delete_hash_table(ps->map_transition_to_coresymbvect);
    delete_hash_table(ps->map_reduce_to_coresymbvect);

#ifdef USE_CORE_SYMB_HASH_TABLE
    delete_hash_table(ps->map_core_symb_to_predcomps);
#else
    OS_DELETE(ps->core_symb_tab_rows);
    VLO_DELETE(ps->core_symb_table_vlo);
#endif
    free_vlo_array(ps);
    OS_DELETE(ps->vect_ids_os);
    VLO_DELETE(ps->new_core_symb_to_predcomps_vlo);
    OS_DELETE(ps->core_symb_to_predcomps_os);
}


#endif

// PART C YAEP_HASHTAB_C ////////////////////////////////////////

#ifdef YAEP_HASHTAB_MODULE

/* This macro defines reserved value for empty table entry. */

#define EMPTY_ENTRY    NULL

/* This macro defines reserved value for table entry which contained
   a deleted element. */

#define DELETED_ENTRY  ((void *) 1)

/* The following function returns the nearest prime number which is
   greater than given source number. */

static unsigned long
higher_prime_number (unsigned long number)
{
  unsigned long i;

  for (number = (number / 2) * 2 + 3;; number += 2)
    {
      for (i = 3; i * i <= number; i += 2)
	if (number % i == 0)
	  break;
      if (i * i > number)
	return number;
    }
}

/* This function creates table with length slightly longer than given
   source length.  Created hash table is initiated as empty (all the
   hash table entries are EMPTY_ENTRY).  The function returns the
   created hash table. */

hash_table_t
create_hash_table (YaepAllocator * allocator, size_t size,
		   unsigned int (*hash_function) (hash_table_entry_t el_ptr),
		   bool (*eq_function) (hash_table_entry_t el1_ptr,
	 			        hash_table_entry_t el2_ptr))
{
  hash_table_t result;
  hash_table_entry_t *entry_ptr;

  size = higher_prime_number (size);
  result = (hash_table_t)yaep_malloc (allocator, sizeof (*result));
  result->entries = (const void**)
    yaep_malloc (allocator, size * sizeof (hash_table_entry_t));
  result->size = size;
  result->hash_function = hash_function;
  result->eq_function = eq_function;
  result->number_of_elements = 0;
  result->number_of_deleted_elements = 0;
  result->searches = 0;
  result->collisions = 0;
  result->alloc = allocator;
  for (entry_ptr = result->entries;
       entry_ptr < result->entries + size; entry_ptr++)
    *entry_ptr = EMPTY_ENTRY;
  return result;
}

/* This function makes the table empty.  Naturally the hash table must
   already exist. */

void
empty_hash_table (hash_table_t htab)
{
  hash_table_entry_t *entry_ptr;

  assert (htab != NULL);
  htab->number_of_elements = 0;
  htab->number_of_deleted_elements = 0;
  for (entry_ptr = htab->entries;
       entry_ptr < htab->entries + htab->size; entry_ptr++)
    *entry_ptr = EMPTY_ENTRY;
}

/* This function frees all memory allocated for given hash table.
   Naturally the hash table must already exist. */

void
delete_hash_table (hash_table_t htab)
{
  assert (htab != NULL);
  yaep_free (htab->alloc, htab->entries);
  yaep_free (htab->alloc, htab);
}

/* The following function changes size of memory allocated for the
   entries and repeatedly inserts the table elements.  The occupancy
   of the table after the call will be about 50%.  Naturally the hash
   table must already exist.  Remember also that the place of the
   table entries is changed. */

static void
expand_hash_table (hash_table_t htab)
{
  hash_table_t new_htab;
  hash_table_entry_t *entry_ptr;
  hash_table_entry_t *new_entry_ptr;

  assert (htab != NULL);
  new_htab =
    create_hash_table (htab->alloc, htab->number_of_elements * 2,
		       htab->hash_function, htab->eq_function);
  for (entry_ptr = htab->entries; entry_ptr < htab->entries + htab->size;
       entry_ptr++)
    if (*entry_ptr != EMPTY_ENTRY && *entry_ptr != DELETED_ENTRY)
      {
	new_entry_ptr = find_hash_table_entry (new_htab, *entry_ptr, true);
	assert (*new_entry_ptr == EMPTY_ENTRY);
	*new_entry_ptr = (*entry_ptr);
      }
  yaep_free (htab->alloc, htab->entries);
  *htab = (*new_htab);
  yaep_free (new_htab->alloc, new_htab);
}

/* The following variable is used for debugging. Its value is number
   of all calls of `find_hash_table_entry' for all hash tables. */
int all_searches = 0;

/* The following variable is used for debugging. Its value is number
   of collisions fixed for time of work with all hash tables. */
int all_collisions = 0;

int get_all_searches ()
{
    return all_searches;
}

int get_all_collisions()
{
    return all_collisions;
}

/* This function searches for hash table entry which contains element
   equal to given value or empty entry in which given value can be
   placed (if the element with given value does not exist in the
   table).  The function works in two regimes.  The first regime is
   used only for search.  The second is used for search and
   reservation empty entry for given value.  The table is expanded if
   occupancy (taking into accout also deleted elements) is more than
   75%.  Naturally the hash table must already exist.  If reservation
   flag is TRUE then the element with given value should be inserted
   into the table entry before another call of
   `find_hash_table_entry'. */

hash_table_entry_t *
find_hash_table_entry (hash_table_t htab,
		       hash_table_entry_t element,
                       bool reserve)
{
  hash_table_entry_t *entry_ptr;
  hash_table_entry_t *first_deleted_entry_ptr;
  unsigned hash_value, secondary_hash_value;

  assert (htab != NULL);
  if (htab->size / 4 <= htab->number_of_elements / 3)
    expand_hash_table (htab);
  hash_value = (*htab->hash_function) (element);
  secondary_hash_value = 1 + hash_value % (htab->size - 2);
  hash_value %= htab->size;
  htab->searches++;
  all_searches++;
  first_deleted_entry_ptr = NULL;
  for (;; htab->collisions++, all_collisions++)
    {
      entry_ptr = htab->entries + hash_value;
      if (*entry_ptr == EMPTY_ENTRY)
	{
	  if (reserve)
	    {
	      htab->number_of_elements++;
	      if (first_deleted_entry_ptr != NULL)
		{
		  entry_ptr = first_deleted_entry_ptr;
		  *entry_ptr = EMPTY_ENTRY;
		}
	    }
	  break;
	}
      else if (*entry_ptr != DELETED_ENTRY)
	{
	  if ((*htab->eq_function) (*entry_ptr, element))
	    break;
	}
      else if (first_deleted_entry_ptr == NULL)
	first_deleted_entry_ptr = entry_ptr;
      hash_value += secondary_hash_value;
      if (hash_value >= htab->size)
	hash_value -= htab->size;
    }
  return entry_ptr;
}

/* This function deletes element with given value from hash table.
   The hash table entry value will be `DELETED_ENTRY' after the
   function call.  Naturally the hash table must already exist.  Hash
   table entry for given value should be not empty (or deleted). */

void
remove_element_from_hash_table_entry (hash_table_t htab,
				      hash_table_entry_t element)
{
  hash_table_entry_t *entry_ptr;

  assert (htab != NULL);
  entry_ptr = find_hash_table_entry (htab, element, false);
  assert (*entry_ptr != EMPTY_ENTRY && *entry_ptr != DELETED_ENTRY);
  *entry_ptr = DELETED_ENTRY;
  htab->number_of_deleted_elements++;
}

/* The following function returns current size of given hash table. */

size_t
hash_table_size (hash_table_t htab)
{
  assert (htab != NULL);
  return htab->size;
}

/* The following function returns current number of elements in given
   hash table. */

size_t
hash_table_elements_number (hash_table_t htab)
{
  assert (htab != NULL);
  return htab->number_of_elements - htab->number_of_deleted_elements;
}

size_t hash_table_memusage(hash_table_t htab)
{
    if (!htab) return 0;
    return htab->size*sizeof(hash_table_entry_t);
}

#endif

// PART C YAEP_OBJSTACK_C ////////////////////////////////////////

#ifdef YAEP_OBJSTACK_MODULE

/* The function implements macro `OS_CREATE' (creation of stack of
   object).  OS must be created before any using other macros of the
   package for work with given OS. */

void
_OS_create_function (os_t * os, size_t initial_segment_length)
{
  if (initial_segment_length == 0) initial_segment_length = OS_DEFAULT_SEGMENT_LENGTH;

  size_t segment_size = initial_segment_length + sizeof (struct _os_segment);
  os->os_current_segment = (struct _os_segment*)yaep_malloc (os->os_alloc, segment_size);
  os->os_current_segment->os_segment_size = segment_size;
  os->os_current_segment->os_previous_segment = NULL;
  os->os_top_object_start = (char *)_OS_ALIGNED_ADDRESS (os->os_current_segment->os_segment_content);
  os->os_top_object_stop = os->os_top_object_start;
  os->os_segment_stop = os->os_top_object_start + initial_segment_length;
  os->initial_segment_length = initial_segment_length;
}

/* The function implements macro `OS_DELETE' (freeing memory allocated
   for OS).  Any work (except for creation) with given OS is not
   possible after evaluation of this macros.  The macro has not side
   effects. */

void
_OS_delete_function (os_t * os)
{
  struct _os_segment *current_segment, *previous_segment;

  assert (os->os_top_object_start != NULL);
  os->os_top_object_start = NULL;
  for (current_segment = os->os_current_segment; current_segment != NULL;
       current_segment = previous_segment)
    {
      previous_segment = current_segment->os_previous_segment;
      yaep_free (os->os_alloc, current_segment);
    }
  os->os_current_segment = NULL;
}

/* The following function implements macro `OS_EMPTY' (freeing memory
   allocated for OS except for the first segment). */

void
_OS_empty_function (os_t * os)
{
  struct _os_segment *current_segment, *previous_segment;

  assert (os->os_top_object_start != NULL && os->os_current_segment != NULL);
  current_segment = os->os_current_segment;
  for (;;)
    {
      previous_segment = current_segment->os_previous_segment;
      if (previous_segment == NULL)
	break;
      yaep_free (os->os_alloc, current_segment);
      current_segment = previous_segment;
    }
  os->os_current_segment = current_segment;
  os->os_top_object_start
    = (char *) _OS_ALIGNED_ADDRESS (current_segment->os_segment_content);
  os->os_top_object_stop = os->os_top_object_start;
  os->os_segment_stop = os->os_top_object_start + os->initial_segment_length;
}

/* The function implements macro `OS_ADD_STRING' (addition of string
   STR (with end marker is '\0') to the end of OS).  Remember that the
   OS place may be changed after the call. */

void
_OS_add_string_function (os_t * os, const char *str)
{
    size_t string_length;

    assert (os->os_top_object_start != NULL);
    if (str == NULL) return;
    if (os->os_top_object_stop != os->os_top_object_start)
    {
        OS_TOP_SHORTEN (*os, 1);
    }
    string_length = strlen (str) + 1;
    if (os->os_top_object_stop + string_length > os->os_segment_stop)
    {
        _OS_expand_memory (os, string_length);
    }
    memcpy( os->os_top_object_stop, str, string_length);
    os->os_top_object_stop = os->os_top_object_stop + string_length;
}

/* The function creates new segment for OS.  The segment becames
   current and its size becames equal to about one and a half of the
   top object length accounting for length of memory which will be
   added after the call (but not less than the default segment
   length).  The function deletes the segment which was current if the
   segment contained only the top object.  Remember that the top
   object place may be changed after the call. */

void
_OS_expand_memory (os_t * os, size_t additional_length)
{
    size_t os_top_object_length, segment_length;
    struct _os_segment *new_segment, *previous_segment;
    char *new_os_top_object_start;

    assert (os->os_top_object_start != NULL);
    os_top_object_length = OS_TOP_LENGTH(*os);

    segment_length = os_top_object_length + additional_length;
    segment_length += segment_length / 2 + 1;

    if (segment_length < OS_DEFAULT_SEGMENT_LENGTH)
    {
        segment_length = OS_DEFAULT_SEGMENT_LENGTH;
    }

    size_t new_segment_size = segment_length + sizeof (struct _os_segment);
    new_segment = (struct _os_segment*)yaep_malloc (os->os_alloc, new_segment_size);
    new_segment->os_segment_size = new_segment_size;
    new_os_top_object_start = (char *)_OS_ALIGNED_ADDRESS (new_segment->os_segment_content);
    memcpy (new_os_top_object_start, os->os_top_object_start, os_top_object_length);

    if (os->os_top_object_start == (char *)_OS_ALIGNED_ADDRESS (os->os_current_segment->os_segment_content))
    {
        previous_segment = os->os_current_segment->os_previous_segment;
        yaep_free (os->os_alloc, os->os_current_segment);
    }
    else
    {
        previous_segment = os->os_current_segment;
    }
    os->os_current_segment = new_segment;
    new_segment->os_previous_segment = previous_segment;

    os->os_top_object_start = new_os_top_object_start;
    os->os_top_object_stop = os->os_top_object_start + os_top_object_length;
    os->os_segment_stop = os->os_top_object_start + segment_length;
}

size_t objstack_memusage(os_t *os)
{
    if (!os) return 0;

    size_t sum = 0;
    struct _os_segment *curr = os->os_current_segment;

    while (curr)
    {
        sum += curr->os_segment_size;
        curr = curr->os_previous_segment;
    }

    return sum;
}

#endif

// PART C YAEP_VLOBJECT_C ////////////////////////////////////////

#ifdef YAEP_VLOBJECT_MODULE

/* The function implements macro `VLO_TAILOR'.  Length of memory
   allocated for VLO becames equal to VLO length (but memory for zero
   length object will contain one byte).  Remember that the VLO place
   may be changed after the call. */

void
_VLO_tailor_function (vlo_t * vlo)
{
  size_t vlo_length;
  char *new_vlo_start;

  assert (vlo->vlo_start != NULL);
  vlo_length = VLO_LENGTH (*vlo);
  if (vlo_length == 0)
    vlo_length = 1;
  new_vlo_start = (char*)yaep_realloc (vlo->vlo_alloc, vlo->vlo_start, vlo_length);
  if (new_vlo_start != vlo->vlo_start)
    {
      vlo->vlo_stop += new_vlo_start - vlo->vlo_start;
      vlo->vlo_start = new_vlo_start;
    }
  vlo->vlo_segment_stop = vlo->vlo_start + vlo_length;
}

/* The following function implements macro `VLO_ADD_STRING' (addition
   of string STR (with end marker is '\0') to the end of VLO).
   Remember that the VLO place may be changed after the call. */

void
_VLO_add_string_function (vlo_t * vlo, const char *str)
{
  size_t length;

  assert (vlo->vlo_start != NULL);
  if (str == NULL)
    return;
  if (vlo->vlo_stop != vlo->vlo_start)
    VLO_SHORTEN (*vlo, 1);
  length = strlen (str) + 1;
  if (vlo->vlo_stop + length > vlo->vlo_segment_stop)
    _VLO_expand_memory (vlo, length);
  memcpy( vlo->vlo_stop, str, length );
  vlo->vlo_stop = vlo->vlo_stop + length;
}

/* The following function changes size of memory allocated for VLO.
   The size becames equal to about one and a half of VLO length
   accounting for length of memory which will be added after the call.
   Remember that the VLO place may be changed after the call. */

void
_VLO_expand_memory (vlo_t * vlo, size_t additional_length)
{
  size_t vlo_length;
  char *new_vlo_start;

  assert (vlo->vlo_start != NULL);
  vlo_length = VLO_LENGTH (*vlo) + additional_length;
  vlo_length += vlo_length / 2 + 1;
  new_vlo_start = (char*)yaep_realloc (vlo->vlo_alloc, vlo->vlo_start, vlo_length);
  if (new_vlo_start != vlo->vlo_start)
    {
      vlo->vlo_stop += new_vlo_start - vlo->vlo_start;
      vlo->vlo_start = new_vlo_start;
    }
  vlo->vlo_segment_stop = vlo->vlo_start + vlo_length;
}

size_t vlo_memusage(vlo_t *vlo)
{
    if (!vlo) return 0;
    return vlo->vlo_segment_stop - vlo->vlo_start;
}

#endif

// PART C YAEP_UTIL_C ////////////////////////////////////////

#ifdef YAEP_UTIL_MODULE

void dbg_breakpoint()
{
}

void dbg_print_core(YaepParseState *ps, YaepStateSetCore *c)
{
    MemBuffer *mb = new_membuffer();
    print_core(mb, c);
    free_membuffer_and_free_content(mb);
}

void dbg_print_coresymbvects(YaepParseState *ps, YaepCoreSymbToPredComps *v)
{
    MemBuffer *mb = new_membuffer();
    print_coresymbvects(mb, ps, v);
    free_membuffer_and_free_content(mb);
}

void dbg_print_dotted_rule(YaepParseState *ps, YaepDottedRule *dotted_rule)
{
    MemBuffer *mb = new_membuffer();
    print_dotted_rule(mb, ps, 0 /*from_i*/, dotted_rule, 0, 0);
    membuffer_append_null(mb);
    puts(mb->buffer_);
    free_membuffer_and_free_content(mb);
}

void fetch_state_vars(YaepParseState *ps,
                      YaepStateSet *state_set,
                      StateVars *out)
{
    if (state_set == NULL && !ps->new_set_ready_p)
    {
        /* The following is necessary if we call the function from a
           debugger.  In this case new_set, new_core and their members
           may be not set up yet. */
        out->state_id = -1;
        out->core_id = -1;
        out->num_started_dotted_rules = out->num_dotted_rules
            = out->num_all_matched_lengths = ps->new_num_leading_dotted_rules;
        out->dotted_rules = ps->new_dotted_rules;
        out->matched_lengths = ps->new_matched_lengths;
        out->to_parent_rule_index = NULL;
        return;
    }

    out->state_id = state_set->id;
    out->core_id = state_set->core->id;
    out->num_dotted_rules = state_set->core->num_dotted_rules;
    out->dotted_rules = state_set->core->dotted_rules;
    out->num_started_dotted_rules = state_set->core->num_started_dotted_rules;
    out->matched_lengths = state_set->matched_lengths;
    out->num_all_matched_lengths = state_set->core->num_all_matched_lengths;
    out->to_parent_rule_index = state_set->core->to_parent_rule_index;
    out->num_started_dotted_rules = state_set->core->num_started_dotted_rules;
}

int find_matched_length(YaepParseState *ps,
                        YaepStateSet *state_set,
                        StateVars *vars,
                        int rule_index_in_core)
{
    int matched_length;

    if (rule_index_in_core >= vars->num_all_matched_lengths)
    {
        // No match yet.
        matched_length = 0;
    }
    else if (rule_index_in_core< vars->num_started_dotted_rules)
    {
        matched_length = vars->matched_lengths[rule_index_in_core];
    }
    else
    {
        matched_length = vars->matched_lengths[vars->to_parent_rule_index[rule_index_in_core]];
    }

    return matched_length;
}

void yaep_debug(YaepParseState *ps, const char *format, ...)
{
    if (!ps->run.debug) return;

    va_list ap;
    va_start(ap, format);

    MemBuffer *mb = new_membuffer();
    membuffer_printf(mb, "@%d ", ps->tok_i);

    char *buf = buf_vsnprintf(format, ap);
    membuffer_append(mb, buf);
    membuffer_append_null(mb);
    free(buf);

    debug_mb("ixml.pa.debug=", mb);
    free_membuffer_and_free_content(mb);
    return;
}

void yaep_trace(YaepParseState *ps, const char *format, ...)
{
    if (!ps->run.trace) return;

    va_list ap;
    va_start(ap, format);

    MemBuffer *mb = new_membuffer();
    membuffer_printf(mb, "@%d ", ps->tok_i);

    char *buf = buf_vsnprintf(format, ap);
    membuffer_append(mb, buf);
    membuffer_append_null(mb);
    free(buf);

    debug_mb("ixml.pa.trace=", mb);
    free_membuffer_and_free_content(mb);
    return;
}

void yaep_view(YaepParseState *ps, const char *format, ...)
{
    if (!ps->run.debug) return;

    va_list ap;
    va_start(ap, format);

    MemBuffer *mb = new_membuffer();
    membuffer_printf(mb, "@%d ", ps->tok_i);

    char *buf = buf_vsnprintf(format, ap);
    membuffer_append(mb, buf);
    membuffer_append_null(mb);
    free(buf);

    debug_mb("ixml.pa.view=", mb);
    free_membuffer_and_free_content(mb);
    return;
}

#endif

// PART C YAEP_SYMBOLS_C ////////////////////////////////////////

#ifdef YAEP_SYMBOLS_MODULE

unsigned symb_repr_hash(hash_table_entry_t s)
{
    YaepSymbol *sym = (YaepSymbol*)s;
    unsigned result = jauquet_prime_mod32;

    for (const char *i = sym->repr; *i; i++)
    {
        result = result * hash_shift +(unsigned)*i;
    }

     return result;
}

bool symb_repr_eq(hash_table_entry_t s1, hash_table_entry_t s2)
{
    YaepSymbol *sym1 = (YaepSymbol*)s1;
    YaepSymbol *sym2 = (YaepSymbol*)s2;

    return !strcmp(sym1->repr, sym2->repr);
}

unsigned symb_code_hash(hash_table_entry_t s)
{
    YaepSymbol *sym = (YaepSymbol*)s;

    assert(sym->is_terminal);

    return sym->u.terminal.code;
}

bool symb_code_eq(hash_table_entry_t s1, hash_table_entry_t s2)
{
    YaepSymbol *sym1 = (YaepSymbol*)s1;
    YaepSymbol *sym2 = (YaepSymbol*)s2;

    assert(sym1->is_terminal && sym2->is_terminal);

    return sym1->u.terminal.code == sym2->u.terminal.code;
}

YaepSymbolStorage *symbolstorage_create(YaepGrammar *grammar)
{
    void*mem;
    YaepSymbolStorage*result;

    mem = yaep_malloc(grammar->alloc, sizeof(YaepSymbolStorage));
    result = (YaepSymbolStorage*)mem;
    OS_CREATE(result->symbs_os, grammar->alloc, 0);
    VLO_CREATE(result->symbs_vlo, grammar->alloc, 1024);
    VLO_CREATE(result->terminals_vlo, grammar->alloc, 512);
    VLO_CREATE(result->nonterminals_vlo, grammar->alloc, 512);
    result->map_repr_to_symb = create_hash_table(grammar->alloc, 300, symb_repr_hash, symb_repr_eq);
    result->map_code_to_symb = create_hash_table(grammar->alloc, 200, symb_code_hash, symb_code_eq);
    result->symb_code_trans_vect = NULL;
    result->num_nonterminals = 0;
    result->num_terminals = 0;

    return result;
}

/* Return symbol(or NULL if it does not exist) whose representation is REPR.*/
YaepSymbol *symb_find_by_repr(YaepParseState *ps, const char*repr)
{
    YaepSymbol symb;
    symb.repr = repr;
    YaepSymbol*r = (YaepSymbol*)*find_hash_table_entry(ps->run.grammar->symbs_ptr->map_repr_to_symb, &symb, false);

    return r;
}

/* Return symbol(or NULL if it does not exist) which is terminal with CODE. */
YaepSymbol *symb_find_by_code(YaepParseState *ps, int code)
{
    YaepSymbol symb;

    if (ps->run.grammar->symbs_ptr->symb_code_trans_vect != NULL)
    {
        if ((code < ps->run.grammar->symbs_ptr->symb_code_trans_vect_start) ||(code >= ps->run.grammar->symbs_ptr->symb_code_trans_vect_end))
        {
            return NULL;
        }
        else
        {
            YaepSymbol *r = ps->run.grammar->symbs_ptr->symb_code_trans_vect[code - ps->run.grammar->symbs_ptr->symb_code_trans_vect_start];
            return r;
        }
    }

    symb.is_terminal = true;
    symb.u.terminal.code = code;
    YaepSymbol*r =(YaepSymbol*)*find_hash_table_entry(ps->run.grammar->symbs_ptr->map_code_to_symb, &symb, false);

    return r;
}

YaepSymbol *symb_find_by_term_id(YaepParseState *ps, int term_id)
{
    return symb_find_by_code(ps, term_id+ps->run.grammar->symbs_ptr->symb_code_trans_vect_start);
}

/* The function creates new terminal symbol and returns reference for
   it.  The symbol should be not in the tables.  The function should
   create own copy of name for the new symbol. */
YaepSymbol *symb_add_terminal(YaepParseState *ps, const char*name, int code)
{
    YaepSymbol symb, *result;
    hash_table_entry_t *repr_entry, *code_entry;

    symb.repr = name;
    if (code >= 32 && code <= 126)
    {
        snprintf(symb.hr, 6, "'%c'", code);
    }
    else
    {
        strncpy(symb.hr, name, 6);
    }
    symb.is_terminal = true;
    // Assign the next available id.
    symb.id = ps->run.grammar->symbs_ptr->num_nonterminals + ps->run.grammar->symbs_ptr->num_terminals;
    symb.u.terminal.code = code;
    symb.u.terminal.term_id = ps->run.grammar->symbs_ptr->num_terminals++;
    symb.empty_p = false;
    symb.is_not_operator = false;
    repr_entry = find_hash_table_entry(ps->run.grammar->symbs_ptr->map_repr_to_symb, &symb, true);
    assert(*repr_entry == NULL);
    code_entry = find_hash_table_entry(ps->run.grammar->symbs_ptr->map_code_to_symb, &symb, true);
    assert(*code_entry == NULL);
    OS_TOP_ADD_STRING(ps->run.grammar->symbs_ptr->symbs_os, name);
    symb.repr =(char*) OS_TOP_BEGIN(ps->run.grammar->symbs_ptr->symbs_os);
    OS_TOP_FINISH(ps->run.grammar->symbs_ptr->symbs_os);
    OS_TOP_ADD_MEMORY(ps->run.grammar->symbs_ptr->symbs_os, &symb, sizeof(YaepSymbol));
    result =(YaepSymbol*) OS_TOP_BEGIN(ps->run.grammar->symbs_ptr->symbs_os);
    OS_TOP_FINISH(ps->run.grammar->symbs_ptr->symbs_os);
   *repr_entry =(hash_table_entry_t) result;
   *code_entry =(hash_table_entry_t) result;
    VLO_ADD_MEMORY(ps->run.grammar->symbs_ptr->symbs_vlo, &result, sizeof(YaepSymbol*));
    VLO_ADD_MEMORY(ps->run.grammar->symbs_ptr->terminals_vlo, &result, sizeof(YaepSymbol*));

    return result;
}

/* The function creates new nonterminal symbol and returns reference
   for it.  The symbol should be not in the table. The function
   should create own copy of name for the new symbol. */
YaepSymbol *symb_add_nonterm(YaepParseState *ps, const char *name)
{
    YaepSymbol symb,*result;
    hash_table_entry_t*entry;

    symb.repr = name;
    strncpy(symb.hr, name, 6);

    symb.is_terminal = false;
    symb.is_not_operator = false;
    // Assign the next available id.
    symb.id = ps->run.grammar->symbs_ptr->num_nonterminals + ps->run.grammar->symbs_ptr->num_terminals;
    symb.u.nonterminal.rules = NULL;
    symb.u.nonterminal.loop_p = false;
    symb.u.nonterminal.nonterm_id = ps->run.grammar->symbs_ptr->num_nonterminals++;
    symb.is_not_operator = (symb.repr[0] == '|' && symb.repr[1] == '!');

    entry = find_hash_table_entry(ps->run.grammar->symbs_ptr->map_repr_to_symb, &symb, true);
    assert(*entry == NULL);
    OS_TOP_ADD_STRING(ps->run.grammar->symbs_ptr->symbs_os, name);
    symb.repr =(char*) OS_TOP_BEGIN(ps->run.grammar->symbs_ptr->symbs_os);
    OS_TOP_FINISH(ps->run.grammar->symbs_ptr->symbs_os);
    OS_TOP_ADD_MEMORY(ps->run.grammar->symbs_ptr->symbs_os, &symb, sizeof(YaepSymbol));
    result =(YaepSymbol*) OS_TOP_BEGIN(ps->run.grammar->symbs_ptr->symbs_os);
    OS_TOP_FINISH(ps->run.grammar->symbs_ptr->symbs_os);
    *entry =(hash_table_entry_t) result;
    VLO_ADD_MEMORY(ps->run.grammar->symbs_ptr->symbs_vlo, &result, sizeof(YaepSymbol*));
    VLO_ADD_MEMORY(ps->run.grammar->symbs_ptr->nonterminals_vlo, &result, sizeof(YaepSymbol*));


    return result;
}

/* The following function return N-th symbol(if any) or NULL otherwise. */
YaepSymbol *symb_get(YaepParseState *ps, int id)
{
    if (id < 0 ||(VLO_LENGTH(ps->run.grammar->symbs_ptr->symbs_vlo) / sizeof(YaepSymbol*) <=(size_t) id))
    {
        return NULL;
    }
    YaepSymbol **vect = (YaepSymbol**)VLO_BEGIN(ps->run.grammar->symbs_ptr->symbs_vlo);
    YaepSymbol *symb = vect[id];
    assert(symb->id == id);

    return symb;
}

/* The following function return N-th symbol(if any) or NULL otherwise. */
YaepSymbol *term_get(YaepParseState *ps, int n)
{
    if (n < 0 || (VLO_LENGTH(ps->run.grammar->symbs_ptr->terminals_vlo) / sizeof(YaepSymbol*) <=(size_t) n))
    {
        return NULL;
    }
    YaepSymbol*symb =((YaepSymbol**) VLO_BEGIN(ps->run.grammar->symbs_ptr->terminals_vlo))[n];
    assert(symb->is_terminal && symb->u.terminal.term_id == n);

    return symb;
}

/* The following function return N-th symbol(if any) or NULL otherwise. */
YaepSymbol *nonterm_get(YaepParseState *ps, int n)
{
    if (n < 0 ||(VLO_LENGTH(ps->run.grammar->symbs_ptr->nonterminals_vlo) / sizeof(YaepSymbol*) <=(size_t) n))
    {
        return NULL;
    }
    YaepSymbol*symb =((YaepSymbol**) VLO_BEGIN(ps->run.grammar->symbs_ptr->nonterminals_vlo))[n];
    assert(!symb->is_terminal && symb->u.nonterminal.nonterm_id == n);

    return symb;
}

void symb_finish_adding_terms(YaepParseState *ps)
{
    int i, max_code, min_code;
    YaepSymbol *symb;
    void *mem;

    for (min_code = max_code = i = 0;(symb = term_get(ps, i)) != NULL; i++)
    {
        if (i == 0 || min_code > symb->u.terminal.code) min_code = symb->u.terminal.code;
        if (i == 0 || max_code < symb->u.terminal.code) max_code = symb->u.terminal.code;
    }
    assert(i != 0);
    assert((max_code - min_code) < MAX_SYMB_CODE_TRANS_VECT_SIZE);

    ps->run.grammar->symbs_ptr->symb_code_trans_vect_start = min_code;
    ps->run.grammar->symbs_ptr->symb_code_trans_vect_end = max_code + 1;

    size_t num_codes = max_code - min_code + 1;
    size_t vec_size = num_codes * sizeof(YaepSymbol*);
    mem = yaep_malloc(ps->run.grammar->alloc, vec_size);

    ps->run.grammar->symbs_ptr->symb_code_trans_vect =(YaepSymbol**)mem;

    for(i = 0;(symb = term_get(ps, i)) != NULL; i++)
    {
        ps->run.grammar->symbs_ptr->symb_code_trans_vect[symb->u.terminal.code - min_code] = symb;
    }

    //debug("ixml.stats=", "num_codes=%zu offset=%d size=%zu", num_codes, ps->run.grammar->symbs_ptr->symb_code_trans_vect_start, vec_size);
}

/* Free memory for symbols. */
void symb_empty(YaepParseState *ps, YaepSymbolStorage *symbs)
{
    if (symbs == NULL) return;

    if (ps->run.grammar->symbs_ptr->symb_code_trans_vect != NULL)
    {
        yaep_free(ps->run.grammar->alloc, ps->run.grammar->symbs_ptr->symb_code_trans_vect);
        ps->run.grammar->symbs_ptr->symb_code_trans_vect = NULL;
    }

    empty_hash_table(symbs->map_repr_to_symb);
    empty_hash_table(symbs->map_code_to_symb);
    VLO_NULLIFY(symbs->nonterminals_vlo);
    VLO_NULLIFY(symbs->terminals_vlo);
    VLO_NULLIFY(symbs->symbs_vlo);
    OS_EMPTY(symbs->symbs_os);
    symbs->num_nonterminals = symbs->num_terminals = 0;
}

void symbolstorage_free(YaepParseState *ps, YaepSymbolStorage *symbs)
{
    if (symbs == NULL) return;

    if (ps->run.grammar->symbs_ptr->symb_code_trans_vect != NULL)
    {
        yaep_free(ps->run.grammar->alloc, ps->run.grammar->symbs_ptr->symb_code_trans_vect);
    }

    delete_hash_table(ps->run.grammar->symbs_ptr->map_repr_to_symb);
    delete_hash_table(ps->run.grammar->symbs_ptr->map_code_to_symb);
    VLO_DELETE(ps->run.grammar->symbs_ptr->nonterminals_vlo);
    VLO_DELETE(ps->run.grammar->symbs_ptr->terminals_vlo);
    VLO_DELETE(ps->run.grammar->symbs_ptr->symbs_vlo);
    OS_DELETE(ps->run.grammar->symbs_ptr->symbs_os);
    yaep_free(ps->run.grammar->alloc, symbs);
}

#endif

// PART C YAEP_TERMINAL_BITSET_C ////////////////////////////////////////

#ifdef YAEP_TERMINAL_BITSET_MODULE

unsigned terminal_bitset_hash(hash_table_entry_t s)
{
    YaepTerminalSet *ts = (YaepTerminalSet*)s;
    terminal_bitset_t *set = ts->set;
    int num_elements = ts->num_elements;
    terminal_bitset_t *bound = set + num_elements;
    unsigned result = jauquet_prime_mod32;

    while (set < bound)
    {
        result = result * hash_shift + *set++;
    }
    return result;
}

/* Equality of terminal sets. */
bool terminal_bitset_eq(hash_table_entry_t s1, hash_table_entry_t s2)
{
    YaepTerminalSet *ts1 = (YaepTerminalSet*)s1;
    YaepTerminalSet *ts2 = (YaepTerminalSet*)s2;
    terminal_bitset_t *i = ts1->set;
    terminal_bitset_t *j = ts2->set;

    assert(ts1->num_elements == ts2->num_elements);

    int num_elements = ts1->num_elements;
    terminal_bitset_t *i_bound = i + num_elements;

    while (i < i_bound)
    {
        if (*i++ != *j++)
        {
            return false;
        }
    }
    return true;
}

YaepTerminalSetStorage *termsetstorage_create(YaepGrammar *grammar)
{
    void *mem;
    YaepTerminalSetStorage *result;

    mem = yaep_malloc(grammar->alloc, sizeof(YaepTerminalSetStorage));
    result =(YaepTerminalSetStorage*) mem;
    OS_CREATE(result->terminal_bitset_os, grammar->alloc, 0);
    result->map_terminal_bitset_to_id = create_hash_table(grammar->alloc, 1000, terminal_bitset_hash, terminal_bitset_eq);
    VLO_CREATE(result->terminal_bitset_vlo, grammar->alloc, 4096);
    result->n_term_sets = result->n_term_sets_size = 0;

    return result;
}

terminal_bitset_t *terminal_bitset_create(YaepParseState *ps)
{
    int size_bytes;
    terminal_bitset_t *result;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;

    assert(sizeof(terminal_bitset_t) <= 8);

    size_bytes = sizeof(terminal_bitset_t) * CALC_NUM_ELEMENTS(num_terminals);

    OS_TOP_EXPAND(ps->run.grammar->term_sets_ptr->terminal_bitset_os, size_bytes);
    result =(terminal_bitset_t*) OS_TOP_BEGIN(ps->run.grammar->term_sets_ptr->terminal_bitset_os);
    OS_TOP_FINISH(ps->run.grammar->term_sets_ptr->terminal_bitset_os);
    ps->run.grammar->term_sets_ptr->n_term_sets++;
    ps->run.grammar->term_sets_ptr->n_term_sets_size += size_bytes;

    return result;
}

void terminal_bitset_clear(YaepParseState *ps, terminal_bitset_t* set)
{
    terminal_bitset_t*bound;
    int size;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;

    size = CALC_NUM_ELEMENTS(num_terminals);
    bound = set + size;
    while(set < bound)
    {
       *set++ = 0;
    }
}

void terminal_bitset_fill(YaepParseState *ps, terminal_bitset_t* set)
{
    terminal_bitset_t*bound;
    int size;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;

    size = CALC_NUM_ELEMENTS(num_terminals);
    bound = set + size;
    while(set < bound)
    {
        *set++ = (terminal_bitset_t)-1;
    }
}

/* Copy SRC into DEST. */
void terminal_bitset_copy(YaepParseState *ps, terminal_bitset_t *dest, terminal_bitset_t *src)
{
    terminal_bitset_t *bound;
    int size;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;

    size = CALC_NUM_ELEMENTS(num_terminals);
    bound = dest + size;

    while (dest < bound)
    {
       *dest++ = *src++;
    }
}

/* Add all terminals from set OP with to SET.  Return true if SET has been changed.*/
bool terminal_bitset_or(YaepParseState *ps, terminal_bitset_t *set, terminal_bitset_t *op)
{
    terminal_bitset_t *bound;
    int size;
    bool changed_p;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;

    size = CALC_NUM_ELEMENTS(num_terminals);
    bound = set + size;
    changed_p = false;
    while (set < bound)
    {
        if ((*set |*op) !=*set)
        {
            changed_p = true;
        }
       *set++ |= *op++;
    }
    return changed_p;
}

/* Add terminal with number NUM to SET.  Return true if SET has been changed.*/
bool terminal_bitset_up(YaepParseState *ps, terminal_bitset_t *set, int num)
{
    const int bits_in_word = CHAR_BIT*sizeof(terminal_bitset_t);

    int word_offset;
    terminal_bitset_t bit_in_word;
    bool changed_p;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;

    assert(num < num_terminals);

    word_offset = num / bits_in_word;
    bit_in_word = ((terminal_bitset_t)1) << (num % bits_in_word);
    changed_p = (set[word_offset] & bit_in_word ? false : true);
    set[word_offset] |= bit_in_word;

    return changed_p;
}

/* Remove terminal with number NUM from SET.  Return true if SET has been changed.*/
bool terminal_bitset_down(YaepParseState *ps, terminal_bitset_t *set, int num)
{
    const int bits_in_word = CHAR_BIT*sizeof(terminal_bitset_t);

    int word_offset;
    terminal_bitset_t bit_in_word;
    bool changed_p;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;

    assert(num < num_terminals);

    word_offset = num / bits_in_word;
    bit_in_word = ((terminal_bitset_t)1) << (num % bits_in_word);
    changed_p = (set[word_offset] & bit_in_word ? true : false);
    set[word_offset] &= ~bit_in_word;

    return changed_p;
}

/* Return true if terminal with number NUM is in SET. */
int terminal_bitset_test(YaepParseState *ps, terminal_bitset_t *set, int num)
{
    const int bits_in_word = CHAR_BIT*sizeof(terminal_bitset_t);

    int word_offset;
    terminal_bitset_t bit_in_word;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;

    assert(num < num_terminals);

    word_offset = num / bits_in_word;
    bit_in_word = ((terminal_bitset_t)1) << (num % bits_in_word);

    return (set[word_offset] & bit_in_word) != 0;
}

/* The following function inserts terminal SET into the table and
   returns its number.  If the set is already in table it returns -its
   number - 1(which is always negative).  Don't set after
   insertion!!! */
int terminal_bitset_insert(YaepParseState *ps, terminal_bitset_t *set)
{
    hash_table_entry_t *entry;
    YaepTerminalSet term_set,*terminal_bitset_ptr;

    term_set.set = set;
    entry = find_hash_table_entry(ps->run.grammar->term_sets_ptr->map_terminal_bitset_to_id, &term_set, true);

    if (*entry != NULL)
    {
        return -((YaepTerminalSet*)*entry)->id - 1;
    }
    else
    {
        OS_TOP_EXPAND(ps->run.grammar->term_sets_ptr->terminal_bitset_os, sizeof(YaepTerminalSet));
        terminal_bitset_ptr = (YaepTerminalSet*)OS_TOP_BEGIN(ps->run.grammar->term_sets_ptr->terminal_bitset_os);
        OS_TOP_FINISH(ps->run.grammar->term_sets_ptr->terminal_bitset_os);
       *entry =(hash_table_entry_t) terminal_bitset_ptr;
        terminal_bitset_ptr->set = set;
        terminal_bitset_ptr->id = (VLO_LENGTH(ps->run.grammar->term_sets_ptr->terminal_bitset_vlo) / sizeof(YaepTerminalSet*));
        terminal_bitset_ptr->num_elements = CALC_NUM_ELEMENTS(ps->run.grammar->symbs_ptr->num_terminals);

        VLO_ADD_MEMORY(ps->run.grammar->term_sets_ptr->terminal_bitset_vlo, &terminal_bitset_ptr, sizeof(YaepTerminalSet*));

        return((YaepTerminalSet*)*entry)->id;
    }
}

/* The following function returns set which is in the table with number NUM. */
terminal_bitset_t *terminal_bitset_from_table(YaepParseState *ps, int num)
{
    assert(num >= 0);
    assert((long unsigned int)num < VLO_LENGTH(ps->run.grammar->term_sets_ptr->terminal_bitset_vlo) / sizeof(YaepTerminalSet*));

    return ((YaepTerminalSet**)VLO_BEGIN(ps->run.grammar->term_sets_ptr->terminal_bitset_vlo))[num]->set;
}

/* Free memory for terminal sets. */
void terminal_bitset_empty(YaepTerminalSetStorage *term_sets)
{
    if (term_sets == NULL) return;

    VLO_NULLIFY(term_sets->terminal_bitset_vlo);
    empty_hash_table(term_sets->map_terminal_bitset_to_id);
    OS_EMPTY(term_sets->terminal_bitset_os);
    term_sets->n_term_sets = term_sets->n_term_sets_size = 0;
}

void termsetstorage_free(YaepGrammar *grammar, YaepTerminalSetStorage *term_sets)
{
    if (term_sets == NULL) return;

    VLO_DELETE(term_sets->terminal_bitset_vlo);
    delete_hash_table(term_sets->map_terminal_bitset_to_id);
    OS_DELETE(term_sets->terminal_bitset_os);
    yaep_free(grammar->alloc, term_sets);
    term_sets = NULL;
}

#endif

// PART C YAEP_TREE_C ////////////////////////////////////////

#ifdef YAEP_TREE_MODULE

static unsigned parse_state_hash(hash_table_entry_t s)
{
    YaepParseTreeBuildState*state =((YaepParseTreeBuildState*) s);

    /* The table contains only states with dot at the end of rule. */
    assert(state->dot_j == state->rule->rhs_len);
    return(((jauquet_prime_mod32* hash_shift +
             (unsigned)(size_t) state->rule)* hash_shift +
             state->from_i)* hash_shift + state->state_set_k);
}

static bool parse_state_eq(hash_table_entry_t s1, hash_table_entry_t s2)
{
    YaepParseTreeBuildState*state1 =((YaepParseTreeBuildState*) s1);
    YaepParseTreeBuildState*state2 =((YaepParseTreeBuildState*) s2);

    /* The table contains only states with dot at the end of rule.*/
    assert(state1->dot_j == state1->rule->rhs_len
            && state2->dot_j == state2->rule->rhs_len);
    return(state1->rule == state2->rule && state1->from_i == state2->from_i
            && state1->state_set_k == state2->state_set_k);
}

/* The following function initializes work with parser states.*/
static void parse_state_init(YaepParseState *ps)
{
    ps->free_parse_state = NULL;
    OS_CREATE(ps->parse_state_os, ps->run.grammar->alloc, 0);
    if (!ps->run.grammar->one_parse_p)
    {
        ps->map_rule_orig_statesetind_to_internalstate =
            create_hash_table(ps->run.grammar->alloc, ps->input_len* 2,
                              (hash_table_hash_function)parse_state_hash,
                              (hash_table_eq_function)parse_state_eq);
    }
}

/* The following function returns new parser state.*/
static YaepParseTreeBuildState *parse_state_alloc(YaepParseState *ps)
{
    YaepParseTreeBuildState*result;

    if (ps->free_parse_state == NULL)
    {
        OS_TOP_EXPAND(ps->parse_state_os, sizeof(YaepParseTreeBuildState));
        result =(YaepParseTreeBuildState*) OS_TOP_BEGIN(ps->parse_state_os);
        OS_TOP_FINISH(ps->parse_state_os);
    }
    else
    {
        result = ps->free_parse_state;
        ps->free_parse_state =(YaepParseTreeBuildState*) ps->free_parse_state->rule;
    }
    return result;
}

/* The following function frees STATE.*/
static void parse_state_free(YaepParseState *ps, YaepParseTreeBuildState*state)
{
    state->rule = (YaepRule*)ps->free_parse_state;
    ps->free_parse_state = state;
}

/* The following function searches for state in the table with the
   same characteristics as "state".  If found, then it returns a pointer
   to the state in the table.  Otherwise the function makes copy of
  *STATE, inserts into the table and returns pointer to copied state.
   In the last case, the function also sets up*NEW_P.*/
static YaepParseTreeBuildState *parse_state_insert(YaepParseState *ps, YaepParseTreeBuildState *state, bool *new_p)
{
    YaepParseTreeBuildState **entry = (YaepParseTreeBuildState**)find_hash_table_entry(
        ps->map_rule_orig_statesetind_to_internalstate,
        state,
        true);

    *new_p = false;

    if (*entry != NULL)
    {
        return *entry;
    }

    *new_p = true;

    /* We make copy because state_set_k can be changed in further processing state. */
    *entry = parse_state_alloc(ps);
    **entry = *state;

    return *entry;
}

static void free_parse_state(YaepParseState *ps)
{
    if (!ps->run.grammar->one_parse_p)
    {
        delete_hash_table(ps->map_rule_orig_statesetind_to_internalstate);
    }
    OS_DELETE(ps->parse_state_os);
}

/* The following function places translation NODE into *PLACE and
   creates alternative nodes if it is necessary. */
static void place_translation(YaepParseState *ps, YaepTreeNode **place, YaepTreeNode *node)
{
    YaepTreeNode *alt, *next_alt;

    assert(place != NULL);
    if (*place == NULL)
    {
        *place = node;
        return;
    }
    /* We need an alternative.*/

    ps->n_parse_alt_nodes++;

    alt =(YaepTreeNode*)(*ps->run.parse_alloc)(sizeof(YaepTreeNode));
    alt->type = YAEP_ALT;
    alt->val.alt.node = node;
    if ((*place)->type == YAEP_ALT)
    {
        alt->val.alt.next =*place;
    }
    else
    {
        /* We need alternative node for the 1st
           alternative too.*/
        ps->n_parse_alt_nodes++;
        next_alt = alt->val.alt.next
            =((YaepTreeNode*)
              (*ps->run.parse_alloc)(sizeof(YaepTreeNode)));
        next_alt->type = YAEP_ALT;
        next_alt->val.alt.node =*place;
        next_alt->val.alt.next = NULL;
    }
   *place = alt;
}

static YaepTreeNode *copy_anode(YaepParseState *ps,
                                YaepTreeNode **place,
                                YaepTreeNode *anode,
                                YaepRule *rule,
                                int rhs_offset)
{
    YaepTreeNode*node;
    int i;

    node = ((YaepTreeNode*)(*ps->run.parse_alloc)(sizeof(YaepTreeNode)
                                              + sizeof(YaepTreeNode*)
                                              *(rule->trans_len + 1)));
   *node =*anode;
    node->val.anode.children = ((YaepTreeNode**)((char*) node + sizeof(YaepTreeNode)));
    for(i = 0; i <= rule->trans_len; i++)
    {
        node->val.anode.children[i] = anode->val.anode.children[i];
    }
    node->val.anode.children[rhs_offset] = NULL;
    place_translation(ps, place, node);

    return node;
}

static void loop_stack(YaepTreeNode **result,
                       int n_candidates,
                       YaepParseState *ps,
                       YaepTreeNode *empty_node,
                       YaepTreeNode *error_node,
                       YaepStateSet *set,
                       YaepDottedRule *dotted_rule,
                       bool *ambiguous_p)
{
    YaepParseTreeBuildState root_state;
    YaepTreeNode root_anode;

    vlo_t stack, orig_states;

    YaepTreeNode **term_node_array = NULL;

    VLO_CREATE(stack, ps->run.grammar->alloc, 10000);

    if (!ps->run.grammar->one_parse_p)
    {
        /* We need this array to reuse terminal nodes when building a parse tree with ALT nodes.
           I.e. a tree with multiple possible parses. */
        size_t term_node_array_size = ps->input_len * sizeof(YaepTreeNode*);
        term_node_array = (YaepTreeNode**)yaep_malloc(ps->run.grammar->alloc, term_node_array_size);
        memset(term_node_array, 0, term_node_array_size);
        /* The following is used to check necessity to create current state with different state_set_k. */
        VLO_CREATE(orig_states, ps->run.grammar->alloc, 0);
    }

    // The root abstract node points to the result tree pointer.
    root_anode.val.anode.children = result;
    // The root state pointsto the root abstract node.
    root_state.anode = &root_anode;

    // Push a new state on the stack
    YaepParseTreeBuildState *state = parse_state_alloc(ps);
    VLO_EXPAND(stack, sizeof(YaepParseTreeBuildState*));
    ((YaepParseTreeBuildState**) VLO_BOUND(stack))[-1] = state;

    YaepRule *rule = state->rule = dotted_rule->rule;
    state->dotted_rule = dotted_rule;
    state->dot_j = dotted_rule->dot_j;
    state->from_i = 0;
    state->state_set_k = ps->state_set_k;

    // Again is state_set_k always == tok_i ????
    assert(ps->state_set_k == ps->tok_i);

    state->parent_anode_state = &root_state;
    state->parent_rhs_offset = 0;
    state->anode = NULL;

    if (ps->run.debug)
    {
        // Log the starting node.
        MemBuffer *mb = new_membuffer();
	membuffer_printf(mb, "adding (d%d,%d-%d) ",
                         state->dotted_rule->id,
                         state->from_i,
                         state->state_set_k);
	print_rule(mb, ps, state->rule);
        debug_mb("ixml.bt.step=", mb);
        free_membuffer_and_free_content(mb);
    }

    while (VLO_LENGTH(stack) != 0)
    {
        if (ps->run.debug && state->dot_j == state->rule->rhs_len)
        {
            // top = (long) VLO_LENGTH(stack) / sizeof(YaepParseTreeBuildState*) - 1
            MemBuffer *mb = new_membuffer();
            membuffer_printf(mb, "push (s%d,d%d) [%d-%d]    ", state->state_set_k, state->dotted_rule->id, state->from_i, state->state_set_k);
            print_rule(mb, ps, state->rule);
            debug_mb("ixml.bt.info=", mb);
            free_membuffer_and_free_content(mb);
        }
        int pos_j = --state->dot_j;
        rule = state->rule;
        YaepParseTreeBuildState *parent_anode_state = state->parent_anode_state;
        YaepTreeNode *parent_anode = parent_anode_state->anode;
        int parent_rhs_offset = state->parent_rhs_offset;
        YaepTreeNode *anode = state->anode;
        int rhs_offset = rule->order[pos_j];
        int state_set_k = state->state_set_k;
        int from_i = state->from_i;
        if (pos_j < 0)
        {
            /* We've processed all rhs of the rule.*/
            if (ps->run.debug)
            {
                MemBuffer *mb = new_membuffer();
                membuffer_printf(mb, "pop ");
                print_rule(mb, ps, state->rule);
                debug_mb("ixml.bt.info=", mb);
                free_membuffer_and_free_content(mb);
            }
            parse_state_free(ps, state);
            VLO_SHORTEN(stack, sizeof(YaepParseTreeBuildState*));
            if (VLO_LENGTH(stack) != 0)
            {
                state = ((YaepParseTreeBuildState**) VLO_BOUND(stack))[-1];
            }
            if (parent_anode != NULL && rule->trans_len == 0 && anode == NULL)
            {
                /* We do dotted_ruleuce nothing but we should. So write empty node.*/
                place_translation(ps, parent_anode->val.anode.children + parent_rhs_offset, empty_node);
                empty_node->val.nil.used = 1;
            } else
            if (anode != NULL)
            {
                /* Change NULLs into empty nodes.  We can not make it
                 the first time because when building several parses
                 the NULL means flag of absence of translations(see
                 function `place_translation'). */
                for (int i = 0; i < rule->trans_len; i++)
                {
                    if (anode->val.anode.children[i] == NULL)
                    {
                        anode->val.anode.children[i] = empty_node;
                        empty_node->val.nil.used = 1;
                    }
                }
            }

            continue;
        }
        assert(pos_j >= 0);
        YaepTreeNode *node;
        YaepSymbol *symb = rule->rhs[pos_j];
        if (symb->is_terminal)
        {
            /* Terminal before dot:*/
            state_set_k--; /* l*/
            /* Because of error recovery input [state_set_k].symb may be not equal to symb.*/
            //assert(ps->input[state_set_k].symb == symb);
            if (parent_anode != NULL && rhs_offset >= 0)
            {
                /* We should generate and use the translation of the terminal.  Add reference to the current node.*/
                if (symb == ps->run.grammar->term_error)
                {
                    // Oups error node.
                    node = error_node;
                    error_node->val.error.used = 1;
                } else
                if (!ps->run.grammar->one_parse_p && (node = term_node_array[state_set_k]) != NULL)
                {
                    // Reuse existing terminal node.
                } else
                {
                    // Allocate terminal node.
                    ps->n_parse_term_nodes++;
                    node = ((YaepTreeNode*) (*ps->run.parse_alloc)(sizeof(YaepTreeNode)));
                    node->type = YAEP_TERM;
                    node->val.terminal.code = symb->u.terminal.code;
                    if (rule->marks && rule->marks[pos_j])
                    {
                        // Copy the ixml mark from the rhs position on to the terminal.
                        node->val.terminal.mark = rule->marks[pos_j];
                    }
                    // Copy any attr from input token to parsed terminal.
                    node->val.terminal.attr = ps->input[state_set_k].attr;
                    if (!ps->run.grammar->one_parse_p)
                    {
                        term_node_array[state_set_k] = node;
                    }
                }

                YaepTreeNode **placement = NULL;
                if (anode)
                {
                    placement = anode->val.anode.children + rhs_offset;
                } else
                {
                    placement = parent_anode->val.anode.children + parent_rhs_offset;
                }
                place_translation(ps, placement, node);
            }
            if (pos_j != 0)
            {
                state->state_set_k = state_set_k;
            }
            continue;
        }
        /* Nonterminal before dot: */
        set = ps->state_sets[state_set_k];
        YaepStateSetCore *set_core = set->core;
        YaepCoreSymbToPredComps *core_symb_to_predcomps = core_symb_to_predcomps_find(ps, set_core, symb);
        //debug("ixml.pa.c=", "core core%d symb %s -> %p", set_core->id, symb->hr, core_symb_to_predcomps);
        if (!core_symb_to_predcomps)
            continue;

        assert(core_symb_to_predcomps->completions.len != 0);
        n_candidates = 0;
        YaepParseTreeBuildState *orig_state = state;
        if (!ps->run.grammar->one_parse_p)
        {
            VLO_NULLIFY(orig_states);
        }
        for (int i = 0; i < core_symb_to_predcomps->completions.len; i++)
        {
            int rule_index_in_core = core_symb_to_predcomps->completions.ids[i];
            dotted_rule = set_core->dotted_rules[rule_index_in_core];
            int dotted_rule_from_i;
            if (rule_index_in_core < set_core->num_started_dotted_rules)
            {
                // The state_set_k is the tok_i for which the state set was created.
                // Ie, it is the to_i inside the Earley item.
                // Now subtract the matched length from this to_i to get the from_i
                // which is the origin.
                dotted_rule_from_i = state_set_k - set->matched_lengths[rule_index_in_core];
            } else
            if (rule_index_in_core < set_core->num_all_matched_lengths)
            {
                // Parent??
                dotted_rule_from_i = state_set_k - set->matched_lengths[set_core->to_parent_rule_index[rule_index_in_core]];
            } else
            {
                dotted_rule_from_i = state_set_k;
            }

            YaepStateSet *check_set = ps->state_sets[dotted_rule_from_i];
            YaepStateSetCore *check_set_core = check_set->core;
            YaepCoreSymbToPredComps *check_core_symb_to_predcomps = core_symb_to_predcomps_find(ps, check_set_core, symb);
            assert(check_core_symb_to_predcomps != NULL);
            bool found = false;
            if (ps->run.debug)
            {
                MemBuffer *mb = new_membuffer();
                membuffer_printf(mb, "trying (s%d,d%d) [%d-%d]  cspc%d check_cspc%d  ",
                                 state_set_k,
                                 dotted_rule->id,
                                 dotted_rule_from_i,
                                 state_set_k,
                                 core_symb_to_predcomps->id,
                                 check_core_symb_to_predcomps->id);
                print_rule(mb, ps, dotted_rule->rule);
                debug_mb("ixml.bt.info=", mb);
                free_membuffer_and_free_content(mb);
            }
            for (int j = 0; j < check_core_symb_to_predcomps->predictions.len; j++)
            {
                int rule_index_in_check_core = check_core_symb_to_predcomps->predictions.ids[j];
                YaepDottedRule *check_dotted_rule = check_set->core->dotted_rules[rule_index_in_check_core];
                if (check_dotted_rule->rule != rule || check_dotted_rule->dot_j != pos_j)
                {
                    continue;
                }
                int check_dotted_rule_from_i = dotted_rule_from_i;
                if (rule_index_in_check_core < check_set_core->num_all_matched_lengths)
                {
                    if (rule_index_in_check_core < check_set_core->num_started_dotted_rules)
                    {
                        check_dotted_rule_from_i = dotted_rule_from_i - check_set->matched_lengths[rule_index_in_check_core];
                    } else
                    {
                        check_dotted_rule_from_i = (dotted_rule_from_i
                                        - check_set->matched_lengths[check_set_core->to_parent_rule_index[rule_index_in_check_core]]);
                    }
                }
                if (check_dotted_rule_from_i == from_i)
                {
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                continue;
            }
            if (n_candidates != 0)
            {
                // Oups, n_candidates is > 0 already, this means that a previous completion matched.
                // We have more than one parse.
                debug("ixml.bt.info=", "n_candidates=%d -> ambiguous=true", n_candidates);
                *ambiguous_p = true;
                if (ps->run.grammar->one_parse_p)
                {
                    break;
                }
            }
            YaepRule *dotted_rule_rule = dotted_rule->rule;
            if (n_candidates == 0)
            {
                orig_state->state_set_k = dotted_rule_from_i;
            }
            if (parent_anode != NULL && rhs_offset >= 0)
            {
                /* We should generate and use the translation of the nonterminal. */
                YaepParseTreeBuildState *curr_state = orig_state;
                anode = orig_state->anode;
                /* We need translation of the rule. */
                if (n_candidates != 0)
                {
                    assert(!ps->run.grammar->one_parse_p);
                    if (n_candidates == 1)
                    {
                        VLO_EXPAND(orig_states, sizeof(YaepParseTreeBuildState*));
                        ((YaepParseTreeBuildState**) VLO_BOUND(orig_states))[-1] = orig_state;
                    }
                    int j = VLO_LENGTH(orig_states) / sizeof(YaepParseTreeBuildState*) - 1;
                    while (j >= 0)
                    {
                        if (((YaepParseTreeBuildState**) VLO_BEGIN(orig_states))[j]->state_set_k == dotted_rule_from_i)
                        {
                            break;
                        }
                        j--;
                    }
                    if (j >= 0)
                    {
                        /* [A -> x., n] & [A -> y., n]*/
                        curr_state = ((YaepParseTreeBuildState**) VLO_BEGIN(orig_states))[j];
                        anode = curr_state->anode;
                    } else
                    {
                        /* [A -> x., n] & [A -> y., m] where n != m.*/
                        /* It is different from the previous ones so add
                         it to process.*/
                        // Push a new state on the stack.
                        state = parse_state_alloc(ps);
                        VLO_EXPAND(stack, sizeof(YaepParseTreeBuildState*));
                        ((YaepParseTreeBuildState**) VLO_BOUND(stack))[-1] = state;
                        *state = *orig_state;
                        state->state_set_k = dotted_rule_from_i;
                        if (anode != NULL)
                        {
                            state->anode = copy_anode(ps, parent_anode->val.anode.children + parent_rhs_offset, anode, rule, rhs_offset);
                        }
                        VLO_EXPAND(orig_states, sizeof(YaepParseTreeBuildState*));
                        ((YaepParseTreeBuildState**) VLO_BOUND(orig_states))[-1] = state;
                        if (ps->run.debug)
                        {
                            MemBuffer *mb = new_membuffer();
                            membuffer_printf(mb, "* (f%d,d%d) add1 modified dotted_rule=", dotted_rule_from_i, state->dotted_rule->id);
                            print_rule_with_dot(mb, ps, state->rule, state->dot_j);
                            membuffer_printf(mb, " state->from_i=%d", state->from_i);
                            debug_mb("ixml.bt.c=", mb);
                            free_membuffer_and_free_content(mb);
                            assert(false);
                        }
                        curr_state = state;
                        anode = state->anode;
                    }
                }
                /* WOOT? if (n_candidates != 0)*/
                if (dotted_rule_rule->anode != NULL)
                {
                    /* This rule creates abstract node. */
                    // Push a new state on the stack.
                    state = parse_state_alloc(ps);
                    VLO_EXPAND(stack, sizeof(YaepParseTreeBuildState*));
                    ((YaepParseTreeBuildState**) VLO_BOUND(stack))[-1] = state;
                    state->rule = dotted_rule_rule;
                    state->dotted_rule = dotted_rule;
                    state->dot_j = dotted_rule->dot_j;
                    state->from_i = dotted_rule_from_i;
                    state->state_set_k = state_set_k;
                    YaepParseTreeBuildState *table_state = NULL;
                    bool new_p;
                    if (!ps->run.grammar->one_parse_p)
                    {
                        table_state = parse_state_insert(ps, state, &new_p);
                    }
                    if (table_state == NULL || new_p)
                    {
                        /* Allocate abtract node. */
                        ps->n_parse_abstract_nodes++;
                        node = ((YaepTreeNode*) (*ps->run.parse_alloc)(sizeof(YaepTreeNode) + sizeof(YaepTreeNode*) * (dotted_rule_rule->trans_len + 1)));
                        node->type = YAEP_ANODE;
                        state->anode = node;
                        if (table_state != NULL)
                        {
                            table_state->anode = node;
                        }
                        if (dotted_rule_rule->caller_anode == NULL)
                        {
                            dotted_rule_rule->caller_anode = ((char*) (*ps->run.parse_alloc)(strlen(dotted_rule_rule->anode) + 1));
                            strcpy(dotted_rule_rule->caller_anode, dotted_rule_rule->anode);
                        }
                        node->val.anode.name = dotted_rule_rule->caller_anode;
                        node->val.anode.cost = dotted_rule_rule->anode_cost;
                        // IXML Copy the rule name -to the generated abstract node.
                        node->val.anode.mark = dotted_rule_rule->mark;
                        if (rule->marks && rule->marks[pos_j])
                        {
                            // But override the mark with the rhs mark!
                            node->val.anode.mark = rule->marks[pos_j];
                        }
                        /////////
                        node->val.anode.children = ((YaepTreeNode**) ((char*) node + sizeof(YaepTreeNode)));
                        for (int k = 0; k <= dotted_rule_rule->trans_len; k++)
                        {
                            node->val.anode.children[k] = NULL;
                        }
                        if (anode == NULL)
                        {
                            state->parent_anode_state = curr_state->parent_anode_state;
                            state->parent_rhs_offset = parent_rhs_offset;
                        } else
                        {
                            state->parent_anode_state = curr_state;
                            state->parent_rhs_offset = rhs_offset;
                        }
                        if (ps->run.debug)
                        {
                            MemBuffer *mb = new_membuffer();
                            membuffer_printf(mb, "adding (d%d,%d-%d) ",
                                             state->dotted_rule->id,
                                             state->from_i, state->state_set_k);
                            print_rule(mb, ps, dotted_rule->rule);
                            debug_mb("ixml.bt.step=", mb);
                            free_membuffer_and_free_content(mb);
                        }
                    } else
                    {
                        /* We allready have the translation.*/
                        assert(!ps->run.grammar->one_parse_p);
                        parse_state_free(ps, state);
                        state = ((YaepParseTreeBuildState**) VLO_BOUND(stack))[-1];
                        node = table_state->anode;
                        assert(node != NULL);
                        if (ps->run.debug)
                        {
                            MemBuffer *mb = new_membuffer();
                            membuffer_printf(mb, "* found prev. translation: state_set_k = %d, dotted_rule = ", state_set_k);
                            print_dotted_rule(mb, ps, -1, dotted_rule, -1, -1);
                            membuffer_printf(mb, ", %d\n", dotted_rule_from_i);
                            debug_mb("ixml.bt.info=", mb);
                            free_membuffer_and_free_content(mb);
                            // assert(false);
                        }
                    }
                    YaepTreeNode **placement = NULL;
                    if (anode)
                    {
                        placement = anode->val.anode.children + rhs_offset;
                    } else
                    {
                        placement = parent_anode->val.anode.children + parent_rhs_offset;
                    }
                    place_translation(ps, placement, node);
                } /* if (dotted_rule_rule->anode != NULL)*/else
                if (dotted_rule->dot_j != 0)
                {
                    /* We should generate and use the translation of the
                     nonterminal.  Add state to get a translation.*/
                    // Push a new state on the stack
                    state = parse_state_alloc(ps);
                    VLO_EXPAND(stack, sizeof(YaepParseTreeBuildState*));
                    ((YaepParseTreeBuildState**) VLO_BOUND(stack))[-1] = state;
                    state->rule = dotted_rule_rule;
                    state->dotted_rule = dotted_rule;
                    state->dot_j = dotted_rule->dot_j;
                    state->from_i = dotted_rule_from_i;
                    state->state_set_k = state_set_k;
                    state->parent_anode_state = (anode == NULL ? curr_state->parent_anode_state : curr_state);
                    assert(state->parent_anode_state);
                    state->parent_rhs_offset = anode == NULL ? parent_rhs_offset : rhs_offset;
                    state->anode = NULL;
                    if (ps->run.debug)
                    {
                        MemBuffer *mb = new_membuffer();
                        membuffer_printf(mb, "* add3   state_set_k=%d   dotted_rule_from_i=%d    ", state_set_k, dotted_rule_from_i);
                        print_rule(mb, ps, dotted_rule->rule);
                        debug_mb("ixml.bt.info=", mb);
                        free_membuffer_and_free_content(mb);
                        //assert(false);
                    }
                } else
                {
                    /* Empty rule should dotted_ruleuce something not abtract
                     node.  So place empty node.*/
                    place_translation(ps, anode == NULL ? parent_anode->val.anode.children + parent_rhs_offset : anode->val.anode.children + rhs_offset,
                                    empty_node);
                    empty_node->val.nil.used = 1;
                }
            }
            /* if (parent_anode != NULL && rhs_offset >= 0)*/                // Continue with next completion.
            n_candidates++;
        }
        /* For all completions of the nonterminal.*//* We should have a parse.*/
        assert(n_candidates != 0 && (!ps->run.grammar->one_parse_p || n_candidates == 1));
    }
    /* For all parser states.*/

    VLO_DELETE(stack);

    if (!ps->run.grammar->one_parse_p)
    {
        VLO_DELETE(orig_states);
        yaep_free(ps->run.grammar->alloc, term_node_array);
    }
}

/* The hash of the memory reference. */
static unsigned reserv_mem_hash(hash_table_entry_t m)
{
    return (size_t)m;
}

/* The equity of the memory reference. */
static bool reserv_mem_eq(hash_table_entry_t m1, hash_table_entry_t m2)
{
    return m1 == m2;
}

/* The following function sets up minimal cost for each abstract node.
   The function returns minimal translation corresponding to NODE.
   The function also collects references to memory which can be
   freed. Remeber that the translation is DAG, altenatives form lists
   (alt node may not refer for another alternative). */
static YaepTreeNode *prune_to_minimal(YaepParseState *ps, YaepTreeNode *node, int *cost)
{
    YaepTreeNode*child,*alt,*next_alt,*result = NULL;
    int i, min_cost = INT_MAX;

    assert(node != NULL);
    switch(node->type)
    {
    case YAEP_NIL:
    case YAEP_ERROR:
    case YAEP_TERM:
        if (ps->run.parse_free != NULL)
        {
            VLO_ADD_MEMORY(ps->tnodes_vlo, &node, sizeof(node));
        }
       *cost = 0;
        return node;
    case YAEP_ANODE:
        if (node->val.anode.cost >= 0)
        {
            if (ps->run.parse_free != NULL)
            {
                VLO_ADD_MEMORY(ps->tnodes_vlo, &node, sizeof(node));
            }
            for (i = 0; (child = node->val.anode.children[i]) != NULL; i++)
            {
                node->val.anode.children[i] = prune_to_minimal(ps, child, cost);
                if (node->val.anode.children[i] != child)
                {
                    //fprintf(stderr, "PRUNEDDDDDDD\n");
                }
                node->val.anode.cost += *cost;
            }
           *cost = node->val.anode.cost;
            node->val.anode.cost = -node->val.anode.cost - 1;        /* flag of visit*/
        }
        return node;
    case YAEP_ALT:
        for(alt = node; alt != NULL; alt = next_alt)
        {
            if (ps->run.parse_free != NULL)
            {
                VLO_ADD_MEMORY(ps->tnodes_vlo, &alt, sizeof(alt));
            }
            next_alt = alt->val.alt.next;
            alt->val.alt.node = prune_to_minimal(ps, alt->val.alt.node, cost);
            if (alt == node || min_cost > *cost)
            {
                if (ps->run.debug)
                {
                    fprintf(stderr, "FOUND smaller cost %d %s\n", *cost, alt->val.alt.node->val.anode.name);
                }
                min_cost = *cost;
                alt->val.alt.next = NULL;
                result = alt;
            }
            else if (min_cost ==*cost && !ps->run.grammar->one_parse_p)
            {
                alt->val.alt.next = result;
                result = alt;
            }
        }
       *cost = min_cost;
        return(result->val.alt.next == NULL ? result->val.alt.node : result);
    default:
        assert(false);
    }
   *cost = 0;
    return NULL;
}

/* The following function traverses the translation collecting
   reference to memory which may not be freed.*/
static void traverse_pruned_translation(YaepParseState *ps, YaepTreeNode *node)
{
    YaepTreeNode*child;
    hash_table_entry_t*entry;
    int i;

next:
    assert(node != NULL);
    if (ps->run.parse_free != NULL && *(entry = find_hash_table_entry(ps->set_of_reserved_memory, node, true)) == NULL)
    {
       *entry = (hash_table_entry_t)node;
    }
    switch(node->type)
    {
    case YAEP_NIL:
    case YAEP_ERROR:
    case YAEP_TERM:
        break;
    case YAEP_ANODE:
        if (ps->run.parse_free != NULL && *(entry = find_hash_table_entry(ps->set_of_reserved_memory,
                                                                      node->val.anode.name,
                                                                      true)) == NULL)
        {
            *entry =(hash_table_entry_t) node->val.anode.name;
        }
        for(i = 0;(child = node->val.anode.children[i]) != NULL; i++)
        {
            traverse_pruned_translation(ps, child);
        }
        // FIXME Is this assert needed? What is its purpose?
        // assert(node->val.anode.cost < 0);
        node->val.anode.cost = -node->val.anode.cost - 1;
        break;
    case YAEP_ALT:
        traverse_pruned_translation(ps, node->val.alt.node);
        if ((node = node->val.alt.next) != NULL)
            goto next;
        break;
    default:
        assert(false);
    }

    return;
}

/* The function finds and returns a minimal cost parse(s). */
static YaepTreeNode *find_minimal_translation(YaepParseState *ps, YaepTreeNode *root)
{
    YaepTreeNode**node_ptr;
    int cost;

    if (ps->run.parse_free != NULL)
    {
        ps->set_of_reserved_memory = create_hash_table(ps->run.grammar->alloc, ps->input_len* 4,
                                                       (hash_table_hash_function)reserv_mem_hash,
                                                       (hash_table_eq_function)reserv_mem_eq);

        VLO_CREATE(ps->tnodes_vlo, ps->run.grammar->alloc, ps->input_len* 4* sizeof(void*));
    }
    root = prune_to_minimal(ps, root, &cost);

    traverse_pruned_translation(ps, root);

    if (ps->run.parse_free != NULL)
    {
        for(node_ptr = (YaepTreeNode**)VLO_BEGIN(ps->tnodes_vlo);
            node_ptr <(YaepTreeNode**)VLO_BOUND(ps->tnodes_vlo);
            node_ptr++)
        {
            if (*find_hash_table_entry(ps->set_of_reserved_memory,*node_ptr, true) == NULL)
            {
                if ((*node_ptr)->type == YAEP_ANODE
                    &&*find_hash_table_entry(ps->set_of_reserved_memory,
                                             (*node_ptr)->val.anode.name,
                                             true) == NULL)
                {
                    // (*ps->run.parse_free)((void*)(*node_ptr)->val.anode.name);
                }
                //(*ps->run.parse_free)(*node_ptr);
            }
        }
        VLO_DELETE(ps->tnodes_vlo);
        delete_hash_table(ps->set_of_reserved_memory);
    }

    return root;
}

YaepTreeNode *build_parse_tree(YaepParseState *ps, bool *ambiguous_p)
{
    yaep_debug(ps, "build_parse_tree()");

    // Number of candiate trees found.
    int n_candidates = 0;

    // The result pointer points to the final parse tree.
    YaepTreeNode *result;

    ps->n_parse_term_nodes = ps->n_parse_abstract_nodes = ps->n_parse_alt_nodes = 0;

    // Pick the final state set, where we completed the axiom $.
    YaepStateSet *set = ps->state_sets[ps->state_set_k];
    assert(ps->run.grammar->axiom != NULL);

    /* We have only one start dotted_rule: "$ : <start symb> eof .". */
    YaepDottedRule *dotted_rule = (set->core->dotted_rules != NULL ? set->core->dotted_rules[0] : NULL);

    if (dotted_rule == NULL
        || set->matched_lengths[0] != ps->state_set_k
        || dotted_rule->rule->lhs != ps->run.grammar->axiom || dotted_rule->dot_j != dotted_rule->rule->rhs_len)
    {
        /* It is possible only if error recovery is switched off.
           Because we always adds rule `axiom: error $eof'.*/
        assert(!ps->run.grammar->error_recovery_p);
        return NULL;
    }
    bool saved_one_parse_p = ps->run.grammar->one_parse_p;
    if (ps->run.grammar->cost_p)
    {
        /* We need all parses to choose the minimal one. */
        ps->run.grammar->one_parse_p = false;
    }

    parse_state_init(ps);

    // The result tree starts empty.
    result = NULL;

    /* Create empty and error node:*/
    YaepTreeNode *empty_node = ((YaepTreeNode*)(*ps->run.parse_alloc)(sizeof(YaepTreeNode)));
    empty_node->type = YAEP_NIL;
    empty_node->val.nil.used = 0;

    YaepTreeNode *error_node = ((YaepTreeNode*)(*ps->run.parse_alloc)(sizeof(YaepTreeNode)));
    error_node->type = YAEP_ERROR;
    error_node->val.error.used = 0;

    verbose("ixml=", "building tree");

    loop_stack(&result, n_candidates, ps, empty_node, error_node, set, dotted_rule, ambiguous_p);

    free_parse_state(ps);
    ps->run.grammar->one_parse_p = saved_one_parse_p;
    if (ps->run.grammar->cost_p && *ambiguous_p)
    {
        /* We can not build minimal tree during building parsing list
           because we have not the translation yet. We can not make it
           during parsing because the abstract nodes are created before
           their children. */
        result = find_minimal_translation(ps, result);
    }
    if (false)
    {
        fprintf(stderr, "(ixml) yaep parse tree: %p\n", result);
        print_parse(ps, stderr, result);
        fprintf(stderr, "\n");
    }
    if (false)
    {
        // Graphviz
        fprintf(stderr, "digraph CFG {\n");
        fprintf(stderr, "  node [shape=ellipse, fontsize=200];\n");
        fprintf(stderr, "  ratio=fill;\n");
        fprintf(stderr, "  ordering=out;\n");
        fprintf(stderr, "  page = \"8.5, 11\"; // inches\n");
        fprintf(stderr, "  size = \"7.5, 10\"; // inches\n\n");
        print_parse(ps, stderr, result);
        fprintf(stderr, "}\n");
    }


    /* Free empty and error node if they have not been used*/
    if (ps->run.parse_free != NULL)
    {
        if (!empty_node->val.nil.used)
        {
            ps->run.parse_free(empty_node);
        }
        if (!error_node->val.error.used)
        {
            ps->run.parse_free(error_node);
        }
    }

    assert(result != NULL && (!ps->run.grammar->one_parse_p || ps->n_parse_alt_nodes == 0));

    return result;
}

/* Hash of translation visit node.*/
unsigned trans_visit_node_hash(hash_table_entry_t n)
{
    return(size_t)((YaepTreeNodeVisit*) n)->node;
}

/* Equality of translation visit nodes.*/
bool trans_visit_node_eq(hash_table_entry_t n1, hash_table_entry_t n2)
{
    return(((YaepTreeNodeVisit*) n1)->node == ((YaepTreeNodeVisit*) n2)->node);
}

/* The following function returns the positive order number of node with number NUM.*/
int canon_node_id(int id)
{
    return (id < 0 ? -id-1 : id);
}

/* The following function checks presence translation visit node with
   given NODE in the table and if it is not present in the table, the
   function creates the translation visit node and inserts it into
   the table.*/
YaepTreeNodeVisit *visit_node(YaepParseState *ps, YaepTreeNode*node)
{
    YaepTreeNodeVisit trans_visit_node;
    hash_table_entry_t*entry;

    trans_visit_node.node = node;
    entry = find_hash_table_entry(ps->map_node_to_visit,
                                   &trans_visit_node, true);

    if (*entry == NULL)
    {
        /* If it is the new node, we did not visit it yet.*/
        trans_visit_node.num = -1 - ps->num_nodes_visits;
        ps->num_nodes_visits++;
        OS_TOP_ADD_MEMORY(ps->node_visits_os,
                           &trans_visit_node, sizeof(trans_visit_node));
       *entry =(hash_table_entry_t) OS_TOP_BEGIN(ps->node_visits_os);
        OS_TOP_FINISH(ps->node_visits_os);
    }
    return(YaepTreeNodeVisit*)*entry;
}

#endif

// PART C YAEP_PRINT_C ////////////////////////////////////////

#ifdef YAEP_PRINT_MODULE

void print_core(MemBuffer*mb, YaepStateSetCore *c)
{
    membuffer_printf(mb, "c%d{", c->id);

    for (int i = 0; i < c->num_started_dotted_rules; ++i)
    {
        if (i > 0) membuffer_append_char(mb, ' ');

        // num_started_dotted_rules;
        YaepDottedRule *dotted_rule = c->dotted_rules[i];
        membuffer_printf(mb, "d%d", dotted_rule->id);
    }
    membuffer_printf(mb, "}");
}

void print_coresymbvects(MemBuffer*mb, YaepParseState *ps, YaepCoreSymbToPredComps *v)
{
    membuffer_printf(mb, "coresymbvect %d %s preds: ", v->core->id, v->symb->hr);
    for (int i = 0; i < v->predictions.len; ++i)
    {
        int dotted_rule_id = v->predictions.ids[i];
        YaepDottedRule *dotted_rule = v->core->dotted_rules[dotted_rule_id];
        membuffer_printf(mb, " (%d)%s", dotted_rule_id, dotted_rule->rule->lhs->hr);
    }
    membuffer_printf(mb, " comps:");
    for (int i = 0; i < v->completions.len; ++i)
    {
        int dotted_rule_id = v->completions.ids[i];
        YaepDottedRule *dotted_rule = v->core->dotted_rules[dotted_rule_id];
        membuffer_printf(mb, " (%d)%s", dotted_rule_id, dotted_rule->rule->lhs->hr);
    }
}

/* The following function prints RULE with its translation(if TRANS_P) to file F.*/
void rule_print(MemBuffer *mb, YaepParseState *ps, YaepRule *rule, bool trans_p)
{
    int i;

    if (rule->mark != 0
        && rule->mark != ' '
        && rule->mark != '-'
        && rule->mark != '@'
        && rule->mark != '^'
        && rule->mark != '*')
    {
        membuffer_append(mb, "\n(yaep) internal error bad rule: ");
        print_symbol(mb, rule->lhs, false);
        debug_mb("ixml=", mb);
        free_membuffer_and_free_content(mb);
        assert(false);
    }

    char m = rule->mark;
    if (m >= 32 && m < 127)
    {
        membuffer_append_char(mb, m);
    }
    print_symbol(mb, rule->lhs, false);
    if (strcmp(rule->lhs->repr, rule->anode))
    {
        membuffer_append_char(mb, '(');
        membuffer_append(mb, rule->anode);
        membuffer_append_char(mb, ')');
    }
    membuffer_append(mb, " → ");
    int cost = rule->anode_cost;
    while (cost > 0)
    {
        membuffer_append_char(mb, '<');
        cost--;
    }
    for (i = 0; i < rule->rhs_len; i++)
    {
        char m = rule->marks[i];
        if (m >= 32 && m < 127)
        {
            membuffer_append_char(mb, m);
        }
        else
        {
            if (!m) membuffer_append(mb, "  ");
            else    assert(false);
        }
        print_symbol(mb, rule->rhs[i], false);
    }
    /*
      if (false && trans_p)
      {
      fprintf(f, "      | ");
      if (rule->anode != NULL)
      {
      fprintf(f, "%s(", rule->anode);
      }
      for(i = 0; i < rule->trans_len; i++)
      {
      for(j = 0; j < rule->rhs_len; j++)
      {
      if (rule->order[j] == i)
      {
      fprintf(f, " %d:", j);
      // TODO symbol_print(f, rule->rhs[j], false);
      break;
      }
      }
      if (j >= rule->rhs_len)
      {
      fprintf(f, " nil");
      }
      }
      if (rule->anode != NULL)
      {
      fprintf(f, " )");
      }
    */
}

/* The following function prints RULE to file F with dot in position pos.
   Pos == 0 means that the dot is all the way to the left in the starting position.
   Pos == rhs_len means that the whole rule has been matched. */
void print_rule_with_dot(MemBuffer *mb, YaepParseState *ps, YaepRule *rule, int dot_j)
{
    assert(dot_j >= 0 && dot_j <= rule->rhs_len);

    print_symbol(mb, rule->lhs, false);
    membuffer_append(mb, " → ");
    for(int j = 0; j < rule->rhs_len; j++)
    {
        membuffer_append(mb, j == dot_j ? " · " : " ");
        print_symbol(mb, rule->rhs[j], false);
    }

    /*
    if (rule->rhs_len == dot_j && rule->rhs_len == 0)
    {
        membuffer_append(mb, " ε");
        }*/
}

void print_rule(MemBuffer *mb, YaepParseState *ps, YaepRule *rule)
{
    int i;

    print_symbol(mb, rule->lhs, false);
    membuffer_append(mb, " → ");
    for(i = 0; i < rule->rhs_len; i++)
    {
        membuffer_append_char(mb, ' ');
        print_symbol(mb, rule->rhs[i], false);
    }
}

/* The following function prints the dotted_rule.
   Print the lookahead set if lookahead_p is true. */
void print_dotted_rule(MemBuffer *mb,
                       YaepParseState *ps,
                       int tok_i, // aka state_set_k
                       YaepDottedRule *dotted_rule,
                       int matched_length,
                       int parent_id)
{
    char buf[256];

    int from = 0;
    int to = 0;

    assert(matched_length >= 0);

    if (matched_length == 0)
    {
        from = tok_i;
        to = tok_i;
    }
    else
    {
        from = 1+tok_i-matched_length;
        to = 1+tok_i;
    }

    snprintf(buf, 256, "(d%d,%d-%d) ", dotted_rule->id, from, to);
    membuffer_append(mb, buf);

    if (matched_length == 0)
    {
        if (dotted_rule->rule->rhs_len == dotted_rule->dot_j)
        {
            snprintf(buf, 256, "full/%d ", ps->input_len);
        }
        else
        {
            snprintf(buf, 256, "none/%d ", ps->input_len);
        }
    }
    else if (matched_length > 0)
    {
        if (dotted_rule->rule->rhs_len == dotted_rule->dot_j)
        {
            snprintf(buf, 256, "full/%d ", ps->input_len);
        }
        else
        {
            snprintf(buf, 256, "part/%d ", ps->input_len);
        }
    }

    membuffer_append(mb, buf);

    print_rule_with_dot(mb, ps, dotted_rule->rule, dotted_rule->dot_j);
}

/* The following function prints the dotted_rule.
   Print the lookahead set if lookahead_p is true. */
void print_dotted_rule_blocked(MemBuffer *mb,
                               YaepParseState *ps,
                               int tok_i, // aka state_set_k
                               YaepDottedRule *dotted_rule,
                               int matched_length,
                               int parent_id)
{
    char buf[256];

    int from = 0;
    int to = 0;

    assert(matched_length >= 0);

    if (matched_length == 0)
    {
        from = tok_i;
        to = tok_i;
    }
    else
    {
        from = 1+tok_i-matched_length;
        to = 1+tok_i;
    }

    snprintf(buf, 256, "(d%d,%d-%d) ", dotted_rule->id, from, to);
    membuffer_append(mb, buf);

    snprintf(buf, 256, "block/%d ", ps->input_len);
    membuffer_append(mb, buf);

    print_rule_with_dot(mb, ps, dotted_rule->rule, dotted_rule->dot_j);
}

void print_matched_lenghts(MemBuffer *mb, YaepStateSet *s)
{
    for (int i = 0; i < s->core->num_started_dotted_rules; ++i)
    {
        if (i > 0) membuffer_append_char(mb, ' ');
        membuffer_printf(mb, "%d(d%d)", s->matched_lengths[i], s->core->dotted_rules[i]->id);
    }
}

/* The following recursive function prints NODE into file F and prints
   all its children(if debug_level < 0 output format is for graphviz).*/
void print_yaep_node(YaepParseState *ps, FILE *f, YaepTreeNode *node)
{
    YaepTreeNodeVisit*trans_visit_node;
    YaepTreeNode*child;
    int i;

    assert(node != NULL);
    trans_visit_node = visit_node(ps, node);
    if (trans_visit_node->num >= 0)
    {
        return;
    }
    trans_visit_node->num = -trans_visit_node->num - 1;
    if (ps->run.debug) fprintf(f, "%7d: ", trans_visit_node->num);
    switch(node->type)
    {
    case YAEP_NIL:
        if (ps->run.debug)
            fprintf(f, "EMPTY\n");
        break;
    case YAEP_ERROR:
        if (ps->run.debug > 0)
            fprintf(f, "ERROR\n");
        break;
    case YAEP_TERM:
        if (ps->run.debug)
            fprintf(f, "TERMINAL: code=%d, repr=%s, mark=%d %c\n", node->val.terminal.code,
                    symb_find_by_code(ps, node->val.terminal.code)->repr, node->val.terminal.mark, node->val.terminal.mark>32?node->val.terminal.mark:' ');
        break;
    case YAEP_ANODE:
        if (ps->run.debug)
        {
            fprintf(f, "ABSTRACT: %c%s(", node->val.anode.mark?node->val.anode.mark:' ', node->val.anode.name);
            for(i = 0;(child = node->val.anode.children[i]) != NULL; i++)
            {
                fprintf(f, " %d", canon_node_id(visit_node(ps, child)->num));
            }
            fprintf(f, ")\n");
        }
        else
        {
            for(i = 0;(child = node->val.anode.children[i]) != NULL; i++)
            {
                fprintf(f, "  \"%d: %s\" -> \"%d: ", trans_visit_node->num,
                         node->val.anode.name,
                        canon_node_id(visit_node(ps, child)->num));
                switch(child->type)
                {
                case YAEP_NIL:
                    fprintf(f, "EMPTY");
                    break;
                case YAEP_ERROR:
                    fprintf(f, "ERROR");
                    break;
                case YAEP_TERM:
                    fprintf(f, "%s",
                            symb_find_by_code(ps, child->val.terminal.code)->repr);
                    break;
                case YAEP_ANODE:
                    fprintf(f, "%s", child->val.anode.name);
                    break;
                case YAEP_ALT:
                    fprintf(f, "ALT");
                    break;
                default:
                    assert(false);
                }
                fprintf(f, "\";\n");
            }
        }
        for (i = 0;(child = node->val.anode.children[i]) != NULL; i++)
        {
            print_yaep_node(ps, f, child);
        }
        break;
    case YAEP_ALT:
        if (ps->run.debug)
        {
            fprintf(f, "ALTERNATIVE: node=%d, next=",
                    canon_node_id(visit_node(ps, node->val.alt.node)->num));
            if (node->val.alt.next != NULL)
                fprintf(f, "%d\n",
                        canon_node_id(visit_node(ps, node->val.alt.next)->num));
            else
                fprintf(f, "nil\n");
        }
        else
        {
            fprintf(f, "  \"%d: ALT\" -> \"%d: ", trans_visit_node->num,
                    canon_node_id(visit_node(ps, node->val.alt.node)->num));
            switch(node->val.alt.node->type)
            {
            case YAEP_NIL:
                fprintf(f, "EMPTY");
                break;
            case YAEP_ERROR:
                fprintf(f, "ERROR");
                break;
            case YAEP_TERM:
                fprintf(f, "%s",
                        symb_find_by_code(ps, node->val.alt.node->val.terminal.code)->
                         repr);
                break;
            case YAEP_ANODE:
                fprintf(f, "%s", node->val.alt.node->val.anode.name);
                break;
            case YAEP_ALT:
                fprintf(f, "ALT");
                break;
            default:
                assert(false);
            }
            fprintf(f, "\";\n");
            if (node->val.alt.next != NULL)
            {
                fprintf(f, "  \"%d: ALT\" -> \"%d: ALT\";\n",
                        trans_visit_node->num,
                        canon_node_id(visit_node(ps, node->val.alt.next)->num));
            }
        }
        print_yaep_node(ps, f, node->val.alt.node);
        if (node->val.alt.next != NULL)
        {
            print_yaep_node(ps, f, node->val.alt.next);
        }
        break;
    default:
        assert(false);
    }
}

/* The following function prints parse tree with ROOT.*/
void print_parse(YaepParseState *ps, FILE* f, YaepTreeNode*root)
{
    ps->map_node_to_visit = create_hash_table(ps->run.grammar->alloc,
                                              ps->input_len* 2,
                                              trans_visit_node_hash,
                                              trans_visit_node_eq);

    ps->num_nodes_visits = 0;
    OS_CREATE(ps->node_visits_os, ps->run.grammar->alloc, 0);
    print_yaep_node(ps, f, root);
    OS_DELETE(ps->node_visits_os);
    delete_hash_table(ps->map_node_to_visit);
}

/* The following function prints SET to file F.  If NOT_YET_STARTED_P is true
   then print all dotted_rules.  The dotted_rules are printed with the
   lookahead set if LOOKAHEAD_P.  SET_DIST is used to print absolute
   matched_lengths of not-yet-started dotted_rules. */
void print_state_set(MemBuffer *mb,
                     YaepParseState *ps,
                     YaepStateSet *state_set,
                     int from_i)
{
    StateVars vars;
    fetch_state_vars(ps, state_set, &vars);

    membuffer_printf(mb, "@%d summary s%dc%d", ps->tok_i, vars.state_id, vars.core_id);

    int dotted_rule_id = 0;
    for(; dotted_rule_id < vars.num_dotted_rules; dotted_rule_id++)
    {
        int matched_length = find_matched_length(ps, state_set, &vars, dotted_rule_id);
        membuffer_append(mb, "\n");
        print_dotted_rule(mb, ps, from_i, vars.dotted_rules[dotted_rule_id], matched_length, -1);
    }
}

/* The following function prints symbol SYMB to file F.  Terminal is
   printed with its code if CODE_P. */
void print_symbol(MemBuffer *mb, YaepSymbol *symb, bool code_p)
{
    if (symb->is_terminal)
    {
        membuffer_append(mb, symb->hr);
        return;
    }
    membuffer_append(mb, symb->repr);
    if (code_p && symb->is_terminal)
    {
        membuffer_printf(mb, "(%d)", symb->u.terminal.code);
    }
}

void print_terminal_bitset(MemBuffer *mb, YaepParseState *ps, terminal_bitset_t *set)
{
    bool first = true;
    int num_set = 0;
    int num_terminals = ps->run.grammar->symbs_ptr->num_terminals;
    for (int i = 0; i < num_terminals; i++) num_set += terminal_bitset_test(ps, set, i);

    if (num_set > num_terminals/2)
    {
        // Print the negation
        membuffer_append(mb, "~[");
        for (int i = 0; i < num_terminals; i++)
        {
            if (!terminal_bitset_test(ps, set, i))
            {
                if (!first) membuffer_append(mb, " "); else first = false;
                print_symbol(mb, term_get(ps, i), false);
            }
        }
        membuffer_append_char(mb, ']');
    }

    membuffer_append_char(mb, '[');
    for (int i = 0; i < num_terminals; i++)
    {
        if (terminal_bitset_test(ps, set, i))
        {
            if (!first) membuffer_append(mb, " "); else first = false;
            print_symbol(mb, term_get(ps, i), false);
        }
    }
    membuffer_append_char(mb, ']');
}

#endif

// PART C YAEP_C ////////////////////////////////////////

#ifdef YAEP_MODULE

// Declarations ///////////////////////////////////////////////////

static bool blocked_by_lookahead(YaepParseState *ps, YaepDottedRule *dotted_rule, YaepSymbol *symb, int n, const char *info);
static bool core_has_not_rules(YaepStateSetCore *core);
static void check_predicted_dotted_rules(YaepParseState *ps, YaepStateSet *set, YaepVect *predictions, int lookahead_term_id, int local_lookahead_level);
static void check_leading_dotted_rules(YaepParseState *ps, YaepStateSet *set, int lookahead_term_id, int local_lookahead_level);
static bool has_lookahead(YaepParseState *ps, YaepSymbol *symb, int n);
static int default_read_token(YaepParseRun *ps, void **attr);
static void error_recovery(YaepParseState *ps, int *start, int *stop);
static void error_recovery_init(YaepParseState *ps);
static void free_error_recovery(YaepParseState *ps);
static size_t memusage(YaepParseState *ps);
static void read_input(YaepParseState *ps);
static void set_add_dotted_rule_with_matched_length(YaepParseState *ps, YaepDottedRule *dotted_rule, int matched_length, const char *why);
static void set_add_dotted_rule_no_match_yet(YaepParseState *ps, YaepDottedRule *dotted_rule, const char *why);
static void set_add_dotted_rule_with_parent(YaepParseState *ps, YaepDottedRule *dotted_rule, int parent_dotted_rule_id, const char *why);
static bool convert_leading_dotted_rules_into_new_set(YaepParseState *ps);
static void prepare_for_leading_dotted_rules(YaepParseState *ps);
static void verbose_stats(YaepParseState *ps);
static void yaep_error(YaepParseState *ps, int code, const char*format, ...);

// Implementations ////////////////////////////////////////////////////////////////////

/* Initialize work with rules and returns pointer to rules storage. */
static YaepRuleStorage *rulestorage_create(YaepGrammar *grammar)
{
    void *mem;
    YaepRuleStorage *result;

    mem = yaep_malloc(grammar->alloc, sizeof(YaepRuleStorage));
    result = (YaepRuleStorage*)mem;
    OS_CREATE(result->rules_os, grammar->alloc, 0);
    result->first_rule = result->current_rule = NULL;
    result->num_rules = result->n_rhs_lens = 0;

    return result;
}

/* Create new rule with LHS empty rhs. */
static YaepRule *rule_new_start(YaepParseState *ps, YaepSymbol *lhs, const char *anode, int anode_cost)
{
    YaepRule *rule;
    YaepSymbol *empty;

    assert(!lhs->is_terminal);

    OS_TOP_EXPAND(ps->run.grammar->rulestorage_ptr->rules_os, sizeof(YaepRule));
    rule =(YaepRule*) OS_TOP_BEGIN(ps->run.grammar->rulestorage_ptr->rules_os);
    OS_TOP_FINISH(ps->run.grammar->rulestorage_ptr->rules_os);
    rule->lhs = lhs;
    rule->mark = 0;
    rule->contains_not_operator = false;
    if (anode == NULL)
    {
        rule->anode = NULL;
        rule->anode_cost = 0;
    }
    else
    {
        OS_TOP_ADD_STRING(ps->run.grammar->rulestorage_ptr->rules_os, anode);
        rule->anode =(char*) OS_TOP_BEGIN(ps->run.grammar->rulestorage_ptr->rules_os);
        OS_TOP_FINISH(ps->run.grammar->rulestorage_ptr->rules_os);
        rule->anode_cost = anode_cost;
    }
    rule->trans_len = 0;
    rule->mark = 0;
    rule->marks = NULL;
    rule->order = NULL;
    rule->next = NULL;
    if (ps->run.grammar->rulestorage_ptr->current_rule != NULL)
    {
        ps->run.grammar->rulestorage_ptr->current_rule->next = rule;
    }
    rule->lhs_next = lhs->u.nonterminal.rules;
    lhs->u.nonterminal.rules = rule;
    rule->rhs_len = 0;
    empty = NULL;
    OS_TOP_ADD_MEMORY(ps->run.grammar->rulestorage_ptr->rules_os, &empty, sizeof(YaepSymbol*));
    rule->rhs =(YaepSymbol**) OS_TOP_BEGIN(ps->run.grammar->rulestorage_ptr->rules_os);
    ps->run.grammar->rulestorage_ptr->current_rule = rule;
    if (ps->run.grammar->rulestorage_ptr->first_rule == NULL)
    {
        ps->run.grammar->rulestorage_ptr->first_rule = rule;
    }
    rule->rule_start_offset = ps->run.grammar->rulestorage_ptr->n_rhs_lens + ps->run.grammar->rulestorage_ptr->num_rules;
    rule->num = ps->run.grammar->rulestorage_ptr->num_rules++;

    return rule;
}

/* Add SYMB at the end of current rule rhs. */
static void rule_new_symb_add(YaepParseState *ps, YaepSymbol *symb)
{
    YaepSymbol *ignore = NULL;

    OS_TOP_ADD_MEMORY(ps->run.grammar->rulestorage_ptr->rules_os, &ignore, sizeof(YaepSymbol*));

    YaepRule *r = ps->run.grammar->rulestorage_ptr->current_rule;
    r->rhs = (YaepSymbol**)OS_TOP_BEGIN(ps->run.grammar->rulestorage_ptr->rules_os);
    r->rhs[ps->run.grammar->rulestorage_ptr->current_rule->rhs_len] = symb;
    r->rhs_len++;
    r->contains_not_operator |= symb->is_not_operator;
    ps->run.grammar->rulestorage_ptr->n_rhs_lens++;
}

/* The function should be called at end of forming each rule.  It
   creates and initializes dotted_rule cache.*/
static void rule_new_stop(YaepParseState *ps)
{
    int i;

    OS_TOP_FINISH(ps->run.grammar->rulestorage_ptr->rules_os);
    OS_TOP_EXPAND(ps->run.grammar->rulestorage_ptr->rules_os, ps->run.grammar->rulestorage_ptr->current_rule->rhs_len* sizeof(int));
    ps->run.grammar->rulestorage_ptr->current_rule->order = (int*)OS_TOP_BEGIN(ps->run.grammar->rulestorage_ptr->rules_os);
    OS_TOP_FINISH(ps->run.grammar->rulestorage_ptr->rules_os);
    for(i = 0; i < ps->run.grammar->rulestorage_ptr->current_rule->rhs_len; i++)
    {
        ps->run.grammar->rulestorage_ptr->current_rule->order[i] = -1;
    }

    OS_TOP_EXPAND(ps->run.grammar->rulestorage_ptr->rules_os, ps->run.grammar->rulestorage_ptr->current_rule->rhs_len* sizeof(char));
    ps->run.grammar->rulestorage_ptr->current_rule->marks = (char*)OS_TOP_BEGIN(ps->run.grammar->rulestorage_ptr->rules_os);
    memset(ps->run.grammar->rulestorage_ptr->current_rule->marks, 0, ps->run.grammar->rulestorage_ptr->current_rule->rhs_len* sizeof(char));
    OS_TOP_FINISH(ps->run.grammar->rulestorage_ptr->rules_os);

    /*
    if (ps->run.grammar->rulestorage_ptr->current_rule->contains_not_operator)
        fprintf(stderr, "NOT inside %s\n", ps->run.grammar->rulestorage_ptr->current_rule->lhs->hr);
    */
}

/* The following function frees memory for rules.*/
static void rulestorage_clear(YaepRuleStorage *rules)
{
    if (rules == NULL) return;

    OS_EMPTY(rules->rules_os);
    rules->first_rule = rules->current_rule = NULL;
    rules->num_rules = rules->n_rhs_lens = 0;
}

static void rulestorage_free(YaepGrammar *grammar, YaepRuleStorage *rules)
{
    if (rules == NULL) return;

    OS_DELETE(rules->rules_os);
    yaep_free(grammar->alloc, rules);
    rules = NULL;
}

static void create_input(YaepParseState *ps)
{
    VLO_CREATE(ps->input_vlo, ps->run.grammar->alloc, NUM_INITIAL_YAEP_TOKENS * sizeof(YaepInputToken));
    ps->input_len = 0;
}

/* Add input token with CODE and attribute at the end of input tokens array.*/
static void tok_add(YaepParseState *ps, int code, void *attr)
{
    YaepInputToken tok;

    tok.attr = attr;
    tok.symb = symb_find_by_code(ps, code);
    if (tok.symb == NULL)
    {
        yaep_error(ps, YAEP_INVALID_TOKEN_CODE, "syntax error at offset %d '%c'", ps->input_len, code);
    }
    VLO_ADD_MEMORY(ps->input_vlo, &tok, sizeof(YaepInputToken));
    ps->input = (YaepInputToken*)VLO_BEGIN(ps->input_vlo);
    ps->input_len++;
}

static void free_input(YaepParseState *ps)
{
    VLO_DELETE(ps->input_vlo);
}

static void init_dotted_rules(YaepParseState *ps)
{
    ps->num_all_dotted_rules = 0;
    OS_CREATE(ps->dotted_rules_os, ps->run.grammar->alloc, 0);
    VLO_CREATE(ps->dotted_rules_table_vlo, ps->run.grammar->alloc, 4096);
    ps->dotted_rules_table = (YaepDottedRule***)VLO_BEGIN(ps->dotted_rules_table_vlo);
}

/* The following function sets up lookahead of dotted_rule SIT.  The
   function returns true if the dotted_rule tail may derive empty
   string.*/
static bool dotted_rule_calculate_lookahead(YaepParseState *ps, YaepDottedRule *dotted_rule)
{
    YaepSymbol *symb, **symb_ptr;
    bool found_not = false;

    if (ps->run.grammar->lookahead_level == 0)
    {
        dotted_rule->lookahead = NULL;
    }
    else
    {
        dotted_rule->lookahead = terminal_bitset_create(ps);
        terminal_bitset_clear(ps, dotted_rule->lookahead);
    }

    if (dotted_rule->rule->lhs->is_not_operator) return false;

    // Point to the first symbol after the dot.
    symb_ptr = &dotted_rule->rule->rhs[dotted_rule->dot_j];
    while ((symb = *symb_ptr) != NULL)
    {
        if (ps->run.grammar->lookahead_level != 0)
        {
            if (symb->is_terminal)
            {
                terminal_bitset_up(ps, dotted_rule->lookahead, symb->u.terminal.term_id);
            }
            else
            {
                terminal_bitset_or(ps, dotted_rule->lookahead, symb->u.nonterminal.first);
            }
        }
        // Stop collecting lookahead if non-empty rule and its not a not-rule.
        if (!symb->empty_p && !symb->is_not_operator) break;
        if (symb->is_not_operator) found_not = true;
        symb_ptr++;
    }

    if (symb == NULL)
    {
        // We reached the end of the tail and all were potentially empty.
        if (ps->run.grammar->lookahead_level == 1)
        {
            terminal_bitset_or(ps, dotted_rule->lookahead, dotted_rule->rule->lhs->u.nonterminal.follow);
        }
        else if (ps->run.grammar->lookahead_level != 0)
        {
            terminal_bitset_or(ps, dotted_rule->lookahead, terminal_bitset_from_table(ps, dotted_rule->dyn_lookahead_context));
        }
        if (found_not) return false;
        return true;
    }
    return false;
}

/* The following function returns dotted_rules with given
   characteristics.  Remember that dotted_rules are stored in one
   exemplar. */
static YaepDottedRule *create_dotted_rule(YaepParseState *ps, YaepRule *rule, int dot_j, int dyn_lookahead_context)
{
    YaepDottedRule *dotted_rule;
    YaepDottedRule ***dyn_lookahead_context_dotted_rules_table_ptr;

    assert(dyn_lookahead_context >= 0);
    dyn_lookahead_context_dotted_rules_table_ptr = ps->dotted_rules_table + dyn_lookahead_context;

    if ((char*) dyn_lookahead_context_dotted_rules_table_ptr >= (char*) VLO_BOUND(ps->dotted_rules_table_vlo))
    {
        YaepDottedRule***bound,***ptr;
        int i, diff;

        assert((ps->run.grammar->lookahead_level <= 1 && dyn_lookahead_context == 0) || (ps->run.grammar->lookahead_level > 1 && dyn_lookahead_context >= 0));
        diff = (char*) dyn_lookahead_context_dotted_rules_table_ptr -(char*) VLO_BOUND(ps->dotted_rules_table_vlo);
        diff += sizeof(YaepDottedRule**);
        if (ps->run.grammar->lookahead_level > 1 && diff == sizeof(YaepDottedRule**))
        {
            diff *= 10;
        }
        VLO_EXPAND(ps->dotted_rules_table_vlo, diff);
        ps->dotted_rules_table =(YaepDottedRule***) VLO_BEGIN(ps->dotted_rules_table_vlo);
        bound =(YaepDottedRule***) VLO_BOUND(ps->dotted_rules_table_vlo);
        dyn_lookahead_context_dotted_rules_table_ptr = ps->dotted_rules_table + dyn_lookahead_context;
        ptr = bound - diff / sizeof(YaepDottedRule**);

        while (ptr < bound)
        {
            OS_TOP_EXPAND(ps->dotted_rules_os,
                          (ps->run.grammar->rulestorage_ptr->n_rhs_lens + ps->run.grammar->rulestorage_ptr->num_rules)
                          *sizeof(YaepDottedRule*));

            *ptr = (YaepDottedRule**)OS_TOP_BEGIN(ps->dotted_rules_os);
            OS_TOP_FINISH(ps->dotted_rules_os);

            for(i = 0; i < ps->run.grammar->rulestorage_ptr->n_rhs_lens + ps->run.grammar->rulestorage_ptr->num_rules; i++)
            {
                (*ptr)[i] = NULL;
            }
            ptr++;
        }
    }

    if ((dotted_rule = (*dyn_lookahead_context_dotted_rules_table_ptr)[rule->rule_start_offset + dot_j]) != NULL)
    {
        return dotted_rule;
    }
    OS_TOP_EXPAND(ps->dotted_rules_os, sizeof(YaepDottedRule));
    dotted_rule =(YaepDottedRule*) OS_TOP_BEGIN(ps->dotted_rules_os);
    OS_TOP_FINISH(ps->dotted_rules_os);
    ps->num_all_dotted_rules++;
    dotted_rule->rule = rule;
    dotted_rule->dot_j = dot_j;
    dotted_rule->id = ps->num_all_dotted_rules;
    dotted_rule->dyn_lookahead_context = dyn_lookahead_context;
    dotted_rule->empty_tail_p = dotted_rule_calculate_lookahead(ps, dotted_rule);

    (*dyn_lookahead_context_dotted_rules_table_ptr)[rule->rule_start_offset + dot_j] = dotted_rule;

    assert(dotted_rule->lookahead);
    return dotted_rule;
}


/* Return hash of sequence of NUM_DOTTED_RULES dotted_rules in array DOTTED_RULES. */
static unsigned dotted_rules_hash(int num_dotted_rules, YaepDottedRule **dotted_rules)
{
    int n, i;
    unsigned result;

    result = jauquet_prime_mod32;
    for(i = 0; i < num_dotted_rules; i++)
    {
        n = dotted_rules[i]->id;
        result = result* hash_shift + n;
    }
    return result;
}

static void free_dotted_rules(YaepParseState *ps)
{
    VLO_DELETE(ps->dotted_rules_table_vlo);
    OS_DELETE(ps->dotted_rules_os);
}

static unsigned stateset_core_hash(YaepStateSet* s)
{
    return s->core->hash;
}

static bool stateset_core_eq(YaepStateSet *s1, YaepStateSet *s2)
{
    YaepDottedRule **dotted_rule_ptr1, **dotted_rule_ptr2, **dotted_rule_bound1;
    YaepStateSetCore*set_core1 = s1->core;
    YaepStateSetCore*set_core2 = s2->core;

    if (set_core1->num_started_dotted_rules != set_core2->num_started_dotted_rules)
    {
        return false;
    }
    dotted_rule_ptr1 = set_core1->dotted_rules;
    dotted_rule_bound1 = dotted_rule_ptr1 + set_core1->num_started_dotted_rules;
    dotted_rule_ptr2 = set_core2->dotted_rules;
    while(dotted_rule_ptr1 < dotted_rule_bound1)
    {
        if (*dotted_rule_ptr1++ !=*dotted_rule_ptr2++)
        {
            return false;
        }
    }
    return true;
}

static unsigned matched_lengths_hash(YaepStateSet *s)
{
    return s->matched_lengths_hash;
}

/* Compare all the matched_lengths stored in the two state sets. */
static bool matched_lengths_eq(YaepStateSet *s1, YaepStateSet *s2)
{
    int *i = s1->matched_lengths;
    int *j = s2->matched_lengths;
    int num_matched_lengths = s1->core->num_started_dotted_rules;

    if (num_matched_lengths != s2->core->num_started_dotted_rules)
    {
        return false;
    }

    int *bound = i + num_matched_lengths;
    while (i < bound)
    {
        if (*i++ != *j++)
        {
            return false;
        }
    }
    return true;
}

static unsigned stateset_core_matched_lengths_hash(YaepStateSet *s)
{
    return stateset_core_hash(s) * hash_shift + matched_lengths_hash(s);
}

static bool stateset_core_matched_lengths_eq(YaepStateSet *s1, YaepStateSet *s2)
{
    YaepStateSetCore *set_core1 = s1->core;
    YaepStateSetCore *set_core2 = s2->core;
    int*matched_lengths1 = s1->matched_lengths;
    int*matched_lengths2 = s2->matched_lengths;

    return set_core1 == set_core2 && matched_lengths1 == matched_lengths2;
}

static unsigned stateset_term_lookahead_hash(YaepStateSetTermLookAhead *s)
{
    YaepStateSet *set = s->set;
    YaepSymbol *term = s->term;
    int lookahead_term = s->lookahead_term;

    return ((stateset_core_matched_lengths_hash(set)* hash_shift + term->u.terminal.term_id)* hash_shift + lookahead_term);
}

static bool stateset_term_lookahead_eq(YaepStateSetTermLookAhead *s1, YaepStateSetTermLookAhead *s2)
{
    YaepStateSet *set1 = s1->set;
    YaepStateSet *set2 = s2->set;
    YaepSymbol *term1 = s1->term;
    YaepSymbol *term2 = s2->term;
    int lookahead1 = s1->lookahead_term;
    int lookahead2 = s2->lookahead_term;

    return set1 == set2 && term1 == term2 && lookahead1 == lookahead2;
}

/* Initiate the set of pairs(sit, dist). */
static void dotted_rule_matched_length_set_init(YaepParseState *ps)
{
    VLO_CREATE(ps->dotted_rule_matched_length_vec_vlo, ps->run.grammar->alloc, 8192);
    ps->dotted_rule_matched_length_vec_generation = 0;
}

/* The clear the set we only need to increment the generation.
   The test for set membership compares with the active generation.
   Thus all previously stored memberships are immediatly invalidated
   through the increment below. Thus clearing the set! */
static void clear_dotted_rule_matched_length_set(YaepParseState *ps)
{
    ps->dotted_rule_matched_length_vec_generation++;
}

/* Insert pair(DOTTED_RULE, DIST) into the ps->dotted_rule_matched_length_vec_vlo.
   Each dotted_rule has a unique id incrementally counted from 0 to the most recent dotted_rule added.
   This id is used as in index into the vector, the vector storing vlo objects.
   Each vlo object maintains a memory region used for an integer array of matched_lengths.

   If such pair exists return true (was false), otherwise return false. (was true). */
static bool dotted_rule_matched_length_test_and_set(YaepParseState *ps, YaepDottedRule *dotted_rule, int dist)
{
    int i, len, id;
    vlo_t *dist_vlo;

    id = dotted_rule->id;

    // Expand the vector to accommodate a new dotted_rule.
    len = VLO_LENGTH(ps->dotted_rule_matched_length_vec_vlo)/sizeof(vlo_t);
    if (len <= id)
    {
        VLO_EXPAND(ps->dotted_rule_matched_length_vec_vlo,(id + 1 - len)* sizeof(vlo_t));
        for(i = len; i <= id; i++)
        {
            // For each new slot in the vector, initialize a new vlo, to be used for matched_lengths.
            VLO_CREATE(((vlo_t*) VLO_BEGIN(ps->dotted_rule_matched_length_vec_vlo))[i], ps->run.grammar->alloc, 64);
        }
    }

    // Now fetch the vlo for this id, which is either an existing vlo or a freshly initialized vlo.
    // The vlo stores an array of integersCheck if the vlo is big enough for this matched_length?
    dist_vlo = &((vlo_t*)VLO_BEGIN(ps->dotted_rule_matched_length_vec_vlo))[id];
    len = VLO_LENGTH(*dist_vlo) / sizeof(int);
    if (len <= dist)
    {
        VLO_EXPAND(*dist_vlo,(dist + 1 - len)* sizeof(int));
        for(i = len; i <= dist; i++)
        {
           ((int*) VLO_BEGIN(*dist_vlo))[i] = 0;
        }
    }
    int *generation = (int*)VLO_BEGIN(*dist_vlo) + dist;
    if (*generation == ps->dotted_rule_matched_length_vec_generation)
    {
        // The pair was already inserted! We know this since we found the current generation in this slot.
        // Remember that we clear the set by incrementing the current generation.
        return true;
    }
    // The pair did not exist in the set. (Since the generation number did not match.)
    // Insert this pair my marking the vec[id][dist] with the current generation.
    *generation = ps->dotted_rule_matched_length_vec_generation;
    return false;
}

static void free_dotted_rule_matched_length_sets(YaepParseState *ps)
{
    int i, len = VLO_LENGTH(ps->dotted_rule_matched_length_vec_vlo) / sizeof(vlo_t);

    for(i = 0; i < len; i++)
    {
        VLO_DELETE(((vlo_t*) VLO_BEGIN(ps->dotted_rule_matched_length_vec_vlo))[i]);
    }
    VLO_DELETE(ps->dotted_rule_matched_length_vec_vlo);
}

/* Initialize work with sets for parsing input with N_INPUT tokens.*/
static void set_init(YaepParseState *ps, int n_input)
{
    int n = n_input >> 3;

    OS_CREATE(ps->set_cores_os, ps->run.grammar->alloc, 0);
    OS_CREATE(ps->set_dotted_rules_os, ps->run.grammar->alloc, 2048);
    OS_CREATE(ps->set_parent_dotted_rule_ids_os, ps->run.grammar->alloc, 2048);
    OS_CREATE(ps->set_matched_lengths_os, ps->run.grammar->alloc, 2048);
    OS_CREATE(ps->sets_os, ps->run.grammar->alloc, 0);
    OS_CREATE(ps->set_term_lookahead_os, ps->run.grammar->alloc, 0);

    ps->cache_stateset_cores = create_hash_table(ps->run.grammar->alloc, 2000,
                                                 (hash_table_hash_function)stateset_core_hash,
                                                 (hash_table_eq_function)stateset_core_eq);

    ps->cache_stateset_matched_lengths = create_hash_table(ps->run.grammar->alloc, n < 20000 ? 20000 : n,
                                                           (hash_table_hash_function)matched_lengths_hash,
                                                           (hash_table_eq_function)matched_lengths_eq);

    ps->cache_stateset_core_matched_lengths = create_hash_table(ps->run.grammar->alloc, n < 20000 ? 20000 : n,
                                                                (hash_table_hash_function)stateset_core_matched_lengths_hash,
                                                                (hash_table_eq_function)stateset_core_matched_lengths_eq);

    ps->cache_stateset_term_lookahead = create_hash_table(ps->run.grammar->alloc, n < 30000 ? 30000 : n,
                                                          (hash_table_hash_function)stateset_term_lookahead_hash,
                                                          (hash_table_eq_function)stateset_term_lookahead_eq);

    ps->num_set_cores = ps->num_set_core_start_dotted_rules= 0;
    ps->num_set_matched_lengths = ps->num_set_matched_lengths_len = ps->num_parent_dotted_rule_ids = 0;
    ps->num_sets_total = ps->num_dotted_rules_total= 0;
    ps->num_set_term_lookahead = 0;
    dotted_rule_matched_length_set_init(ps);
}

static void debug_step(YaepParseState *ps, YaepDottedRule *dotted_rule, int matched_length, int parent_id)
{
    if (!ps->run.debug) return;

    MemBuffer *mb = new_membuffer();
    membuffer_printf(mb, "@%d ", ps->tok_i);

    print_dotted_rule(mb, ps, ps->tok_i, dotted_rule, matched_length, parent_id);

    debug_mb("ixml.pa.step=", mb);
    free_membuffer_and_free_content(mb);

}

static void debug_step_blocked(YaepParseState *ps, YaepDottedRule *dotted_rule, int matched_length, int parent_id)
{
    if (!ps->run.debug) return;

    MemBuffer *mb = new_membuffer();
    membuffer_printf(mb, "@%d ", ps->tok_i);

    print_dotted_rule_blocked(mb, ps, ps->tok_i, dotted_rule, matched_length, parent_id);

    debug_mb("ixml.pa.step=", mb);
    free_membuffer_and_free_content(mb);
}

static void append_dotted_rule_no_core_yet(YaepParseState *ps, YaepDottedRule *dotted_rule)
{
    assert(!ps->new_core);

    OS_TOP_EXPAND(ps->set_dotted_rules_os, sizeof(YaepDottedRule*));
    ps->new_dotted_rules = (YaepDottedRule**) OS_TOP_BEGIN(ps->set_dotted_rules_os);
    ps->new_dotted_rules[ps->new_num_leading_dotted_rules] = dotted_rule;
}

static void append_dotted_rule_to_core(YaepParseState *ps, YaepDottedRule *dotted_rule)
{
    assert(ps->new_core);

    OS_TOP_EXPAND(ps->set_dotted_rules_os, sizeof(YaepDottedRule*));
    ps->new_core->dotted_rules = (YaepDottedRule**)OS_TOP_BEGIN(ps->set_dotted_rules_os);
    ps->new_core->dotted_rules[ps->new_core->num_dotted_rules++] = dotted_rule;

    // cache ptr
    ps->new_dotted_rules = ps->new_core->dotted_rules;
}

static void append_matched_length_no_core_yet(YaepParseState *ps, int matched_length)
{
    assert(!ps->new_core);

    OS_TOP_EXPAND(ps->set_matched_lengths_os, sizeof(int));
    ps->new_matched_lengths = (int*)OS_TOP_BEGIN(ps->set_matched_lengths_os);
    ps->new_matched_lengths[ps->new_num_leading_dotted_rules] = matched_length;
}

/*
  start SIT = the leading dotted rules added first in a cycle.
     no core yet, no set yet.
     duplicates are not tested, do not add those!
     set_add_dotted_rule_with_matched_length

  non-start initial = new dotted rule with zero matched length (no parent)
     core yes, set yes
     duplicates are ignored
     set_add_dotted_rule_no_match_yet

  non-start non-initial = new dotted rule with parent pointer.
     core yes, set yes
     duplicates are ignored
     set_add_dotted_rule_with_parent

 */

/* Add start SIT with distance DIST at the end of the situation array
   of the set being formed.
   set_new_add_start_sit (struct sit *sit, int dist)
*/
static void set_add_dotted_rule_with_matched_length(YaepParseState *ps, YaepDottedRule *dotted_rule, int matched_length, const char *why)
{
    assert(!ps->new_set_ready_p);
    assert(!ps->new_set);
    assert(!ps->new_core);

    append_dotted_rule_no_core_yet(ps, dotted_rule);
    append_matched_length_no_core_yet(ps, matched_length);

    ps->new_num_leading_dotted_rules++;

    yaep_trace(ps, "%s add leading d%d len %d", why, dotted_rule->id, matched_length);
    debug_step(ps, dotted_rule, matched_length, -1);
}

/* Add non-start (initial) SIT with zero distance at the end of the
   situation array of the set being formed.  If this is non-start
   situation and there is already the same pair (situation, zero
   distance), we do not add it.
   set_new_add_initial_sit (struct sit *sit)
*/
static void set_add_dotted_rule_no_match_yet(YaepParseState *ps, YaepDottedRule *dotted_rule, const char *why)
{
    assert(ps->new_set_ready_p);
    assert(ps->new_set);
    assert(ps->new_core);

    /* When we add not-yet-started dotted_rules we need to have pairs
       (dotted_rule, the corresponding matched_length) without duplicates
       because we also form core_symb_to_predcomps at that time. */
    for (int i = ps->new_num_leading_dotted_rules; i < ps->new_core->num_dotted_rules; i++)
    {
        // Check if already added.
        if (ps->new_dotted_rules[i] == dotted_rule) {
            //yaep_trace(ps, "skip d%", dotted_rule->id);
            return;
        }
    }
    /* Remember we do not store matched_length for not-yet-started dotted_rules. */
    append_dotted_rule_to_core(ps, dotted_rule);

    yaep_trace(ps, "%s add d%d to c%d", why, dotted_rule->id, ps->new_core->id);
    debug_step(ps, dotted_rule, 0, -1);
}

/* Add nonstart, noninitial SIT with distance DIST at the end of the
   situation array of the set being formed.  If this is situation and
   there is already the same pair (situation, the corresponding
   distance), we do not add it.
   set_add_new_nonstart_sit (struct sit *sit, int parent)
*/
static void set_add_dotted_rule_with_parent(YaepParseState *ps,
                                            YaepDottedRule *dotted_rule,
                                            int parent_rule_index,
                                            const char *why)
{
    assert(ps->new_set_ready_p);
    assert(ps->new_set);
    assert(ps->new_core);

    /* When we add predicted dotted_rules we need to have pairs
       (dotted_rule + parent_dotted_rule_id) without duplicates
       because we also form core_symb_to_predcomps at that time. */
    for (int rule_index_in_core = ps->new_num_leading_dotted_rules;
         rule_index_in_core < ps->new_core->num_dotted_rules;
         rule_index_in_core++)
    {
        if (ps->new_dotted_rules[rule_index_in_core] == dotted_rule &&
            ps->new_core->to_parent_rule_index[rule_index_in_core] == parent_rule_index)
        {
            // The dotted_rule + parent dotted rule already exists.
            yaep_trace(ps, "reusing d%d with parent rule index %d", dotted_rule->id, parent_rule_index);
            return;
        }
    }

    // Increase the object stack storing dotted_rules, with the size of a new dotted_rule.
    OS_TOP_EXPAND(ps->set_dotted_rules_os, sizeof(YaepDottedRule*));
    ps->new_dotted_rules = ps->new_core->dotted_rules = (YaepDottedRule**)OS_TOP_BEGIN(ps->set_dotted_rules_os);

    // Increase the parent index vector with another int.
    // This integer points to ...?
    OS_TOP_EXPAND(ps->set_parent_dotted_rule_ids_os, sizeof(int));
    ps->new_core->to_parent_rule_index = (int*)OS_TOP_BEGIN(ps->set_parent_dotted_rule_ids_os) - ps->new_num_leading_dotted_rules;

    // Store dotted_rule into new dotted_rules.
    ps->new_dotted_rules[ps->new_core->num_dotted_rules++] = dotted_rule;
    // Store parent index. Meanst what...?
    ps->new_core->to_parent_rule_index[ps->new_core->num_all_matched_lengths++] = parent_rule_index;
    ps->num_parent_dotted_rule_ids++;

    int matched_length = ps->new_set->matched_lengths[parent_rule_index];

    yaep_trace(ps, "%s add d%d with parent index %d to c%d", why, dotted_rule->id, parent_rule_index, ps->new_core->id);
    debug_step(ps, dotted_rule, matched_length, parent_rule_index);
}

/* Set up hash of matched_lengths of set S. */
static void setup_set_matched_lengths_hash(hash_table_entry_t s)
{
    YaepStateSet *set = (YaepStateSet*)s;

    int num_matched_lengths = set->core->num_started_dotted_rules;
    unsigned result = jauquet_prime_mod32;

    int *i = set->matched_lengths;
    int *stop = i + num_matched_lengths;

    while (i < stop)
    {
        result = result*hash_shift + *i++;
    }
    set->matched_lengths_hash = result;
}

/* Set up hash of core of set S. */
static void setup_stateset_core_hash(YaepStateSet *s)
{
    s->core->hash = dotted_rules_hash(s->core->num_started_dotted_rules, s->core->dotted_rules);
}

static void prepare_for_leading_dotted_rules(YaepParseState *ps)
{
    ps->new_set = NULL;
    ps->new_core = NULL;
    ps->new_set_ready_p = false;
    ps->new_dotted_rules = NULL;
    ps->new_matched_lengths = NULL;
    ps->new_num_leading_dotted_rules = 0;

    yaep_trace(ps, "start collecting leading rules");
}

/* The new set should contain only start dotted_rules.
   Sort dotted_rules, remove duplicates and insert set into the set table.
   Returns true if a new core was allocated. False if an old core was reused. */
static bool convert_leading_dotted_rules_into_new_set(YaepParseState *ps)
{
    bool added;

    assert(!ps->new_set_ready_p);

    OS_TOP_EXPAND(ps->sets_os, sizeof(YaepStateSet));
    ps->new_set = (YaepStateSet*)OS_TOP_BEGIN(ps->sets_os);
    ps->new_set->matched_lengths = ps->new_matched_lengths;
    ps->new_set->id = ps->num_sets_total;

    yaep_trace(ps, "convert leading rules into s%d", ps->new_set->id);

    OS_TOP_EXPAND(ps->set_cores_os, sizeof(YaepStateSetCore));
    ps->new_set->core = ps->new_core = (YaepStateSetCore*) OS_TOP_BEGIN(ps->set_cores_os);
    ps->new_core->num_started_dotted_rules = ps->new_num_leading_dotted_rules;
    ps->new_core->dotted_rules = ps->new_dotted_rules;

#ifdef USE_SET_HASH_TABLE
    // Lookup matched_lengths from cache table.
    setup_set_matched_lengths_hash(ps->new_set);
    YaepStateSet **sm = (YaepStateSet**)find_hash_table_entry(ps->cache_stateset_matched_lengths, ps->new_set, true);
    if (*sm != NULL)
    {
        // The matched lengths already existed use the cache.
        ps->new_matched_lengths = ps->new_set->matched_lengths = (*sm)->matched_lengths;
        OS_TOP_NULLIFY(ps->set_matched_lengths_os);

        if (xmq_trace_enabled_)
        {
            MemBuffer *mb = new_membuffer();
            print_matched_lenghts(mb, ps->new_set);
            membuffer_append_null(mb);
            yaep_trace(ps, "re-using matched lengths %s", mb->buffer_);
            free_membuffer_and_free_content(mb);
        }
    }
    else
    {
        // This is a new set of matched lengths.
        OS_TOP_FINISH(ps->set_matched_lengths_os);
        *sm = ps->new_set;
        ps->num_set_matched_lengths++;
        ps->num_set_matched_lengths_len += ps->new_num_leading_dotted_rules;

        if (xmq_trace_enabled_)
        {
            MemBuffer *mb = new_membuffer();
            print_matched_lenghts(mb, ps->new_set);
            membuffer_append_null(mb);
            yaep_trace(ps, "new matched lengths (%s)", mb->buffer_);
            free_membuffer_and_free_content(mb);
        }
    }
#else
    OS_TOP_FINISH(ps->set_matched_lengths_os);
    ps->num_set_matched_lengths++;
    ps->num_set_matched_lengths_len += ps->new_num_leading_dotted_rules;
#endif

    /* Insert set core into table.*/
    setup_stateset_core_hash(ps->new_set);
    /* We look for a core with an identical list of started dotted rules (pointer equivalence). */
    YaepStateSet **sc = (YaepStateSet**)find_hash_table_entry(ps->cache_stateset_cores, ps->new_set, true);
    bool reuse_core = *sc != NULL;
    if (reuse_core)
    {
        // We can potentially re-use this core, but lets check if there are not-operators in any of the dotted rules.
        // If so, then we cannot re-use the core. Later on we can improve this by checking if the not rules
        // apply to this position as well.
        if (core_has_not_rules((*sc)->core)) reuse_core = false;
    }
    if (reuse_core)
    {
        // The core already existed, drop the core allocation.
        // Point to the old core instead.
        OS_TOP_NULLIFY(ps->set_cores_os);
        ps->new_set->core = ps->new_core = (*sc)->core;
        ps->new_dotted_rules = ps->new_core->dotted_rules;

        OS_TOP_NULLIFY(ps->set_dotted_rules_os);
        added = false;

        if (xmq_trace_enabled_)
        {
            MemBuffer *mb = new_membuffer();
            print_core(mb, (*sc)->core);
            membuffer_append_null(mb);
            yaep_trace(ps, "re-using %s", mb->buffer_);
            free_membuffer_and_free_content(mb);
        }
    }
    else
    {
        OS_TOP_FINISH(ps->set_cores_os);
        ps->new_core->id = ps->num_set_cores++;
        ps->new_core->num_dotted_rules = ps->new_num_leading_dotted_rules;
        ps->new_core->num_all_matched_lengths = ps->new_num_leading_dotted_rules;
        ps->new_core->to_parent_rule_index = NULL;
        *sc = ps->new_set;
        ps->num_set_core_start_dotted_rules += ps->new_num_leading_dotted_rules;
        added = true;

        if (xmq_trace_enabled_)
        {
            MemBuffer *mb = new_membuffer();
            print_core(mb, ps->new_set->core);
            membuffer_append_null(mb);
            yaep_trace(ps, "new %s", mb->buffer_);
            free_membuffer_and_free_content(mb);
        }
    }

#ifdef USE_SET_HASH_TABLE
    /* Insert set into table.*/
    YaepStateSet **scm = (YaepStateSet**)find_hash_table_entry(ps->cache_stateset_core_matched_lengths, ps->new_set, true);
    if (*scm == NULL)
    {
       *scm = ps->new_set;
        ps->num_sets_total++;
        ps->num_dotted_rules_total += ps->new_num_leading_dotted_rules;
        OS_TOP_FINISH(ps->sets_os);
        yaep_trace(ps, "new s%d", ps->new_set->id);
    }
    else
    {
        ps->new_set = *scm;
        OS_TOP_NULLIFY(ps->sets_os);
        yaep_trace(ps, "re-using s%d", ps->new_set->id);
    }
#else
    OS_TOP_FINISH(ps->sets_os);
#endif

    ps->new_set_ready_p = true;
    return added;
}

/* The following function finishes work with set being formed.*/
static void set_new_core_stop(YaepParseState *ps)
{
    OS_TOP_FINISH(ps->set_dotted_rules_os);
    OS_TOP_FINISH(ps->set_parent_dotted_rule_ids_os);
}

static void free_sets(YaepParseState *ps)
{
    free_dotted_rule_matched_length_sets(ps);
    delete_hash_table(ps->cache_stateset_term_lookahead);
    delete_hash_table(ps->cache_stateset_core_matched_lengths);
    delete_hash_table(ps->cache_stateset_matched_lengths);
    delete_hash_table(ps->cache_stateset_cores);
    OS_DELETE(ps->set_term_lookahead_os);
    OS_DELETE(ps->sets_os);
    OS_DELETE(ps->set_parent_dotted_rule_ids_os);
    OS_DELETE(ps->set_dotted_rules_os);
    OS_DELETE(ps->set_matched_lengths_os);
    OS_DELETE(ps->set_cores_os);
}

/* Initialize work with the parser list.*/
static void pl_init(YaepParseState *ps)
{
    ps->state_sets = NULL;
}

/* The following function creates Earley's parser list.*/
static void allocate_state_sets(YaepParseState *ps)
{
    /* Because of error recovery we may have sets 2 times more than tokens.*/
    void *mem = yaep_malloc(ps->run.grammar->alloc, sizeof(YaepStateSet*)*(ps->input_len + 1)* 2);
    ps->state_sets = (YaepStateSet**)mem;
    ps->state_set_k = -1;
}

static void free_state_sets(YaepParseState *ps)
{
    if (ps->state_sets != NULL)
    {
        yaep_free(ps->run.grammar->alloc, ps->state_sets);
        ps->state_sets = NULL;
    }
}

static void verbose_stats(YaepParseState *ps)
{
    size_t size = memusage(ps);
    char *siz = humanReadableTwoDecimals(size);
    verbose("ixml=", "@%d/%d #sets=%d #cores=%d #dotted_rules=%d #matched_lengths=%d mem=%s",
            ps->tok_i,
            ps->input_len,
            ps->num_sets_total,
            ps->num_set_cores,
            ps->num_dotted_rules_total,
            ps->num_set_matched_lengths,
            siz
        );
    free(siz);
}
/* The following function stores error CODE and MESSAGE.  The function
   makes long jump after that.  So the function is designed to process
   only one error.*/
static void yaep_error(YaepParseState *ps, int code, const char*format, ...)
{
    va_list arguments;

    ps->run.grammar->error_code = code;
    va_start(arguments, format);
    vsprintf(ps->run.grammar->error_message, format, arguments);
    va_end(arguments);
    assert(strlen(ps->run.grammar->error_message) < YAEP_MAX_ERROR_MESSAGE_LENGTH);
    longjmp(ps->error_longjump_buff, code);
}

/* The following function processes allocation errors. */
static void error_func_for_allocate(void *ps)
{
   yaep_error((YaepParseState*)ps, YAEP_NO_MEMORY, "no memory");
}

YaepGrammar *yaepNewGrammar()
{
    YaepAllocator *allocator;

    allocator = yaep_alloc_new(NULL, NULL, NULL, NULL);
    if (allocator == NULL)
    {
        return NULL;
    }
    YaepGrammar *grammar = (YaepGrammar*)yaep_malloc(allocator, sizeof(*grammar));

    if (grammar == NULL)
    {
        yaep_alloc_del(allocator);
        return NULL;
    }
    grammar->alloc = allocator;
    yaep_alloc_seterr(allocator, error_func_for_allocate,
                      yaep_alloc_getuserptr(allocator));

    grammar->undefined_p = true;
    grammar->error_code = 0;
   *grammar->error_message = '\0';
    grammar->lookahead_level = 1;
    grammar->one_parse_p = true;
    grammar->cost_p = false;
    grammar->error_recovery_p = false;
    grammar->recovery_token_matches = DEFAULT_RECOVERY_TOKEN_MATCHES;
    grammar->symbs_ptr = symbolstorage_create(grammar);
    grammar->term_sets_ptr = termsetstorage_create(grammar);
    grammar->rulestorage_ptr = rulestorage_create(grammar);
    return grammar;
}

YaepParseRun *yaepNewParseRun(YaepGrammar *g)
{
    YaepParseState *ps = (YaepParseState*)calloc(1, sizeof(YaepParseState));
    INSTALL_PARSE_STATE_MAGIC(ps);

    ps->run.grammar = g;

    return (YaepParseRun*)ps;
}

void yaepFreeParseRun(YaepParseRun *pr)
{
    YaepParseState *ps = (YaepParseState*)pr;
    assert(CHECK_PARSE_STATE_MAGIC(ps));
    free(ps);
}

void yaepSetUserData(YaepGrammar *g, void *data)
{
    g->user_data = data;
}

void *yaepGetUserData(YaepGrammar *g)
{
    return g->user_data;
}

/* The following function makes grammar empty.*/
static void yaep_empty_grammar(YaepParseState *ps,YaepGrammar *grammar)
{
    if (grammar != NULL)
    {
        rulestorage_clear(grammar->rulestorage_ptr);
        terminal_bitset_empty(grammar->term_sets_ptr);
        symb_empty(ps, grammar->symbs_ptr);
    }
}

/* The function returns the last occurred error code for given grammar. */
int
yaep_error_code(YaepGrammar*g)
{
    assert(g != NULL);
    return g->error_code;
}

/* The function returns message are always contains error message
   corresponding to the last occurred error code.*/
const char*
yaep_error_message(YaepGrammar*g)
{
    assert(g != NULL);
    return g->error_message;
}

/* The following function creates sets FIRST and FOLLOW for all
   grammar nonterminals.*/
static void create_first_follow_sets(YaepParseState *ps)
{
    YaepSymbol *symb, **rhs, *rhs_symb, *next_rhs_symb;
    YaepRule *rule;
    int i, j, k, rhs_len;
    bool changed_p, first_continue_p;

    for (i = 0; (symb = nonterm_get(ps, i)) != NULL; i++)
    {
        symb->u.nonterminal.first = terminal_bitset_create(ps);
        terminal_bitset_clear(ps, symb->u.nonterminal.first);

        symb->u.nonterminal.follow = terminal_bitset_create(ps);
        terminal_bitset_clear(ps, symb->u.nonterminal.follow);
    }

    do
    {
        changed_p = false;
        for(i = 0;(symb = nonterm_get(ps, i)) != NULL; i++)
        {
            for (rule = symb->u.nonterminal.rules;
                 rule != NULL;
                 rule = rule->lhs_next)
            {
                first_continue_p = true;
                rhs = rule->rhs;
                rhs_len = rule->rhs_len;
                for(j = 0; j < rhs_len; j++)
                {
                    rhs_symb = rhs[j];
                    if (rhs_symb->is_terminal)
                    {
                        if (first_continue_p)
                        {
                            changed_p |= terminal_bitset_up(ps, symb->u.nonterminal.first,
                                                            rhs_symb->u.terminal.term_id);
                        }
                    }
                    else
                    {
                        if (first_continue_p)
                        {
                            changed_p |= terminal_bitset_or(ps,
                                                            symb->u.nonterminal.first,
                                                            rhs_symb->u.nonterminal.first);
                        }
                        for(k = j + 1; k < rhs_len; k++)
                        {
                            next_rhs_symb = rhs[k];
                            if (next_rhs_symb->is_terminal)
                            {
                                changed_p
                                    |= terminal_bitset_up(ps, rhs_symb->u.nonterminal.follow,
                                                   next_rhs_symb->u.terminal.term_id);
                            }
                            else
                            {
                                changed_p
                                    |= terminal_bitset_or(ps, rhs_symb->u.nonterminal.follow,
                                                   next_rhs_symb->u.nonterminal.first);
                            }
                            if (!next_rhs_symb->empty_p && !next_rhs_symb->is_not_operator)
                            {
                                break;
                            }
                        }
                        if (k == rhs_len)
                        {
                            changed_p |= terminal_bitset_or(ps, rhs_symb->u.nonterminal.follow,
                                                     symb->u.nonterminal.follow);
                        }
                    }
                    if (!rhs_symb->empty_p && !rhs_symb->is_not_operator)
                    {
                        first_continue_p = false;
                    }
                }
            }
        }
    }
    while (changed_p);
}

/* The following function sets up flags empty_p, access_p and
   derivation_p for all grammar symbols.*/
static void set_empty_access_derives(YaepParseState *ps)
{
    bool empty_changed_p, derivation_changed_p, accessibility_change_p;

    for (int i = 0; ; i++)
    {
        YaepSymbol *symb = symb_get(ps, i);
        if (!symb) break;
        symb->empty_p = false;
        symb->derivation_p = (symb->is_terminal ? true : false);
        symb->access_p = false;
    }

    ps->run.grammar->axiom->access_p = 1;
    do
    {
        empty_changed_p = derivation_changed_p = accessibility_change_p = false;
        for (int i = 0; ; i++)
        {
            YaepSymbol *symb = nonterm_get(ps, i);
            if (!symb) break;

            for (YaepRule *rule = symb->u.nonterminal.rules; rule != NULL; rule = rule->lhs_next)
            {
                bool empty_p = true;
                bool derivation_p = true;

                if (rule->lhs->is_not_operator)
                {
                    empty_p = 0;
                }

                for (int j = 0; j < rule->rhs_len; j++)
                {
                    YaepSymbol *rhs_symb = rule->rhs[j];
                    if (symb->access_p)
                    {
                        accessibility_change_p |= rhs_symb->access_p ^ 1;
                        rhs_symb->access_p = 1;
                    }
                    // A not rule forbids emptiness since it must always be checked against the input.
                    if (rhs_symb->is_not_operator)
                    {
                        empty_p = 0;
                    }
                    empty_p &= rhs_symb->empty_p;
                    derivation_p &= rhs_symb->derivation_p;
                }
                if (empty_p)
                {
                    empty_changed_p |= symb->empty_p ^ empty_p;
                    symb->empty_p = empty_p;
                }
                if (derivation_p)
                {
                    derivation_changed_p |= symb->derivation_p ^ derivation_p;
                    symb->derivation_p = derivation_p;
                }
            }
        }
    }
    while(empty_changed_p || derivation_changed_p || accessibility_change_p);

}

/* The following function sets up flags loop_p for nonterminals. */
static void set_loop_p(YaepParseState *ps)
{
    YaepSymbol*symb,*lhs;
    YaepRule*rule;
    int i, j, k;
    bool loop_p, changed_p;
    /* Initialize accoding to minimal criteria: There is a rule in which
       the nonterminal stands and all the rest symbols can derive empty
       strings.*/
    for(rule = ps->run.grammar->rulestorage_ptr->first_rule; rule != NULL; rule = rule->next)
        for(i = 0; i < rule->rhs_len; i++)
            if (!(symb = rule->rhs[i])->is_terminal)
            {
                for(j = 0; j < rule->rhs_len; j++)
                    if (i == j)
                        continue;
                    else if (!rule->rhs[j]->empty_p)
                        break;
                if (j >= rule->rhs_len)
                    symb->u.nonterminal.loop_p = true;
            }
    /* Major cycle: Check looped nonterminal that there is a rule with
       the nonterminal in lhs with a looped nonterminal in rhs and all
       the rest rhs symbols deriving empty string.*/
    do
    {
        changed_p = false;
        for(i = 0;(lhs = nonterm_get(ps, i)) != NULL; i++)
            if (lhs->u.nonterminal.loop_p)
            {
                loop_p = false;
                for(rule = lhs->u.nonterminal.rules;
                     rule != NULL; rule = rule->lhs_next)
                    for(j = 0; j < rule->rhs_len; j++)
                        if (!(symb = rule->rhs[j])->is_terminal && symb->u.nonterminal.loop_p)
                        {
                            for(k = 0; k < rule->rhs_len; k++)
                                if (j == k)
                                    continue;
                                else if (!rule->rhs[k]->empty_p)
                                    break;
                            if (k >= rule->rhs_len)
                                loop_p = true;
                        }
                if (!loop_p)
                    changed_p = true;
                lhs->u.nonterminal.loop_p = loop_p;
            }
    }
    while(changed_p);
}

/* The following function evaluates different sets and flags for
   grammar and checks the grammar on correctness.*/
static void check_grammar(YaepParseState *ps, int strict_p)
{
    YaepSymbol*symb;
    int i;

    set_empty_access_derives(ps);
    set_loop_p(ps);
    if (strict_p)
    {
        for(i = 0;(symb = nonterm_get(ps, i)) != NULL; i++)
        {
            if (!symb->derivation_p)
            {
                yaep_error(ps, YAEP_NONTERM_DERIVATION,
                     "nonterm `%s' does not derive any term string", symb->repr);
            }
            else if (!symb->access_p)
            {
                yaep_error(ps,
                           YAEP_UNACCESSIBLE_NONTERM,
                           "nonterm `%s' is not accessible from axiom",
                           symb->repr);
            }
        }
    }
    else if (!ps->run.grammar->axiom->derivation_p)
    {
        yaep_error(ps, YAEP_NONTERM_DERIVATION,
                   "nonterm `%s' does not derive any term string",
                   ps->run.grammar->axiom->repr);
    }
    for(i = 0;(symb = nonterm_get(ps, i)) != NULL; i++)
    {
        if (symb->u.nonterminal.loop_p)
        {
            yaep_error(ps, YAEP_LOOP_NONTERM,
                 "nonterm `%s' can derive only itself(grammar with loops)",
                 symb->repr);
        }
    }
    /* We should have correct flags empty_p here.*/
    create_first_follow_sets(ps);
}

/* The following are names of additional symbols.  Don't use them in
   grammars.*/
#define AXIOM_NAME "$"
#define END_MARKER_NAME "ω"
#define TERM_ERROR_NAME "error"

/* It should be negative.*/
#define END_MARKER_CODE -1
#define TERM_ERROR_CODE -2

int yaep_read_grammar(YaepParseRun *pr,
                      YaepGrammar *g,
                      int strict_p,
                      const char*(*read_terminal)(YaepParseRun*pr,YaepGrammar*g,int*code),
                      const char*(*read_rule)(YaepParseRun*pr,YaepGrammar*g,const char***rhs,
                                              const char**abs_node,
                                              int*anode_cost, int**transl, char*mark, char**marks))
{
    const char*name,*lhs,**rhs,*anode;
    YaepSymbol*symb, *start;
    YaepRule*rule;
    int anode_cost;
    int*transl;
    char mark;
    char*marks;
    int i, el, code;

    assert(g != NULL);
    YaepParseState *ps = (YaepParseState*)pr;
    assert(CHECK_PARSE_STATE_MAGIC(ps));

    if ((code = setjmp(ps->error_longjump_buff)) != 0)
    {
        return code;
    }
    if (!ps->run.grammar->undefined_p)
    {
        yaep_empty_grammar(ps, ps->run.grammar);
    }
    while((name =(*read_terminal)(pr, pr->grammar, &code)) != NULL)
    {
        if (code < 0)
        {
            yaep_error(ps, YAEP_NEGATIVE_TERM_CODE,
                        "term `%s' has negative code", name);
        }
        symb = symb_find_by_repr(ps, name);
        if (symb != NULL)
        {
            yaep_error(ps, YAEP_REPEATED_TERM_DECL,
                        "repeated declaration of term `%s'", name);
        }
        if (symb_find_by_code(ps, code) != NULL)
        {
            yaep_error(ps, YAEP_REPEATED_TERM_CODE,
                        "repeated code %d in term `%s'", code, name);
        }
        symb_add_terminal(ps, name, code);
    }

    /* Adding error symbol.*/
    if (symb_find_by_repr(ps, TERM_ERROR_NAME) != NULL)
    {
        yaep_error(ps, YAEP_FIXED_NAME_USAGE, "do not use fixed name `%s'", TERM_ERROR_NAME);
    }

    if (symb_find_by_code(ps, TERM_ERROR_CODE) != NULL) abort();

    ps->run.grammar->term_error = symb_add_terminal(ps, TERM_ERROR_NAME, TERM_ERROR_CODE);
    ps->run.grammar->term_error_id = ps->run.grammar->term_error->u.terminal.term_id;
    ps->run.grammar->axiom = ps->run.grammar->end_marker = NULL;

    for (;;)
    {
        lhs = (*read_rule)(pr, pr->grammar, &rhs, &anode, &anode_cost, &transl, &mark, &marks);
        if (lhs == NULL) break;

        symb = symb_find_by_repr(ps, lhs);
        if (symb == NULL)
        {
            symb = symb_add_nonterm(ps, lhs);
        }
        else if (symb->is_terminal)
        {
            yaep_error(ps, YAEP_TERM_IN_RULE_LHS,
                        "term `%s' in the left hand side of rule", lhs);
        }
        if (anode == NULL && transl != NULL &&*transl >= 0 && transl[1] >= 0)
        {
            yaep_error(ps, YAEP_INCORRECT_TRANSLATION,
                        "rule for `%s' has incorrect translation", lhs);
        }
        if (anode != NULL && anode_cost < 0)
        {
            yaep_error(ps, YAEP_NEGATIVE_COST,
                        "translation for `%s' has negative cost", lhs);
        }
        if (ps->run.grammar->axiom == NULL)
        {
            /* We made this here becuase we want that the start rule has number 0.*/
            /* Add axiom and end marker.*/
            start = symb;
            ps->run.grammar->axiom = symb_find_by_repr(ps, AXIOM_NAME);
            if (ps->run.grammar->axiom != NULL)
            {
                yaep_error(ps, YAEP_FIXED_NAME_USAGE, "do not use fixed name `%s'", AXIOM_NAME);
            }
            ps->run.grammar->axiom = symb_add_nonterm(ps, AXIOM_NAME);
            ps->run.grammar->end_marker = symb_find_by_repr(ps, END_MARKER_NAME);
            if (ps->run.grammar->end_marker != NULL)
            {
                yaep_error(ps, YAEP_FIXED_NAME_USAGE, "do not use fixed name `%s'", END_MARKER_NAME);
            }
            if (symb_find_by_code(ps, END_MARKER_CODE) != NULL) abort();
            ps->run.grammar->end_marker = symb_add_terminal(ps, END_MARKER_NAME, END_MARKER_CODE);

            /* Add rules for start*/
            rule = rule_new_start(ps, ps->run.grammar->axiom, NULL, 0);
            rule_new_symb_add(ps, symb);
            rule_new_symb_add(ps, ps->run.grammar->end_marker);
            rule_new_stop(ps);
            rule->order[0] = 0;
            rule->trans_len = 1;
        }
        rule = rule_new_start(ps, symb, anode,(anode != NULL ? anode_cost : 0));
        size_t rhs_len = 0;
        while(*rhs != NULL)
        {
            rhs_len++;
            symb = symb_find_by_repr(ps, *rhs);
            if (symb == NULL)
            {
                symb = symb_add_nonterm(ps, *rhs);
            }
            rule_new_symb_add(ps, symb);
            rhs++;
        }
        rule_new_stop(ps);
        // IXML
        rule->mark = mark;
        memcpy(rule->marks, marks, rhs_len);

        if (transl != NULL)
        {
            for(i = 0;(el = transl[i]) >= 0; i++)
            {
                if (el >= rule->rhs_len)
                {
                    if (el != YAEP_NIL_TRANSLATION_NUMBER)
                    {
                        yaep_error(ps, YAEP_INCORRECT_SYMBOL_NUMBER,
                                   "translation symbol number %d in rule for `%s' is out of range",
                                   el, lhs);
                    }
                    else
                    {
                        rule->trans_len++;
                    }
                }
                else if (rule->order[el] >= 0)
                {
                    yaep_error(ps, YAEP_REPEATED_SYMBOL_NUMBER,
                               "repeated translation symbol number %d in rule for `%s'",
                               el, lhs);
                }
                else
                {
                    rule->order[el] = i;
                    rule->trans_len++;
                }
                assert(i < rule->rhs_len || transl[i] < 0);
            }
        }
    }

    if (ps->run.grammar->axiom == NULL)
    {
        yaep_error(ps, YAEP_NO_RULES, "grammar does not contains rules");
    }

    assert(start != NULL);

    /* Adding axiom : error $eof if it is neccessary.*/
    for(rule = start->u.nonterminal.rules; rule != NULL; rule = rule->lhs_next)
    {
        if (rule->rhs[0] == ps->run.grammar->term_error) break;
    }

    if (rule == NULL)
    {
        rule = rule_new_start(ps, ps->run.grammar->axiom, NULL, 0);
        rule_new_symb_add(ps, ps->run.grammar->term_error);
        rule_new_symb_add(ps, ps->run.grammar->end_marker);
        rule_new_stop(ps);
        rule->trans_len = 0;
        rule->mark = 0;
    }

    check_grammar(ps, strict_p);

    symb_finish_adding_terms(ps);

    if (ps->run.verbose)
    {
        /* Print rules.*/
        for(rule = ps->run.grammar->rulestorage_ptr->first_rule; rule != NULL; rule = rule->next)
        {
            if (rule->lhs->repr[0] != '$')
            {
                MemBuffer *mb = new_membuffer();
                rule_print(mb, ps, rule, true);
                debug_mb("ixml.gr=", mb);
                free_membuffer_and_free_content(mb);
            }
        }
        /* Print symbol sets with lookahead.*/
        if (ps->run.debug)
        {
            for(i = 0;(symb = nonterm_get(ps, i)) != NULL; i++)
            {
                MemBuffer *mb = new_membuffer();
                membuffer_printf(mb, "%s%s%s%s%s\n",
                                 symb->repr,
                                 (symb->empty_p ? " CAN_BECOME_EMPTY" : ""),
                                 (symb->is_not_operator ? " NOT_OP" : ""),
                                 (symb->access_p ? "" : " OUPS_NOT_REACHABLE"),
                                 (symb->derivation_p ? "" : " OUPS_NO_TEXT"));
                membuffer_append(mb, "  1st: ");
                print_terminal_bitset(mb, ps, symb->u.nonterminal.first);
                membuffer_append(mb, "\n  2nd: ");
                print_terminal_bitset(mb, ps, symb->u.nonterminal.follow);
                debug_mb("ixml.nt=", mb);
                free_membuffer_and_free_content(mb);
            }
        }
    }

    ps->run.grammar->undefined_p = false;
    return 0;
}

/* The following functions set up parameter which affect parser work
   and return the previous parameter value.*/
int yaep_set_lookahead_level(YaepGrammar *grammar, int level)
{
    int old;

    assert(grammar != NULL);
    old = grammar->lookahead_level;
    grammar->lookahead_level =(level < 0 ? 0 : level > 2 ? 2 : level);
    return old;
}

bool yaep_set_one_parse_flag(YaepGrammar *grammar, bool flag)
{
    assert(grammar != NULL);
    bool old = grammar->one_parse_p;
    grammar->one_parse_p = flag;
    return old;
}

bool yaep_set_cost_flag(YaepGrammar *grammar, bool flag)
{
    assert(grammar != NULL);
    bool old = grammar->cost_p;
    grammar->cost_p = flag;
    return old;
}

bool yaep_set_error_recovery_flag(YaepGrammar *grammar, bool flag)
{
    assert(grammar != NULL);
    bool old = grammar->error_recovery_p;
    grammar->error_recovery_p = flag;
    return old;
}

int yaep_set_recovery_match(YaepGrammar *grammar, int n_input)
{
    int old;

    assert(grammar != NULL);
    old = grammar->recovery_token_matches;
    grammar->recovery_token_matches = n_input;
    return old;
}

static void yaep_parse_init(YaepParseState *ps, int n_input)
{
    YaepRule*rule;

    init_dotted_rules(ps);
    set_init(ps, n_input);
    core_symb_to_predcomps_init(ps);
#ifdef USE_CORE_SYMB_HASH_TABLE
    {
        int i;
        YaepSymbol*symb;

        for(i = 0;(symb = symb_get(ps, i)) != NULL; i++)
        {
            symb->cached_core_symb_to_predcomps = NULL;
        }
    }
#endif
    for(rule = ps->run.grammar->rulestorage_ptr->first_rule; rule != NULL; rule = rule->next)
    {
        rule->caller_anode = NULL;
    }
}

static void free_inside_parse_state(YaepParseState *ps)
{
    free_core_symb_to_vect_lookup(ps);
    free_sets(ps);
    free_dotted_rules(ps);
}

static size_t memusage(YaepParseState *ps)
{
    size_t sum = 0;

    // Grammar...

    // Symbols, the memory usage for the symbols is static during the parse.
    sum += objstack_memusage(&ps->run.grammar->symbs_ptr->symbs_os);
    sum += vlo_memusage(&ps->run.grammar->symbs_ptr->symbs_vlo);
    sum += vlo_memusage(&ps->run.grammar->symbs_ptr->terminals_vlo);
    sum += vlo_memusage(&ps->run.grammar->symbs_ptr->nonterminals_vlo);
    sum += hash_table_memusage(ps->run.grammar->symbs_ptr->map_repr_to_symb);
    sum += hash_table_memusage(ps->run.grammar->symbs_ptr->map_code_to_symb);

    // Rules, the memory usage is static during the parse.
    sum += objstack_memusage(&ps->run.grammar->rulestorage_ptr->rules_os);

    // Terminal bitsets static.
    sum += objstack_memusage(&ps->run.grammar->term_sets_ptr->terminal_bitset_os);
    sum += vlo_memusage(&ps->run.grammar->term_sets_ptr->terminal_bitset_vlo);
    sum += hash_table_memusage(ps->run.grammar->term_sets_ptr->map_terminal_bitset_to_id);

    // Parse state, increasing during parse.

    sum += objstack_memusage(&ps->set_cores_os);
    sum += objstack_memusage(&ps->set_dotted_rules_os);
    sum += objstack_memusage(&ps->set_parent_dotted_rule_ids_os);
    sum += objstack_memusage(&ps->set_matched_lengths_os);
    sum += objstack_memusage(&ps->sets_os);
    sum += objstack_memusage(&ps->set_term_lookahead_os);

    sum += hash_table_memusage(ps->cache_stateset_cores);
    sum += hash_table_memusage(ps->cache_stateset_matched_lengths);
    sum += hash_table_memusage(ps->cache_stateset_core_matched_lengths);
    sum += hash_table_memusage(ps->cache_stateset_term_lookahead);

    sum += vlo_memusage(&ps->dotted_rules_table_vlo);
    sum += objstack_memusage(&ps->dotted_rules_os);
    sum += vlo_memusage(&ps->dotted_rule_matched_length_vec_vlo);
    sum += objstack_memusage(&ps->core_symb_to_predcomps_os);
    sum += vlo_memusage(&ps->new_core_symb_to_predcomps_vlo);
    sum += objstack_memusage(&ps->vect_ids_os);

#ifdef USE_CORE_SYMB_HASH_TABLE
    sum += hash_table_memusage(ps->map_core_symb_to_predcomps);
#else
    sum += vlo_memusage(&ps->core_symb_table_vlo);
    sum += objstack_memusage(&ps->core_symb_tab_rows);
#endif

    sum += hash_table_memusage(ps->map_transition_to_coresymbvect);
    sum += hash_table_memusage(ps->map_reduce_to_coresymbvect);
    sum += objstack_memusage(&ps->recovery_state_tail_sets);
    sum += vlo_memusage(&ps->original_state_set_tail_stack);
    sum += vlo_memusage(&ps->vlo_array);
    sum += hash_table_memusage(ps->set_of_reserved_memory);
    sum += vlo_memusage(&ps->tnodes_vlo);
    sum += hash_table_memusage(ps->map_node_to_visit);
    sum += objstack_memusage(&ps->node_visits_os);
    sum += vlo_memusage(&ps->recovery_state_stack);
    sum += objstack_memusage(&ps->parse_state_os);
    sum += hash_table_memusage(ps->map_rule_orig_statesetind_to_internalstate);

    return sum;
}

/* The following function reads all input tokens.*/
static void read_input(YaepParseState *ps)
{
    int code;
    void *attr;

    while((code = ps->run.read_token((YaepParseRun*)ps, &attr)) >= 0)
    {
        tok_add(ps, code, attr);
    }

    tok_add(ps, END_MARKER_CODE, NULL);
}

/* Add predicted (derived) not yet started dotted_rules which is formed from
   given start dotted_rule DOTTED_RULE with matched_length DIST by reducing symbol
   which can derivate empty string and which is placed after dot in
   given dotted_rule. */
static void complete_empty_nonterminals_in_rule(YaepParseState *ps,
                                                YaepDottedRule *dotted_rule,
                                                int dotted_rule_parent_id,
                                                bool only_nots)
{
    YaepRule *rule = dotted_rule->rule;
    int dyn_lookahead_context = dotted_rule->dyn_lookahead_context;

    for(int j = dotted_rule->dot_j; ; ++j)
    {
        if (!rule->rhs[j]) break;
        if (rule->rhs[j]->empty_p)
        {
            if (!only_nots)
            {
                YaepDottedRule *new_dotted_rule = create_dotted_rule(ps, rule, j+1, dyn_lookahead_context);
                set_add_dotted_rule_with_parent(ps, new_dotted_rule, dotted_rule_parent_id, "complete empty");
            }
        }
        else if (rule->rhs[j]->is_not_operator)
        {
            if (blocked_by_lookahead(ps, dotted_rule, rule->rhs[j], 1-only_nots, "lookahead1"))
            {
                break;
            }
            else
            {
                YaepDottedRule *new_dotted_rule = create_dotted_rule(ps, rule, j+1, dyn_lookahead_context);
                set_add_dotted_rule_with_parent(ps, new_dotted_rule, dotted_rule_parent_id, "complete lookahead ok pre");
            }
        }
        else
        {
            break;
        }
    }
}

static int mmin(int a, int b)
{
    if (a <= b) return a;
    return b;
}

static bool blocked_by_lookahead(YaepParseState *ps, YaepDottedRule *dotted_rule, YaepSymbol *symb, int n, const char *info)
{
    // Some empty rules encode a purpose in their names.
    // We have +"howdy" which becomes a rule |+howdy for insertions and
    // we have !"chapter" which becaomes a rule |!Schapter for not lookups if strings.
    // we have !!"chapter" which becomes a rule |?Schapter for required lookups if strings.
    // and ![L] which becomes |!CL for charsets
    // and ![Ls;'_-'] which becomes |![Ls;'_-']
    // and !#41 which becomes |!SA

    if (!symb) return false;
    if (!symb->is_not_operator) return false;

    // The rule is a NOT lookup rule that can potentially block the completion of this empty rule if the matching lookahead exists.
    bool is_blocked = has_lookahead(ps, symb, n);

    if (ps->run.debug)
    {
        MemBuffer *mb = new_membuffer();
        int tok_i = ps->tok_i+n;
        membuffer_printf(mb, "@%d NOT operator %s ", ps->tok_i, symb->repr);
        membuffer_printf(mb, "%s", info);
        if (is_blocked)
        {
            membuffer_printf(mb, " blocked: ");
        }
        else
        {
            membuffer_printf(mb, " ok ");
        }
        int to = mmin(ps->input_len, tok_i+strlen(symb->repr));
        for (int i = tok_i; i < to; ++i)
        {
            membuffer_printf(mb, "%s ", ps->input[i].symb->hr);
        }
        membuffer_append_null(mb);

        yaep_trace(ps, mb->buffer_);
        debug_step_blocked(ps, dotted_rule, 0, 0);

        free_membuffer_and_free_content(mb);
    }


    return is_blocked;
}

static bool has_lookahead(YaepParseState *ps, YaepSymbol *symb, int n)
{
    assert(symb->repr[0]== '|');
    assert(symb->repr[1]== '!');
    assert(symb->repr[2]== 'S' || symb->repr[2]== '[');

    int p = ps->tok_i+n;

    // End of buffer immediately returns no lookahead match.
    if (p >= ps->input_len) return false;
    // The last token is $eof returs no lookahead match.
    if (!strcmp(ps->input[p].symb->hr, "$eof")) return false;

    char type = symb->repr[2];
    if (type == 'S')
    {
        // Start scanning utf8 characters after the S: |!S...
        const char *u = symb->repr+3;
        const char *stop = symb->repr+strlen(symb->repr);
        for (int i = p; i < ps->input_len && u < stop; ++i)
        {
            YaepSymbol *next = ps->input[i].symb;
            if (!strcmp(next->hr, "$eof")) return false;
            int unc = 0;
            size_t len = 0;
            bool ok = decode_utf8(u, stop, &unc, &len);
            if (!ok)
            {
                fprintf(stderr, "Illegal utf8 encoding in not lookahead >%s<\n", symb->repr);
                exit(1);
            }
            if (next->u.terminal.code != unc)
            {
                // There is a mismatch between the lookahead and the actual content.
                // We can stop early and report failed lookahead.
                return false;
            }
            // Jump to the next character in the lookahead string.
            u += len;
        }
        // All characters matched the lookahead!
        return true;
    }

    // Charset lookahead.
    YaepSymbol *ys = symb_find_by_repr(ps, symb->repr+2);
    if (!ys)
    {
        // No charset exists, because no input characters matched this charset.
        // Easy, no possible lookahead match.
        return false;
    }
    YaepSymbol *next = ps->input[p].symb;
    bool match = terminal_bitset_test(ps, ys->u.nonterminal.first, next->u.terminal.term_id);

    return match;
}

static bool core_has_not_rules(YaepStateSetCore *c)
{
    for (int i = 0; i < c->num_started_dotted_rules; ++i)
    {
        YaepDottedRule *dotted_rule = c->dotted_rules[i];
        if (dotted_rule->rule->contains_not_operator) return true;
    }
    return false;
}

/* The following function adds the rest(predicted not-yet-started) dotted_rules to the
   new set and and forms triples(set_core, symbol, indexes) for
   further fast search of start dotted_rules from given core by
   transition on given symbol(see comment for abstract data `core_symb_to_predcomps'). */
static void expand_new_set(YaepParseState *ps)
{
    YaepSymbol *symb;
    YaepCoreSymbToPredComps *core_symb_to_predcomps;

    /* Look for dotted rules that can be progressed because the next non-terminal
       can be empty. I.e. we can complete the E immediately.
       S = E, 'a'.
       E = .
    */
    for (int leading_rule_index = 0;
         leading_rule_index < ps->new_num_leading_dotted_rules;
         leading_rule_index++)
    {
        YaepDottedRule *dotted_rule = ps->new_dotted_rules[leading_rule_index];
        complete_empty_nonterminals_in_rule(ps,
                                            dotted_rule,
                                            leading_rule_index,
                                            false);
    }

    for (int rule_index_in_core = 0;
         rule_index_in_core < ps->new_core->num_dotted_rules;
         rule_index_in_core++)
    {
        YaepDottedRule *dotted_rule = ps->new_dotted_rules[rule_index_in_core];

        // Check that there is a symbol after the dot!
        if (dotted_rule->dot_j < dotted_rule->rule->rhs_len)
        {
            // Yes.
            symb = dotted_rule->rule->rhs[dotted_rule->dot_j];
            core_symb_to_predcomps = core_symb_to_predcomps_find(ps, ps->new_core, symb);

            if (core_symb_to_predcomps)
            {
                //yaep_trace(ps, "re-using cspc%d[c%d %s]", core_symb_to_predcomps->id, ps->new_core->id, symb->hr);
            }
            else
            {
                // No vector found for this core+symb combo.
                // Add a new vector.
                core_symb_to_predcomps = core_symb_to_predcomps_new(ps, ps->new_core, symb);
                yaep_trace(ps, "new cspc%d [c%d %s]", core_symb_to_predcomps->id, ps->new_core->id, symb->hr);

                if (!symb->is_terminal)
                {
                    for (YaepRule *r = symb->u.nonterminal.rules; r != NULL; r = r->lhs_next)
                    {
                        YaepDottedRule *new_dotted_rule = create_dotted_rule(ps, r, 0, 0);
                        char buf[128];
                        snprintf(buf, 128, "d%d@%d predicts %s", dotted_rule->id, dotted_rule->dot_j, r->lhs->hr);
                        set_add_dotted_rule_no_match_yet(ps, new_dotted_rule, buf);
                    }
                }
            }
            // Add a prediction to the core+symb lookup that points to this dotted rule.
            // I.e. when we reach a certain symbol within this core, the we just find
            // a vector using the core+symb lookup. This vector stores all predicted dotted_rules
            // that should be added for further parsing.
            core_symb_to_predcomps_add_predict(ps, core_symb_to_predcomps, rule_index_in_core);

            // The non-terminal can be empty and this is a not-yet added dotted_rule.
            if (symb->empty_p && rule_index_in_core >= ps->new_core->num_all_matched_lengths)
            {
                int first = 1; //ps->new_set->id == 0 ? 0 : 1;
                if (!blocked_by_lookahead(ps, dotted_rule, dotted_rule->rule->rhs[dotted_rule->dot_j], first, "lookahead2a"))
                {
                    YaepDottedRule *nnnew_dotted_rule = create_dotted_rule(ps,
                                                                           dotted_rule->rule,
                                                                           dotted_rule->dot_j+1,
                                                                           0);
                    //  "complete_empty_rule_gurka");
                    // Add new rule to be handled in next loop iteration.
                    yaep_trace(ps, "complete empty rule %s", dotted_rule->rule->lhs->hr);
                    set_add_dotted_rule_no_match_yet(ps, nnnew_dotted_rule, "complete empty rule");
                }
            }
            if (symb->is_not_operator && rule_index_in_core >= ps->new_core->num_all_matched_lengths)
            {
                int first = ps->new_set->id == 0 ? 0 : 1;
                if (!blocked_by_lookahead(ps, dotted_rule, dotted_rule->rule->rhs[dotted_rule->dot_j], first, "lookahead2b"))
                {
                    YaepDottedRule *new_dotted_rule = create_dotted_rule(ps,
                                                                         dotted_rule->rule,
                                                                         dotted_rule->dot_j+1,
                                                                         0);
                    //"complete_lookahead_ok");
                    yaep_trace(ps, "complete lookahead ok");
                    set_add_dotted_rule_no_match_yet(ps, new_dotted_rule, "complete lookahead ok");
                }
            }
        }
    }

    for (int rule_index_in_core = 0;
         rule_index_in_core < ps->new_core->num_dotted_rules;
         rule_index_in_core++)
    {
        YaepDottedRule *dotted_rule = ps->new_dotted_rules[rule_index_in_core];

        // Is this dotted_rule complete? I.e. the dot is at its rightmost position?
        if (dotted_rule->dot_j != dotted_rule->rule->rhs_len) continue;

        // Yes, all rhs elements have been completed/scanned.
        symb = dotted_rule->rule->lhs;

        core_symb_to_predcomps = core_symb_to_predcomps_find(ps, ps->new_core, symb);
        if (core_symb_to_predcomps == NULL)
        {
            core_symb_to_predcomps = core_symb_to_predcomps_new(ps, ps->new_core, symb);
            yaep_trace(ps, "new cspc%d [c%d %s]", core_symb_to_predcomps->id, ps->new_core->id, symb->hr);
        }
        core_symb_to_predcomps_add_complete(ps, core_symb_to_predcomps, rule_index_in_core);
    }

    if (ps->run.grammar->lookahead_level > 1)
    {
        YaepDottedRule *new_dotted_rule, *shifted_dotted_rule;
        terminal_bitset_t *dyn_lookahead_context_set;
        int dyn_lookahead_context, j;
        bool changed_p;

        /* Now we have incorrect initial dotted_rules because their dyn_lookahead_context is not correct. */
        dyn_lookahead_context_set = terminal_bitset_create(ps);
        do
        {
            changed_p = false;
            for(int new_rule_index_in_core = ps->new_core->num_all_matched_lengths;
                new_rule_index_in_core < ps->new_core->num_dotted_rules;
                new_rule_index_in_core++)
            {
                terminal_bitset_clear(ps, dyn_lookahead_context_set);
                new_dotted_rule = ps->new_dotted_rules[new_rule_index_in_core];

                core_symb_to_predcomps = core_symb_to_predcomps_find(ps, ps->new_core, new_dotted_rule->rule->lhs);

                for (j = 0; j < core_symb_to_predcomps->predictions.len; j++)
                {
                    int rule_index_in_core = core_symb_to_predcomps->predictions.ids[j];
                    YaepDottedRule *dotted_rule = ps->new_dotted_rules[rule_index_in_core];
                    shifted_dotted_rule = create_dotted_rule(ps,
                                                             dotted_rule->rule,
                                                             dotted_rule->dot_j+1,
                                                             dotted_rule->dyn_lookahead_context);
                    //                                                             "expand_enss3");
                    terminal_bitset_or(ps, dyn_lookahead_context_set, shifted_dotted_rule->lookahead);
                }
                dyn_lookahead_context = terminal_bitset_insert(ps, dyn_lookahead_context_set);
                if (dyn_lookahead_context >= 0)
                {
                    dyn_lookahead_context_set = terminal_bitset_create(ps);
                }
                else
                {
                    dyn_lookahead_context = -dyn_lookahead_context - 1;
                }
                YaepDottedRule *dotted_rule = create_dotted_rule(ps,
                                                                 new_dotted_rule->rule,
                                                                 new_dotted_rule->dot_j,
                                                                 dyn_lookahead_context);
                // "expand_enss4");

                if (dotted_rule != new_dotted_rule)
                {
                    ps->new_dotted_rules[new_rule_index_in_core] = dotted_rule;
                    changed_p = true;
                }
            }
        }
        while(changed_p);
    }

    set_new_core_stop(ps);
    core_symb_to_predcomps_new_all_stop(ps);
}

static void build_start_set(YaepParseState *ps)
{
    int dyn_lookahead_context = 0;

    prepare_for_leading_dotted_rules(ps);

    if (ps->run.grammar->lookahead_level > 1)
    {
        terminal_bitset_t *empty_dyn_lookahead_context_set = terminal_bitset_create(ps);
        terminal_bitset_clear(ps, empty_dyn_lookahead_context_set);
        dyn_lookahead_context = terminal_bitset_insert(ps, empty_dyn_lookahead_context_set);

        /* Empty dyn_lookahead_context in the table has always number zero. */
        assert(dyn_lookahead_context == 0);
    }

    for (YaepRule *rule = ps->run.grammar->axiom->u.nonterminal.rules; rule != NULL; rule = rule->lhs_next)
    {
        YaepDottedRule *new_dotted_rule = create_dotted_rule(ps, rule, 0, dyn_lookahead_context);
        set_add_dotted_rule_with_matched_length(ps, new_dotted_rule, 0, "axiom");
    }

    bool core_added = convert_leading_dotted_rules_into_new_set(ps);
    assert(core_added);

    expand_new_set(ps);
    ps->state_sets[0] = ps->new_set;
}

static int lookup_matched_length(YaepParseState *ps, YaepStateSet *set, int rule_index_in_core)
{
    if (rule_index_in_core >= set->core->num_all_matched_lengths)
    {
        return 0;
    }
    if (rule_index_in_core < set->core->num_started_dotted_rules)
    {
        return set->matched_lengths[rule_index_in_core];
    }
    return set->matched_lengths[set->core->to_parent_rule_index[rule_index_in_core]];
}

static void trace_lookahead_predicts_no_match(YaepParseState *ps, int lookahead_term_id, YaepDottedRule *new_dotted_rule, const  char *info)
{
    MemBuffer *mb = new_membuffer();
    YaepSymbol *symb = symb_find_by_term_id(ps, lookahead_term_id);
    const char *hr = symb->hr;
    if (!symb) hr = "?";
    membuffer_printf(mb, "look bitset %s (%d) %s blocked by ", info, lookahead_term_id, hr);
    print_dotted_rule(mb, ps, ps->tok_i-1, new_dotted_rule, 0, 0);
    membuffer_append(mb, "\n");
}

void try_eat_token(const char *why, YaepParseState *ps, YaepStateSet *set,
                   YaepDottedRule *dotted_rule, int rule_index_in_core,
                   int lookahead_term_id, int local_lookahead_level,
                   int add_matched_length);

void try_eat_token(const char *why, YaepParseState *ps, YaepStateSet *set,
                   YaepDottedRule *dotted_rule, int rule_index_in_core,
                   int lookahead_term_id, int local_lookahead_level,
                   int add_matched_length)
{
    YaepDottedRule *new_dotted_rule = create_dotted_rule(ps,
                                                         dotted_rule->rule, dotted_rule->dot_j + 1,
                                                         dotted_rule->dyn_lookahead_context);
    if (false && local_lookahead_level != 0 &&
        !terminal_bitset_test(ps, new_dotted_rule->lookahead, lookahead_term_id) &&
        !terminal_bitset_test(ps, new_dotted_rule->lookahead, ps->run.grammar->term_error_id))
    {
        // Lookahead predicted no-match. Stop here.
        return;
    }

    int matched_length = lookup_matched_length(ps, set, rule_index_in_core);
    matched_length += add_matched_length;

    // This combo dotted_rule + matched_length did not already exist, lets add it.
    // But first test if there is a not lookahead that blocks....
    if (!blocked_by_lookahead(ps, new_dotted_rule, new_dotted_rule->rule->rhs[new_dotted_rule->dot_j], 1, why))
    {
        if (!dotted_rule_matched_length_test_and_set(ps, new_dotted_rule, matched_length))
        {
            set_add_dotted_rule_with_matched_length(ps, new_dotted_rule, matched_length, why);
        }
    }
}

void check_predicted_dotted_rules(YaepParseState *ps,
                                  YaepStateSet *set,
                                  YaepVect *predictions,
                                  int lookahead_term_id,
                                  int local_lookahead_level)
{
    for (int i = 0; i < predictions->len; i++)
    {
        int rule_index_in_core = predictions->ids[i];
        YaepDottedRule *dotted_rule = set->core->dotted_rules[rule_index_in_core];
        try_eat_token("scan", ps, set, dotted_rule, rule_index_in_core, lookahead_term_id, local_lookahead_level, 1);
    }
}

void check_leading_dotted_rules(YaepParseState *ps, YaepStateSet *set, int lookahead_term_id, int local_lookahead_level)
{
    for (int i = 0; i < ps->new_num_leading_dotted_rules; i++)
    {
        YaepDottedRule *new_dotted_rule = ps->new_dotted_rules[i];
        bool completed = new_dotted_rule->empty_tail_p;

        YaepSymbol *sym = new_dotted_rule->rule->rhs[new_dotted_rule->dot_j];
        if (!completed && sym && sym->is_not_operator
            && !blocked_by_lookahead(ps, new_dotted_rule, new_dotted_rule->rule->rhs[new_dotted_rule->dot_j], 1, "lookaheadbanan"))
        {
            completed = true;
        }

        // Note that empty_tail_p is both true if you reached the end of the rule
        // and if the rule can derive the empty string from the dot.
        if (completed)
        {
            /* All tail in new sitiation may derivate empty string so
               make reduce and add new dotted_rules. */
            int new_matched_length = ps->new_matched_lengths[i];
            int place = ps->state_set_k + 1 - new_matched_length;
            YaepStateSet *prev_set = ps->state_sets[place];
            YaepCoreSymbToPredComps *prev_core_symb_to_predcomps = core_symb_to_predcomps_find(ps, prev_set->core, new_dotted_rule->rule->lhs);
            if (prev_core_symb_to_predcomps == NULL)
            {
                assert(new_dotted_rule->rule->lhs == ps->run.grammar->axiom);
                continue;
            }
            for (int j = 0; j < prev_core_symb_to_predcomps->predictions.len; j++)
            {
                int rule_index_in_core = prev_core_symb_to_predcomps->predictions.ids[j];
                YaepDottedRule *dotted_rule = prev_set->core->dotted_rules[rule_index_in_core];
                try_eat_token("complete", ps, prev_set, dotted_rule, rule_index_in_core, lookahead_term_id, local_lookahead_level, new_matched_length);
            }
        }
    }
}

/* The following function predicts a new state set by shifting dotted_rules
   of SET given in CORE_SYMB_TO_PREDCOMPS with given lookahead terminal number.
   If the number is negative, we ignore lookahead at all. */
static void complete_and_predict_new_state_set(YaepParseState *ps,
                                               YaepStateSet *set,
                                               YaepCoreSymbToPredComps *core_symb_to_predcomps,
                                               YaepSymbol *THE_TERMINAL,
                                               YaepSymbol *NEXT_TERMINAL)
{
    int lookahead_term_id = NEXT_TERMINAL?NEXT_TERMINAL->u.terminal.term_id:-1;
    int local_lookahead_level = (lookahead_term_id < 0 ? 0 : ps->run.grammar->lookahead_level);

    prepare_for_leading_dotted_rules(ps);

    YaepVect *predictions = &core_symb_to_predcomps->predictions;

    clear_dotted_rule_matched_length_set(ps);

    check_predicted_dotted_rules(ps, set, predictions, lookahead_term_id, local_lookahead_level);
    check_leading_dotted_rules(ps, set, lookahead_term_id, local_lookahead_level);

    bool core_added = convert_leading_dotted_rules_into_new_set(ps);

    if (core_added)
    {
        expand_new_set(ps);
        ps->new_core->term = core_symb_to_predcomps->symb;
    }
}

/* This page contains error recovery code.  This code finds minimal
   cost error recovery.  The cost of error recovery is number of
   tokens ignored by error recovery.  The error recovery is successful
   when we match at least RECOVERY_TOKEN_MATCHES tokens.*/

/* The following function may be called if you know that state set has
   original sets upto LAST element(including it).  Such call can
   decrease number of restored sets.*/
static void set_original_set_bound(YaepParseState *ps, int last)
{
    assert(last >= 0 && last <= ps->recovery_start_set_k
            && ps->original_last_state_set_el <= ps->recovery_start_set_k);
    ps->original_last_state_set_el = last;
}

/* The following function guarantees that original state set tail sets
   starting with state_set_k(including the state) is saved.  The function
   should be called after any decreasing state_set_k with subsequent
   writing to state set [state_set_k]. */
static void save_original_sets(YaepParseState *ps)
{
    int length, curr_pl;

    assert(ps->state_set_k >= 0 && ps->original_last_state_set_el <= ps->recovery_start_set_k);
    length = VLO_LENGTH(ps->original_state_set_tail_stack) / sizeof(YaepStateSet*);

    for(curr_pl = ps->recovery_start_set_k - length; curr_pl >= ps->state_set_k; curr_pl--)
    {
        VLO_ADD_MEMORY(ps->original_state_set_tail_stack, &ps->state_sets[curr_pl],
                        sizeof(YaepStateSet*));

        if (ps->run.debug)
        {
            fprintf(stderr, "++++Save original set=%d\n", curr_pl);
            /*print_state_set(ps,
                            stderr,
                            ps->state_sets[curr_pl],
                            curr_pl);*/
            fprintf(stderr, "\n");
        }

    }
    ps->original_last_state_set_el = ps->state_set_k - 1;
}

/* If it is necessary, the following function restores original pl
   part with states in range [0, last_state_set_el]. */
static void restore_original_sets(YaepParseState *ps, int last_state_set_el)
{
    assert(last_state_set_el <= ps->recovery_start_set_k
            && ps->original_last_state_set_el <= ps->recovery_start_set_k);
    if (ps->original_last_state_set_el >= last_state_set_el)
    {
        ps->original_last_state_set_el = last_state_set_el;
        return;
    }
    for(;;)
    {
        ps->original_last_state_set_el++;
        ps->state_sets[ps->original_last_state_set_el]
            =((YaepStateSet**) VLO_BEGIN(ps->original_state_set_tail_stack))
            [ps->recovery_start_set_k - ps->original_last_state_set_el];

        if (ps->run.debug)
        {
            fprintf(stderr, "++++++Restore original set=%d\n", ps->original_last_state_set_el);
            /*print_state_set(ps, stderr, ps->state_sets[ps->original_last_state_set_el], ps->original_last_state_set_el);*/
            fprintf(stderr, "\n");
        }

        if (ps->original_last_state_set_el >= last_state_set_el)
            break;
    }
}

/* The following function looking backward in state set starting with element
   START_STATE_SET_EL and returns state set element which refers set with dotted_rule
   containing `. error'.  START_STATE_SET_EL should be non negative.
   Remember that zero state set set contains `.error' because we added such
   rule if it is necessary.  The function returns number of terminals
  (not taking error into account) on path(result, start_state_set_set].*/
static int find_error_state_set_set(YaepParseState *ps, int start_state_set_set, int*cost)
{
    int curr_pl;

    assert(start_state_set_set >= 0);
   *cost = 0;
    for(curr_pl = start_state_set_set; curr_pl >= 0; curr_pl--)
        if (core_symb_to_predcomps_find(ps, ps->state_sets[curr_pl]->core, ps->run.grammar->term_error) != NULL)
            break;
        else if (ps->state_sets[curr_pl]->core->term != ps->run.grammar->term_error)
           (*cost)++;
    assert(curr_pl >= 0);
    return curr_pl;
}

/* The following function creates and returns new error recovery state
   with charcteristics(LAST_ORIGINAL_STATE_SET_EL, BACKWARD_MOVE_COST,
   state_set_k, tok_i). */
static YaepRecoveryState new_recovery_state(YaepParseState *ps, int last_original_state_set_el, int backward_move_cost)
{
    YaepRecoveryState state;
    int i;

    assert(backward_move_cost >= 0);

    if (ps->run.debug)
    {
        fprintf(stderr, "++++Creating recovery state: original set=%d, tok=%d, ",
                last_original_state_set_el, ps->tok_i);
        // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
        fprintf(stderr, "\n");
    }

    state.last_original_state_set_el = last_original_state_set_el;
    state.state_set_tail_length = ps->state_set_k - last_original_state_set_el;
    assert(state.state_set_tail_length >= 0);
    for(i = last_original_state_set_el + 1; i <= ps->state_set_k; i++)
    {
        OS_TOP_ADD_MEMORY(ps->recovery_state_tail_sets, &ps->state_sets[i], sizeof(ps->state_sets[i]));

        if (ps->run.debug)
        {
            fprintf(stderr, "++++++Saving set=%d\n", i);
            /*print_state_set(ps, stderr, ps->state_sets[i], i);*/
            fprintf(stderr, "\n");
        }

    }
    state.state_set_tail =(YaepStateSet**) OS_TOP_BEGIN(ps->recovery_state_tail_sets);
    OS_TOP_FINISH(ps->recovery_state_tail_sets);
    state.start_tok = ps->tok_i;
    state.backward_move_cost = backward_move_cost;
    return state;
}

/* The following function creates new error recovery state and pushes
   it on the states stack top. */
static void push_recovery_state(YaepParseState *ps, int last_original_state_set_el, int backward_move_cost)
{
    YaepRecoveryState state;

    state = new_recovery_state(ps, last_original_state_set_el, backward_move_cost);

    if (ps->run.debug)
    {
        fprintf(stderr, "++++Push recovery state: original set=%d, tok=%d, ",
                 last_original_state_set_el, ps->tok_i);
        // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
        fprintf(stderr, "\n");
    }

    VLO_ADD_MEMORY(ps->recovery_state_stack, &state, sizeof(state));
}

/* The following function sets up parser state(pl, state_set_k, ps->tok_i)
   according to error recovery STATE. */
static void set_recovery_state(YaepParseState *ps, YaepRecoveryState*state)
{
    int i;

    ps->tok_i = state->start_tok;
    restore_original_sets(ps, state->last_original_state_set_el);
    ps->state_set_k = state->last_original_state_set_el;

    if (ps->run.debug)
    {
        fprintf(stderr, "++++Set recovery state: set=%d, tok=%d, ",
                 ps->state_set_k, ps->tok_i);
        // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
        fprintf(stderr, "\n");
    }

    for(i = 0; i < state->state_set_tail_length; i++)
    {
        ps->state_sets[++ps->state_set_k] = state->state_set_tail[i];

        if (ps->run.debug)
        {
            fprintf(stderr, "++++++Add saved set=%d\n", ps->state_set_k);
            /*print_state_set(ps, stderr, ps->state_sets[ps->state_set_k], ps->state_set_k);*/
            fprintf(stderr, "\n");
        }

    }
}

/* The following function pops the top error recovery state from
   states stack.  The current parser state will be setup according to
   the state. */
static YaepRecoveryState pop_recovery_state(YaepParseState *ps)
{
    YaepRecoveryState *state;

    state = &((YaepRecoveryState*) VLO_BOUND(ps->recovery_state_stack))[-1];
    VLO_SHORTEN(ps->recovery_state_stack, sizeof(YaepRecoveryState));

    if (ps->run.debug)
        fprintf(stderr, "++++Pop error recovery state\n");

    set_recovery_state(ps, state);
    return*state;
}

/* Return true if goto set SET from parsing list PLACE can be used as
   the next set.  The criterium is that all origin sets of start
   dotted_rules are the same as from PLACE. */
static bool can_transition_to_set(YaepParseState *ps, YaepStateSet*set, int place)
{
    int i, dist;
    int*matched_lengths = set->matched_lengths;

    for(i = set->core->num_started_dotted_rules - 1; i >= 0; i--)
    {
        if ((dist = matched_lengths[i]) <= 1)
            continue;
        /* Sets at origins of dotted_rules with matched_length one are supposed
           to be the same. */
        if (ps->state_sets[ps->state_set_k + 1 - dist] != ps->state_sets[place + 1 - dist])
            return false;
    }
    return true;
}

static int try_to_recover(YaepParseState *ps)
{
    int saved_tok_i, start, stop;

    /* Error recovery.  We do not check transition vector
       because for terminal transition vector is never NULL
       and reduce is always NULL. */

    saved_tok_i = ps->tok_i;
    if (ps->run.grammar->error_recovery_p)
    {
        fprintf(stderr, "Attempting error recovery...\n");
        error_recovery(ps, &start, &stop);
        ps->run.syntax_error(
            (YaepParseRun*)ps,
            saved_tok_i,
            ps->input[saved_tok_i].attr,
            start,
            ps->input[start].attr,
            stop,
            ps->input[stop].attr);
        return 1;
    }
    else
    {
        ps->run.failed_p = true;
        ps->run.syntax_error(
            (YaepParseRun*)ps,
            saved_tok_i,
            ps->input[saved_tok_i].attr,
            -1,
            NULL,
            -1,
            NULL);
        return 2;
    }

    return 0;
}

static YaepStateSetTermLookAhead *lookup_cached_set_term_lookahead(YaepParseState *ps,
                                                                   YaepSymbol *THE_TERMINAL,
                                                                   YaepSymbol *NEXT_TERMINAL,
                                                                   YaepStateSet *set)
{
    OS_TOP_EXPAND(ps->set_term_lookahead_os, sizeof(YaepStateSetTermLookAhead));

    YaepStateSetTermLookAhead *new_set_term_lookahead = (YaepStateSetTermLookAhead*)OS_TOP_BEGIN(ps->set_term_lookahead_os);
    new_set_term_lookahead->set = set;
    new_set_term_lookahead->term = THE_TERMINAL;
    new_set_term_lookahead->lookahead_term = NEXT_TERMINAL?NEXT_TERMINAL->u.terminal.term_id:-1;

    memset(new_set_term_lookahead->result, 0, MAX_CACHED_GOTO_RESULTS*sizeof(new_set_term_lookahead->result[0]));

    new_set_term_lookahead->curr = 0;
    // We write into the hashtable using the entry point! Yay!
    // I.e. there is no write hash table entry function.....
    YaepStateSetTermLookAhead **stlg = (YaepStateSetTermLookAhead**)find_hash_table_entry(ps->cache_stateset_term_lookahead, new_set_term_lookahead, true);

    if (*stlg != NULL)
    {
        YaepStateSet *s;

        OS_TOP_NULLIFY(ps->set_term_lookahead_os);
        for(int i = 0; i < MAX_CACHED_GOTO_RESULTS; i++)
        {
            s = (*stlg)->result[i];
            if (s == NULL) break;
            if (can_transition_to_set(ps, s, (*stlg)->place[i]))
            {
                ps->new_set = s;
                ps->n_goto_successes++;
                if (xmq_trace_enabled_)
                {
                    YaepSymbol *lookahead_symb = symb_find_by_term_id(ps, new_set_term_lookahead->lookahead_term);
                    const char *losymb = "";
                    if (lookahead_symb && lookahead_symb->hr) losymb = lookahead_symb->hr;

                    yaep_trace(ps, "found stlg [s%d %s %s] -> s%d",
                               new_set_term_lookahead->set->id,
                               new_set_term_lookahead->term->hr,
                               losymb,
                               ps->new_set->id);
                }
                break;
            }
        }
    }
    else
    {
        OS_TOP_FINISH(ps->set_term_lookahead_os);
        // Write the new_set_term_lookahead triplet into the hash table.
        *stlg = new_set_term_lookahead;
        ps->num_set_term_lookahead++;

        /*
        if (xmq_trace_enabled_)
        {
            YaepSymbol *lookahead_symb = symb_find_by_term_id(ps, new_set_term_lookahead->lookahead_term);
            const char *losymb = "";
            if (lookahead_symb && lookahead_symb->hr) losymb = lookahead_symb->hr;
            yaep_trace(ps, "create stlg [s%d %s %s]",
                       new_set_term_lookahead->set->id,
                       new_set_term_lookahead->term->hr,
                       losymb);
                       }*/
    }

    return *stlg;
}

/* Save(set, term, lookahead) -> new_set in the table. */
static void save_cached_set(YaepParseState *ps, YaepStateSetTermLookAhead *entry, YaepSymbol *NEXT_TERMINAL)
{
    int i = entry->curr;
    entry->result[i] = ps->new_set;
    entry->place[i] = ps->state_set_k;
    entry->lookahead_term = NEXT_TERMINAL ? NEXT_TERMINAL->u.terminal.term_id : -1;
    entry->curr = (i + 1) % MAX_CACHED_GOTO_RESULTS;

    if (xmq_trace_enabled_)
    {
        YaepSymbol *lookahead_symb = symb_find_by_term_id(ps, entry->lookahead_term);
        const char *losymb = "";
        if (lookahead_symb && lookahead_symb->hr) losymb = lookahead_symb->hr;
        yaep_trace(ps, "store stlg [s%d %s %s] -> s%d",
                   entry->set->id,
                   entry->term->hr,
                   losymb,
                   ps->new_set->id);
    }
}

static void perform_parse(YaepParseState *ps)
{
    yaep_debug(ps, "perform_parse()");
    error_recovery_init(ps);
    build_start_set(ps);

    if (ps->run.trace)
    {
        MemBuffer *mb = new_membuffer();
        print_state_set(mb, ps, ps->new_set, 0);
        debug_mb("ixml.pa.state=", mb);
        free_membuffer_and_free_content(mb);
    }

    ps->tok_i = 0;
    ps->state_set_k = 0;

    for(; ps->tok_i < ps->input_len; ps->tok_i++)
    {
        // This assert is TODO! Theoretically the state_set_k could be less than tok_i
        // assuming a state set has been reused.
        // So far I have not seen that, so assume state_set_k == tok_i == index of input char.
        assert(ps->tok_i == ps->state_set_k);

        YaepSymbol *THE_TERMINAL = ps->input[ps->tok_i].symb;
        YaepSymbol *NEXT_TERMINAL = NULL;

        if (ps->run.grammar->lookahead_level != 0 && ps->tok_i < ps->input_len-1)
        {
            NEXT_TERMINAL = ps->input[ps->tok_i + 1].symb;
        }

        assert(ps->tok_i == ps->state_set_k);

        if (xmq_verbose_enabled_ && ps->tok_i % 100000 == 0)
        {
            verbose_stats(ps);
        }
        debug("ixml.pa.token=", "@%d %s", ps->tok_i, THE_TERMINAL->hr);
        if (NEXT_TERMINAL && xmq_trace_enabled_)
        {
            yaep_view(ps, "READ %s next %s", THE_TERMINAL->hr, NEXT_TERMINAL->hr);
        }
        else
        {
            yaep_view(ps, "READ %s", THE_TERMINAL->hr);
        }

        YaepStateSet *set = ps->state_sets[ps->state_set_k];
        ps->new_set = NULL;

#ifdef USE_SET_HASH_TABLE
        // This command also adds the set to the hashtable if it does not already exist.
        // As a side effect it writes into ps->new_set
        YaepStateSetTermLookAhead *entry = lookup_cached_set_term_lookahead(ps, THE_TERMINAL, NEXT_TERMINAL, set);
#endif

        if (ps->new_set == NULL)
        {
            YaepCoreSymbToPredComps *core_symb_to_predcomps = core_symb_to_predcomps_find(ps, set->core, THE_TERMINAL);

            if (core_symb_to_predcomps == NULL)
            {
                int c = try_to_recover(ps);
                if (c == 1)
                {
                    continue;
                }
                else if (c == 2)
                {
                    break;
                }
            }

            /*
            if (ps->run.debug)
            {
                MemBuffer *mb = new_membuffer();
                membuffer_printf(mb, "input[%d]=", ps->tok_i);
                print_symbol(mb, THE_TERMINAL, true);
                membuffer_printf(mb, " s%d core%d -> csl%d", set->id, set->core->id, core_symb_to_predcomps->id);
                debug_mb("ixml.pa.scan=", mb);
                free_membuffer_and_free_content(mb);
                }*/

            // Do the actual predict/complete cycle.
            complete_and_predict_new_state_set(ps, set, core_symb_to_predcomps, THE_TERMINAL, NEXT_TERMINAL);

#ifdef USE_SET_HASH_TABLE
            save_cached_set(ps, entry, NEXT_TERMINAL);
#endif
        }

        ps->state_set_k++;
        ps->state_sets[ps->state_set_k] = ps->new_set;

        if (ps->run.trace)
        {
            MemBuffer *mb = new_membuffer();
            print_state_set(mb, ps, ps->new_set, ps->state_set_k);
            debug_mb("ixml.pa.state=", mb);
            free_membuffer_and_free_content(mb);
        }
    }
    free_error_recovery(ps);

    verbose_stats(ps);
}



static void *parse_alloc_default(int nmemb)
{
    void *result;

    assert(nmemb > 0);

    result = malloc(nmemb);
    if (result == NULL)
    {
        exit(1);
    }

    return result;
}

static void parse_free_default(void *mem)
{
    free(mem);
}

static void print_statistics(YaepParseState *ps, bool *ambiguous_p, int table_searches, int table_collisions)
{
    if (ps->run.debug)
    {
        yaep_debug(ps, "print_statistics()");
        yaep_trace(ps, "symbs_os=%zu", objstack_memusage(&ps->run.grammar->symbs_ptr->symbs_os));

        yaep_trace(ps, "input_len=%d #s=%d #dotted_rules=%d",
                   ps->input_len,
                   ps->num_sets_total,
                   ps->num_dotted_rules_total);

        yaep_trace(ps, "#terminals=%d #nonterms=%d %s",
                   ps->run.grammar->symbs_ptr->num_terminals,
                   ps->run.grammar->symbs_ptr->num_nonterminals,
                   *ambiguous_p ? "AMBIGUOUS " : "");

        yaep_trace(ps, "#terminals=%d #nonterms=%d %s",
                   ps->run.grammar->symbs_ptr->num_terminals,
                   ps->run.grammar->symbs_ptr->num_nonterminals,
                   *ambiguous_p ? "AMBIGUOUS " : "");
        yaep_trace(ps, "#rules=%d lengths=%d",
                 ps->run.grammar->rulestorage_ptr->num_rules,
                 ps->run.grammar->rulestorage_ptr->n_rhs_lens);
        yaep_trace(ps,  "#tokens=%d  #unique_dotted_rules=%d",
                   ps->input_len, ps->num_all_dotted_rules);
        yaep_trace(ps, "#terminal_sets=%d size=%d",
                 ps->run.grammar->term_sets_ptr->n_term_sets, ps->run.grammar->term_sets_ptr->n_term_sets_size);
        yaep_trace(ps, "#cores=%d #their start dotted_rules=%d",
                 ps->num_set_cores, ps->num_set_core_start_dotted_rules);
        yaep_trace(ps, "#parent indexes for some non start dotted_rules = %d",
                 ps->num_parent_dotted_rule_ids);
        yaep_trace(ps, "#unique set dist. vects = %d, their length = %d",
                 ps->num_set_matched_lengths, ps->num_set_matched_lengths_len);
        yaep_trace(ps, "#stl=%d goto_successes=%d",
                ps->num_set_term_lookahead, ps->n_goto_successes);
        yaep_trace(ps, "#cspc=%d cspc_vector_lengths=%d",
                 ps->n_core_symb_pairs, ps->n_core_symb_to_predcomps_len);
        yaep_trace(ps, "#unique transition vectors = %d, their length = %d",
                ps->n_transition_vects, ps->n_transition_vect_len);
        yaep_trace(ps, "#unique reduce vectors = %d, their length = %d",
                 ps->n_reduce_vects, ps->n_reduce_vect_len);
        yaep_trace(ps, "#term nodes = %d, #abstract nodes = %d",
                 ps->n_parse_term_nodes, ps->n_parse_abstract_nodes);
        yaep_trace(ps, "#alternative nodes = %d, #all nodes = %d",
                 ps->n_parse_alt_nodes,
                 ps->n_parse_term_nodes + ps->n_parse_abstract_nodes
                 + ps->n_parse_alt_nodes);
        if (table_searches == 0) table_searches++;
        yaep_trace(ps, "#table collisions = %.2g%%(%d out of %d)",
                 table_collisions* 100.0 / table_searches,
                 table_collisions, table_searches);
    }
}
/* The following function parses input according read grammar.
   ONE_PARSE_FLAG means build only one parse tree.  For unambiguous
   grammar the flag does not affect the result.  LA_LEVEL means usage
   of statik(if 1) or dynamic(2) lookahead to decrease size of sets.
   Static lookaheads gives the best results with the point of space
   and speed, dynamic ones does sligthly worse, and no usage of
   lookaheds does the worst.  D_LEVEL says what debugging information
   to output(it works only if we compiled without defined macro
   NO_YAEP_DEBUG_PRINT).  The function returns the error code(which
   will be also in error_code).  The function sets up
   *AMBIGUOUS_P if we found that the grammer is ambigous.
   (It works even we asked only one parse tree without alternatives.) */
int yaepParse(YaepParseRun *pr, YaepGrammar *g)
{
    YaepParseState *ps = (YaepParseState*)pr;

    assert(CHECK_PARSE_STATE_MAGIC(ps));

    ps->run.grammar = g;
    YaepTreeNode **root = &ps->run.root;
    bool *ambiguous_p = &ps->run.ambiguous_p;

    int code;
    bool tok_init_p, parse_init_p;

    /* Set up parse allocation*/
    if (ps->run.parse_alloc == NULL)
    {
        if (ps->run.parse_free != NULL)
        {
            /* Cannot allocate memory with a null function*/
            return YAEP_NO_MEMORY;
        }
        /* Set up defaults*/
        ps->run.parse_alloc = parse_alloc_default;
        ps->run.parse_free = parse_free_default;
    }

    assert(ps->run.grammar != NULL);
    *root = NULL;
    *ambiguous_p = false;
    pl_init(ps);
    tok_init_p = parse_init_p = false;

    if (!ps->run.read_token) ps->run.read_token = default_read_token;

    if ((code = setjmp(ps->error_longjump_buff)) != 0)
    {
        free_state_sets(ps);
        if (parse_init_p)
        {
            free_inside_parse_state(ps);
        }
        if (tok_init_p)
        {
            free_input(ps);
        }
        return code;
    }
    if (g->undefined_p)
    {
        yaep_error(ps, YAEP_UNDEFINED_OR_BAD_GRAMMAR, "undefined or bad grammar");
    }
    ps->n_goto_successes = 0;
    create_input(ps);
    tok_init_p = true;
    read_input(ps);
    yaep_parse_init(ps, ps->input_len);
    parse_init_p = true;
    allocate_state_sets(ps);

    int table_collisions_init = get_all_collisions();
    int table_searches_init = get_all_searches();

    // Perform a parse.
    perform_parse(ps);

    // Reconstruct a parse tree from the state sets.
    *root = build_parse_tree(ps, ambiguous_p);

    int table_collisions = get_all_collisions() - table_collisions_init;
    int table_searches = get_all_searches() - table_searches_init;

    print_statistics(ps, ambiguous_p, table_searches, table_collisions);

    free_state_sets(ps);
    free_inside_parse_state(ps);
    free_input(ps);
    verbose("ixml=", "done parse");
    return pr->failed_p;
}

/* The following function frees memory allocated for the grammar.*/
void yaepFreeGrammar(YaepParseRun *pr, YaepGrammar *g)
{
    YaepParseState *ps = (YaepParseState*)pr;
    assert(CHECK_PARSE_STATE_MAGIC(ps));

    YaepAllocator *allocator;

    if (g != NULL)
    {
        allocator = g->alloc;
        free_state_sets(ps);
        rulestorage_free(g, g->rulestorage_ptr);
        termsetstorage_free(g, g->term_sets_ptr);
        symbolstorage_free(ps, g->symbs_ptr);
        yaep_free(allocator, g);
        yaep_alloc_del(allocator);
    }
}

static void free_tree_reduce(YaepTreeNode *node)
{
    YaepTreeNodeType type;
    YaepTreeNode **childp;
    size_t numChildren, pos, freePos;

    assert(node != NULL);
    assert((node->type & _yaep_VISITED) == 0);

    type = node->type;
    node->type =(YaepTreeNodeType)(node->type | _yaep_VISITED);

    switch(type)
    {
    case YAEP_NIL:
    case YAEP_ERROR:
    case YAEP_TERM:
        break;

    case YAEP_ANODE:
        if (node->val.anode.name[0] == '\0')
        {
            /* We have already seen the node name*/
            node->val.anode.name = NULL;
        }
        else
        {
            /* Mark the node name as seen*/
            node->val._anode_name.name[0] = '\0';
        }
        for(numChildren = 0, childp = node->val.anode.children;
            *childp != NULL; ++numChildren, ++childp)
        {
            if ((*childp)->type & _yaep_VISITED)
            {
               *childp = NULL;
            }
            else
            {
                free_tree_reduce(*childp);
            }
        }
        /* Compactify children array*/
        for(freePos = 0, pos = 0; pos != numChildren; ++pos)
        {
            if (node->val.anode.children[pos] != NULL)
            {
                if (freePos < pos)
                {
                    node->val.anode.children[freePos] =
                        node->val.anode.children[pos];
                    node->val.anode.children[pos] = NULL;
                }
                ++freePos;
            }
        }
        break;

    case YAEP_ALT:
        if (node->val.alt.node->type & _yaep_VISITED)
        {
            node->val.alt.node = NULL;
        }
        else
        {
            free_tree_reduce(node->val.alt.node);
        }
        while((node->val.alt.next != NULL)
               &&(node->val.alt.next->type & _yaep_VISITED))
        {
            assert(node->val.alt.next->type ==(YAEP_ALT | _yaep_VISITED));
            node->val.alt.next = node->val.alt.next->val.alt.next;
        }
        if (node->val.alt.next != NULL)
        {
            assert((node->val.alt.next->type & _yaep_VISITED) == 0);
            free_tree_reduce(node->val.alt.next);
        }
        break;

    default:
        assert("This should not happen" == NULL);
    }
}

static void free_tree_sweep(YaepTreeNode *node,
                            void(*parse_free)(void*),
                            void(*termcb)(YaepTerminalNode*))
{
    YaepTreeNodeType type;
    YaepTreeNode *next;
    YaepTreeNode **childp;

    if (node == NULL)
    {
        return;
    }

    assert(node->type & _yaep_VISITED);
    type =(YaepTreeNodeType)(node->type & ~_yaep_VISITED);

    switch(type)
    {
    case YAEP_NIL:
    case YAEP_ERROR:
        break;

    case YAEP_TERM:
        if (termcb != NULL)
        {
            termcb(&node->val.terminal);
        }
        break;

    case YAEP_ANODE:
        parse_free(node->val._anode_name.name);
        for(childp = node->val.anode.children;*childp != NULL; ++childp)
        {
            free_tree_sweep(*childp, parse_free, termcb);
        }
        break;

    case YAEP_ALT:
        free_tree_sweep(node->val.alt.node, parse_free, termcb);
        next = node->val.alt.next;
        parse_free(node);
        free_tree_sweep(next, parse_free, termcb);
        return;                        /* Tail recursion*/

    default:
        assert("This should not happen" == NULL);
    }

    parse_free(node);
}

void yaepFreeTree(YaepTreeNode *root, void (*parse_free)(void*), void (*termcb)(YaepTerminalNode*))
{
    if (root == NULL)
    {
        return;
    }
    if (parse_free == NULL)
    {
        parse_free = parse_free_default;
    }

    /* Since the parse tree is actually a DAG, we must carefully avoid
     double free errors. Therefore, we walk the parse tree twice.
     On the first walk, we reduce the DAG to an actual tree.
     On the second walk, we recursively free the tree nodes. */
    free_tree_reduce(root);
    free_tree_sweep(root, parse_free, termcb);
}

static int default_read_token(YaepParseRun *ps, void **attr)
{
    *attr = NULL;
    if (ps->buffer_i >= ps->buffer_stop) return -1;

    int uc = 0;
    size_t len = 0;
    bool ok = decode_utf8(ps->buffer_i, ps->buffer_stop, &uc, &len);
    if (!ok)
    {
        fprintf(stderr, "xmq: broken utf8\n");
        exit(1);
    }
    ps->buffer_i += len;

    return uc;
}

/* The following function is major function of syntax error recovery.
   It searches for minimal cost error recovery.  The function returns
   in the parameter number of start token which is ignored and number
   of the first token which is not ignored.  If the number of ignored
   tokens is zero,*START will be equal to*STOP and number of token
   on which the error occurred.*/
static void error_recovery(YaepParseState *ps, int *start, int *stop)
{
    YaepStateSet*set;
    YaepCoreSymbToPredComps*core_symb_to_predcomps;
    YaepRecoveryState best_state, state;
    int best_cost, cost, num_matched_input;
    int back_to_frontier_move_cost, backward_move_cost;


    if (ps->run.verbose)
        fprintf(stderr, "\n++Error recovery start\n");

   *stop =*start = -1;
    OS_CREATE(ps->recovery_state_tail_sets, ps->run.grammar->alloc, 0);
    VLO_NULLIFY(ps->original_state_set_tail_stack);
    VLO_NULLIFY(ps->recovery_state_stack);
    ps->recovery_start_set_k = ps->state_set_k;
    ps->recovery_start_tok_i = ps->tok_i;
    /* Initialize error recovery state stack.*/
    ps->state_set_k = ps->back_state_set_frontier = find_error_state_set_set(ps, ps->state_set_k, &backward_move_cost);
    back_to_frontier_move_cost = backward_move_cost;
    save_original_sets(ps);
    push_recovery_state(ps, ps->back_state_set_frontier, backward_move_cost);
    best_cost = 2* ps->input_len;
    while(VLO_LENGTH(ps->recovery_state_stack) > 0)
    {
        state = pop_recovery_state(ps);
        cost = state.backward_move_cost;
        assert(cost >= 0);
        /* Advance back frontier.*/
        if (ps->back_state_set_frontier > 0)
        {
            int saved_state_set_k = ps->state_set_k;
            int saved_tok_i = ps->tok_i;

            /* Advance back frontier.*/
            ps->state_set_k = find_error_state_set_set(ps, ps->back_state_set_frontier - 1,
                                         &backward_move_cost);

            if (ps->run.debug)
                fprintf(stderr, "++++Advance back frontier: old=%d, new=%d\n",
                         ps->back_state_set_frontier, ps->state_set_k);

            if (best_cost >= back_to_frontier_move_cost + backward_move_cost)
            {
                ps->back_state_set_frontier = ps->state_set_k;
                ps->tok_i = ps->recovery_start_tok_i;
                save_original_sets(ps);
                back_to_frontier_move_cost += backward_move_cost;
                push_recovery_state(ps, ps->back_state_set_frontier,
                                    back_to_frontier_move_cost);
                set_original_set_bound(ps, state.last_original_state_set_el);
                ps->tok_i = saved_tok_i;
            }
            ps->state_set_k = saved_state_set_k;
        }
        /* Advance head frontier.*/
        if (best_cost >= cost + 1)
        {
            ps->tok_i++;
            if (ps->tok_i < ps->input_len)
            {

                if (ps->run.debug)
                {
                    fprintf(stderr,
                             "++++Advance head frontier(one pos): tok=%d, ",
                             ps->tok_i);
                    // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
                    fprintf(stderr, "\n");

                }
                push_recovery_state(ps, state.last_original_state_set_el, cost + 1);
            }
            ps->tok_i--;
        }
        set = ps->state_sets[ps->state_set_k];

        if (ps->run.debug)
        {
            fprintf(stderr, "++++Trying set=%d, tok=%d, ", ps->state_set_k, ps->tok_i);
            // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
            fprintf(stderr, "\n");
        }

        /* Shift error:*/
        core_symb_to_predcomps = core_symb_to_predcomps_find(ps, set->core, ps->run.grammar->term_error);
        assert(core_symb_to_predcomps != NULL);

        if (ps->run.debug)
            fprintf(stderr, "++++Making error shift in set=%d\n", ps->state_set_k);

        complete_and_predict_new_state_set(ps, set, core_symb_to_predcomps, NULL, NULL);
        ps->state_sets[++ps->state_set_k] = ps->new_set;

        if (ps->run.debug)
        {
            fprintf(stderr, "++Trying new set=%d\n", ps->state_set_k);
            // print_state_set(ps, stderr, ps->new_set, ps->state_set_k, ps->run.debug, ps->run.debug);
            fprintf(stderr, "\n");
        }

        /* Search the first right token.*/
        while(ps->tok_i < ps->input_len)
        {
            core_symb_to_predcomps = core_symb_to_predcomps_find(ps, ps->new_core, ps->input[ps->tok_i].symb);
            if (core_symb_to_predcomps != NULL)
                break;

            if (ps->run.debug)
            {
                fprintf(stderr, "++++++Skipping=%d ", ps->tok_i);
                // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
                fprintf(stderr, "\n");
            }

            cost++;
            ps->tok_i++;
            if (cost >= best_cost)
            {
                /* This state is worse.  Reject it.*/
                break;
            }
        }
        if (cost >= best_cost)
        {

            if (ps->run.debug)
            {
                fprintf(stderr, "++++Too many ignored tokens %d(already worse recovery)\n", cost);
            }

            /* This state is worse.  Reject it.*/
            continue;
        }
        if (ps->tok_i >= ps->input_len)
        {

            if (ps->run.debug)
            {
                fprintf(stderr, "++++We achieved EOF without matching -- reject this state\n");
            }

            /* Go to the next recovery state.  To guarantee that state set does
               not grows to much we don't push secondary error recovery
               states without matching in primary error recovery state.
               So we can say that state set length at most twice length of
               tokens array.*/
            continue;
        }

        /* Shift the found token.*/
        YaepSymbol *NEXT_TERMINAL = NULL;
        if (ps->tok_i + 1 < ps->input_len)
        {
            NEXT_TERMINAL = ps->input[ps->tok_i + 1].symb;
        }
        complete_and_predict_new_state_set(ps, ps->new_set, core_symb_to_predcomps, NULL, NEXT_TERMINAL);
        ps->state_sets[++ps->state_set_k] = ps->new_set;

        if (ps->run.debug)
        {
            fprintf(stderr, "++++++++Building new set=%d\n", ps->state_set_k);
            if (ps->run.debug)
            {
                // print_state_set(ps, stderr, ps->new_set, ps->state_set_k, ps->run.debug, ps->run.debug);
            }
        }

        num_matched_input = 0;
        for(;;)
        {

            if (ps->run.debug)
            {
                fprintf(stderr, "++++++Matching=%d ", ps->tok_i);
                // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
                fprintf(stderr, "\n");
            }

            num_matched_input++;
            if (num_matched_input >= ps->run.grammar->recovery_token_matches)
            {
                break;
            }
            ps->tok_i++;
            if (ps->tok_i >= ps->input_len)
            {
                break;
            }
            /* Push secondary recovery state(with error in set).*/
            if (core_symb_to_predcomps_find(ps, ps->new_core, ps->run.grammar->term_error) != NULL)
            {
                if (ps->run.debug)
                {
                    fprintf(stderr, "++++Found secondary state: original set=%d, tok=%d, ",
                            state.last_original_state_set_el, ps->tok_i);
                    // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
                    fprintf(stderr, "\n");
                }

                push_recovery_state(ps, state.last_original_state_set_el, cost);
            }
            core_symb_to_predcomps = core_symb_to_predcomps_find(ps, ps->new_core, ps->input[ps->tok_i].symb);
            if (core_symb_to_predcomps == NULL)
            {
                break;
            }
            YaepSymbol *NEXT_TERMINAL = NULL;
            if (ps->tok_i + 1 < ps->input_len)
            {
                NEXT_TERMINAL = ps->input[ps->tok_i + 1].symb;
            }
            complete_and_predict_new_state_set(ps, ps->new_set, core_symb_to_predcomps, NULL, NEXT_TERMINAL);
            ps->state_sets[++ps->state_set_k] = ps->new_set;
        }
        if (num_matched_input >= ps->run.grammar->recovery_token_matches || ps->tok_i >= ps->input_len)
        {
            /* We found an error recovery.  Compare costs.*/
            if (best_cost > cost)
            {

                if (ps->run.debug)
                {
                    fprintf(stderr, "++++Ignore %d tokens(the best recovery now): Save it:\n", cost);
                }
                best_cost = cost;
                if (ps->tok_i == ps->input_len)
                {
                    ps->tok_i--;
                }
                best_state = new_recovery_state(ps, state.last_original_state_set_el,
                                                 /* It may be any constant here
                                                    because it is not used.*/
                                                 0);
               *start = ps->recovery_start_tok_i - state.backward_move_cost;
               *stop = *start + cost;
            }
            else if (ps->run.debug)
            {
                fprintf(stderr, "++++Ignore %d tokens(worse recovery)\n", cost);
            }
        }
        else if (cost < best_cost && ps->run.debug)
            fprintf(stderr, "++++No %d matched tokens  -- reject this state\n",
                     ps->run.grammar->recovery_token_matches);

    }

    if (ps->run.debug)
        fprintf(stderr, "\n++Finishing error recovery: Restore best state\n");

    set_recovery_state(ps, &best_state);

    if (ps->run.debug)
    {
        fprintf(stderr, "\n++Error recovery end: curr token %d=", ps->tok_i);
        // TODO symbol_print(stderr, ps->input[ps->tok_i].symb, true);
        fprintf(stderr, ", Current set=%d:\n", ps->state_set_k);
        if (ps->run.debug)
        {
            /*print_state_set(ps, stderr, ps->state_sets[ps->state_set_k],
              ps->state_set_k, ps->run.debug, ps->run.debug);*/
        }
    }

    OS_DELETE(ps->recovery_state_tail_sets);
}

/* Initialize work with error recovery.*/
static void error_recovery_init(YaepParseState *ps)
{
    VLO_CREATE(ps->original_state_set_tail_stack, ps->run.grammar->alloc, 4096);
    VLO_CREATE(ps->recovery_state_stack, ps->run.grammar->alloc, 4096);
}

/* Finalize work with error recovery.*/
static void free_error_recovery(YaepParseState *ps)
{
    VLO_DELETE(ps->recovery_state_stack);
    VLO_DELETE(ps->original_state_set_tail_stack);
}

#endif // YAEP_MODULE


#endif
