]> granicus.if.org Git - ejabberd/commitdiff
* src/stringprep/: Support for stringprep (not completed yet)
authorAlexey Shchepin <alexey@process-one.net>
Fri, 26 Sep 2003 18:55:01 +0000 (18:55 +0000)
committerAlexey Shchepin <alexey@process-one.net>
Fri, 26 Sep 2003 18:55:01 +0000 (18:55 +0000)
* src/mod_muc/mod_muc.erl: Replaced io:format calls to ?DEBUG ones

SVN Revision: 141

ChangeLog
src/mod_muc/mod_muc.erl
src/stringprep/Makefile [new file with mode: 0644]
src/stringprep/stringprep.erl [new file with mode: 0644]
src/stringprep/stringprep_drv.c [new file with mode: 0644]
src/stringprep/uni_data.c [new file with mode: 0644]
src/stringprep/uni_parse.tcl [new file with mode: 0644]

index 9060f845acb4ca717e993e86826d55ad1047ecfd..e11036721a130e0c67f15779ef9084d86f0f55da 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2003-09-26  Alexey Shchepin  <alexey@sevcom.net>
+
+       * src/stringprep/: Support for stringprep (not completed yet)
+
+2003-09-24  Alexey Shchepin  <alexey@sevcom.net>
+
+       * src/mod_muc/mod_muc.erl: Replaced io:format calls to ?DEBUG ones
+
 2003-09-19  Alexey Shchepin  <alexey@sevcom.net>
 
        * src/mod_muc/mod_muc_room.erl: Debug output switched off
index 2954eaa5a21aa2d5fe811d4e7135adb8a90ea77a..ce1328ca6cb7cfae3b08aabc7b29fdf8b529f288 100644 (file)
@@ -180,7 +180,7 @@ do_route(Host, From, To, Packet) ->
                    Type = xml:get_attr_s("type", Attrs),
                    case {Name, Type} of
                        {"presence", ""} ->
-                           io:format("MUC: open new room '~s'~n", [Room]),
+                           ?DEBUG("MUC: open new room '~s'~n", [Room]),
                            {ok, Pid} = mod_muc_room:start(
                                          Host, Room, From, Nick),
                            ets:insert(
@@ -195,7 +195,7 @@ do_route(Host, From, To, Packet) ->
                    end;
                [R] ->
                    Pid = R#muc_online_room.pid,
-                   io:format("MUC: send to process ~p~n", [Pid]),
+                   ?DEBUG("MUC: send to process ~p~n", [Pid]),
                    mod_muc_room:route(Pid, From, Nick, Packet),
                    ok
            end
diff --git a/src/stringprep/Makefile b/src/stringprep/Makefile
new file mode 100644 (file)
index 0000000..3f48c10
--- /dev/null
@@ -0,0 +1,42 @@
+# $Id$
+
+include ../Makefile.inc
+
+INCLUDES = -I$(ERLANG_DIR)/usr/include \
+          -I$(EI_DIR)/include \
+          -I/usr/local/include
+
+LIBDIRS = -L$(EI_DIR)/lib -L/usr/local/lib
+
+ERLSHLIBS = ../stringprep_drv.so
+
+
+
+OUTDIR = ..
+EFLAGS = -I .. -pz ..
+OBJS   = \
+       $(OUTDIR)/stringprep.beam
+
+all:    $(OBJS) $(ERLSHLIBS)
+
+$(OUTDIR)/%.beam:       %.erl
+       erlc -W $(EFLAGS) -o $(OUTDIR) $<
+
+
+
+#all:  $(ERLSHLIBS)
+#      erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt
+
+$(ERLSHLIBS):  ../%.so:        %.c
+                       gcc -Wall $(INCLUDES) $(LIBDIRS) \
+                       $(subst ../,,$(subst .so,.c,$@)) \
+                       -lerl_interface \
+                       -lei \
+                       -o $@ -fpic -shared \
+
+clean:
+       rm -f *.beam
+
+TAGS:
+       etags *.erl
+
diff --git a/src/stringprep/stringprep.erl b/src/stringprep/stringprep.erl
new file mode 100644 (file)
index 0000000..919ae7a
--- /dev/null
@@ -0,0 +1,74 @@
+%%%----------------------------------------------------------------------
+%%% File    : stringprep.erl
+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
+%%% Purpose : Interface to stringprep_drv
+%%% Created : 16 Feb 2003 by Alexey Shchepin <alexey@sevcom.net>
+%%% Id      : $Id$
+%%%----------------------------------------------------------------------
+
+-module(stringprep).
+-author('alexey@sevcom.net').
+-vsn('$Revision$ ').
+
+-behaviour(gen_server).
+
+-export([start/0, start_link/0, tolower/1]).
+
+%% Internal exports, call-back functions.
+-export([init/1,
+        handle_call/3,
+        handle_cast/2,
+        handle_info/2,
+        code_change/3,
+        terminate/2]).
+
+
+
+start() ->
+    gen_server:start({local, ?MODULE}, ?MODULE, [], []).
+
+start_link() ->
+    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+init([]) ->
+    ok = erl_ddll:load_driver(".", stringprep_drv),
+    Port = open_port({spawn, stringprep_drv}, []),
+    ets:new(stringprep_table, [set, public, named_table]),
+    ets:insert(stringprep_table, {port, Port}),
+    {ok, Port}.
+
+
+%%% --------------------------------------------------------
+%%% The call-back functions.
+%%% --------------------------------------------------------
+
+handle_call(_, _, State) ->
+    {noreply, State}.
+
+handle_cast(_, State) ->
+    {noreply, State}.
+
+handle_info({'EXIT', Pid, Reason}, Port) ->
+    {noreply, Port};
+
+handle_info({'EXIT', Port, Reason}, Port) ->
+    {stop, {port_died, Reason}, Port};
+handle_info(_, State) ->
+    {noreply, State}.
+
+code_change(OldVsn, State, Extra) ->
+    {ok, State}.
+
+terminate(_Reason, Port) ->
+    Port ! {self, close},
+    ok.
+
+
+
+tolower(String) ->
+    [{port, Port} | _] = ets:lookup(stringprep_table, port),
+    Res = port_control(Port, 1, String),
+    binary_to_list(Res).
+
+
+
diff --git a/src/stringprep/stringprep_drv.c b/src/stringprep/stringprep_drv.c
new file mode 100644 (file)
index 0000000..e6eb3d3
--- /dev/null
@@ -0,0 +1,147 @@
+/* $Id$ */
+
+#include <stdio.h>
+#include <erl_driver.h>
+#include <ei.h>
+#include <iconv.h>
+
+#include "uni_data.c"
+
+typedef struct {
+      ErlDrvPort port;
+} stringprep_data;
+
+
+static ErlDrvData stringprep_erl_start(ErlDrvPort port, char *buff)
+{
+   stringprep_data* d = (stringprep_data*)driver_alloc(sizeof(stringprep_data));
+   d->port = port;
+
+   set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
+   
+   return (ErlDrvData)d;
+}
+
+static void stringprep_erl_stop(ErlDrvData handle)
+{
+   driver_free((char*)handle);
+}
+
+static int stringprep_erl_control(ErlDrvData drv_data,
+                                 unsigned int command,
+                                 char *buf, int len,
+                                 char **rbuf, int rlen)
+{
+   int i, j=0;
+   unsigned char c;
+   int bad = 0;
+   int uc, ruc;
+   int size;
+   int info;
+   ErlDrvBinary *b;
+   char *rstring;
+
+   size = len;
+
+   rstring = malloc(size);
+
+   for(i=0; i < len; i++)
+   {
+      c = buf[i];
+      if(c < 0x80) {
+        uc = c;
+      } else if(c < 0xC0) {
+        bad = 1;
+      } else if(c < 0xE0) {
+        if(i+1 < len && (buf[i+1] & 0xC0) == 0x80) {
+           uc = ((c & 0x1F) << 6) | (buf[i+1] & 0x3F);
+           i++;
+        } else {
+           bad = 1;
+        }
+      } else if(c < 0xF0) {
+        if(i+2 < len && (buf[i+1] & 0xC0) == 0x80 &&
+           (buf[i+2] & 0xC0) == 0x80) {
+           uc = ((c & 0x1F) << 12) | ((buf[i+1] & 0x1F) << 6)
+              | (buf[i+2] & 0x3F);
+           i += 2;
+        } else {
+           bad = 1;
+        }
+      } else {
+        // TODO
+        bad = 1;
+      }
+
+      if(bad) {
+        *rbuf = (char*)(b = driver_alloc_binary(1));
+        b->orig_bytes[0] = 0;
+        free(rstring);
+        return 1;
+      }
+      
+      
+      info = GetUniCharInfo(uc);
+      ruc = uc + GetDelta(info);
+
+      if(ruc < 0x80) {
+        if(j >= size) {
+           size = 2*size + 1;
+           rstring = realloc(rstring, size);
+        }
+        rstring[j] = (char) ruc;
+        j++;
+      } else if(ruc < 0x7FF) {
+        if(j >= size) {
+           size = 2*size + 2;
+           rstring = realloc(rstring, size);
+        }
+        rstring[j] = (char) ((ruc >> 6) | 0xC0);
+        rstring[j+1] = (char) ((ruc | 0x80) & 0xBF);
+        j += 2;
+      } else if(ruc < 0xFFFF) {
+        if(j >= size) {
+           size = 2*size + 3;
+           rstring = realloc(rstring, size);
+        }
+        rstring[j] = (char) ((ruc >> 12) | 0xE0);
+        rstring[j+1] = (char) (((ruc >> 6) | 0x80) & 0xBF);
+        rstring[j+2] = (char) ((ruc | 0x80) & 0xBF);
+        j += 3;
+      }
+   }
+   
+   
+   
+   *rbuf = (char*)(b = driver_alloc_binary(j));
+   memcpy(b->orig_bytes, rstring, j);
+   free(rstring);
+   
+   return j;
+}
+
+
+
+ErlDrvEntry stringprep_driver_entry = {
+   NULL,                       /* F_PTR init, N/A */
+   stringprep_erl_start,          /* L_PTR start, called when port is opened */
+   stringprep_erl_stop,           /* F_PTR stop, called when port is closed */
+   NULL,         /* F_PTR output, called when erlang has sent */
+   NULL,                       /* F_PTR ready_input, called when input descriptor ready */
+   NULL,                       /* F_PTR ready_output, called when output descriptor ready */
+   "stringprep_drv",              /* char *driver_name, the argument to open_port */
+   NULL,                       /* F_PTR finish, called when unloaded */
+   NULL,                       /* handle */
+   stringprep_erl_control,          /* F_PTR control, port_command callback */
+   NULL,                       /* F_PTR timeout, reserved */
+   NULL                        /* F_PTR outputv, reserved */
+};
+
+#ifdef WIN32
+__declspec(dllexport)
+#endif
+DRIVER_INIT(stringprep_erl) /* must match name in driver_entry */
+{
+   return &stringprep_driver_entry;
+}
+
diff --git a/src/stringprep/uni_data.c b/src/stringprep/uni_data.c
new file mode 100644 (file)
index 0000000..5c94e7d
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * uni_data.c --
+ *
+ *     Declarations of Unicode character information tables.  This file is
+ *     automatically generated by the uni_parse.tcl script.  Do not
+ *     modify this file by hand.
+ *
+ * Copyright (c) 1998 by Scriptics Corporation.
+ * All rights reserved.
+ *
+ * Modified for ejabberd by Alexey Shchepin
+ *
+ * RCS: @(#) $Id$
+ */
+
+/*
+ * A 16-bit Unicode character is split into two parts in order to index
+ * into the following tables.  The lower OFFSET_BITS comprise an offset
+ * into a page of characters.  The upper bits comprise the page number.
+ */
+
+#define OFFSET_BITS 5
+
+/*
+ * The pageMap is indexed by page number and returns an alternate page number
+ * that identifies a unique page of characters.  Many Unicode characters map
+ * to the same alternate page number.
+ */
+
+static unsigned char pageMap[] = {
+    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 8, 16, 17, 18, 
+    18, 19, 20, 21, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 18, 8, 31, 
+    8, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 46, 
+    47, 48, 49, 50, 51, 4, 46, 52, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 53, 54, 
+    55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 57, 69, 70, 
+    71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 79, 80, 83, 84, 79, 
+    85, 86, 87, 88, 37, 89, 90, 4, 91, 92, 93, 4, 94, 95, 96, 97, 98, 99, 
+    100, 4, 18, 101, 102, 4, 4, 18, 103, 104, 18, 18, 105, 18, 18, 106, 
+    18, 107, 108, 18, 109, 18, 110, 111, 112, 113, 111, 18, 114, 115, 4, 
+    18, 18, 116, 37, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 117, 118, 18, 18, 119, 120, 121, 122, 123, 18, 
+    124, 125, 126, 127, 18, 18, 128, 18, 129, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 130, 8, 8, 131, 132, 133, 
+    134, 135, 18, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 51, 
+    146, 147, 148, 149, 150, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 
+    22, 151, 18, 152, 153, 22, 143, 4, 22, 154, 51, 22, 155, 156, 157, 
+    158, 22, 22, 22, 22, 22, 22, 22, 22, 159, 22, 22, 160, 161, 4, 4, 4, 
+    162, 163, 164, 165, 166, 167, 145, 168, 22, 22, 22, 22, 22, 22, 22, 
+    22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 169, 22, 22, 170, 22, 22, 22, 22, 22, 22, 171, 4, 172, 
+    173, 37, 18, 174, 175, 18, 176, 177, 178, 18, 18, 113, 128, 4, 17, 
+    179, 18, 180, 181, 18, 182, 183, 184, 18, 18, 18, 185, 18, 18, 186, 
+    184, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 187, 4, 4, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 188, 4, 4, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    189, 22, 154, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+    18, 18, 18, 18, 190, 4, 4, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 191, 
+    191, 191, 191, 191, 18, 18, 18, 18, 18, 18, 18, 18, 18, 192, 18, 193, 
+    4, 4, 4, 4, 194, 195, 196, 46, 46, 197, 198, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 199, 200, 46, 201, 46, 202, 203, 204, 205, 206, 207, 
+    46, 46, 46, 208, 165, 209, 210, 211, 18, 184, 212, 213
+};
+
+/*
+ * The groupMap is indexed by combining the alternate page number with
+ * the page offset and returns a group number that identifies a unique
+ * set of character attributes.
+ */
+
+static unsigned char groupMap[] = {
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 
+    2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 
+    2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, 7, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 
+    2, 2, 5, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 
+    5, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 
+    9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 
+    5, 9, 5, 9, 5, 5, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 
+    5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 
+    5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 
+    9, 5, 9, 5, 9, 5, 9, 5, 10, 9, 5, 9, 5, 9, 5, 11, 5, 12, 9, 5, 9, 5, 
+    13, 9, 5, 14, 14, 9, 5, 5, 15, 16, 17, 9, 5, 14, 18, 5, 19, 20, 9, 
+    5, 5, 5, 19, 21, 5, 22, 9, 5, 9, 5, 9, 5, 23, 9, 5, 23, 5, 5, 9, 5, 
+    23, 9, 5, 24, 24, 9, 5, 9, 5, 25, 9, 5, 5, 5, 9, 5, 5, 5, 5, 5, 5, 
+    5, 26, 9, 5, 26, 9, 5, 26, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 
+    9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 
+    5, 26, 9, 5, 9, 5, 27, 28, 9, 5, 9, 5, 9, 5, 9, 5, 29, 6, 9, 5, 9, 
+    5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 30, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 
+    6, 6, 5, 6, 6, 6, 2, 6, 6, 6, 6, 6, 2, 2, 31, 2, 32, 32, 32, 6, 33, 
+    6, 34, 34, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 9, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 35, 
+    36, 5, 5, 5, 37, 38, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 
+    5, 9, 5, 9, 5, 9, 5, 9, 5, 39, 40, 41, 5, 42, 43, 2, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 
+    44, 44, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 9, 5, 5, 2, 2, 2, 2, 6, 2, 2, 9, 5, 9, 5, 9, 5, 9, 5, 
+    9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, 9, 5, 9, 5, 9, 5, 9, 5, 
+    9, 5, 9, 5, 9, 5, 6, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 
+    9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 6, 
+    6, 9, 5, 6, 6, 6, 6, 6, 6, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 
+    9, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 45, 45, 45, 45, 45, 45, 45, 45, 
+    45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 
+    45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 6, 6, 5, 5, 5, 
+    5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 
+    2, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 6, 2, 2, 2, 46, 2, 46, 2, 2, 46, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 6, 6, 6, 6, 6, 46, 46, 
+    46, 46, 46, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 46, 6, 6, 
+    6, 46, 6, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 6, 6, 6, 6, 6, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    46, 46, 46, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 2, 2, 2, 2, 2, 2, 2, 47, 2, 2, 2, 2, 2, 2, 2, 46, 
+    46, 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 46, 46, 
+    46, 46, 46, 6, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 6, 6, 46, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 6, 6, 6, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 46, 46, 46, 46, 46, 46, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 46, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 5, 6, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 6, 6, 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 2, 6, 
+    6, 5, 2, 2, 2, 2, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 2, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 6, 6, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 
+    5, 5, 5, 5, 5, 5, 6, 5, 6, 6, 6, 5, 5, 5, 5, 6, 6, 2, 6, 5, 5, 5, 2, 
+    2, 2, 2, 6, 6, 5, 5, 6, 6, 5, 5, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 
+    6, 6, 6, 5, 5, 6, 5, 5, 5, 2, 2, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 2, 6, 6, 5, 5, 
+    5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 6, 
+    5, 5, 6, 5, 5, 6, 6, 2, 6, 5, 5, 5, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 2, 
+    2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 
+    6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 2, 2, 5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 5, 
+    5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 6, 5, 5, 5, 5, 5, 6, 6, 2, 5, 5, 
+    5, 5, 2, 2, 2, 2, 2, 6, 2, 2, 5, 6, 5, 5, 2, 6, 6, 5, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 6, 6, 5, 5, 5, 5, 6, 
+    6, 2, 5, 5, 2, 5, 2, 2, 2, 6, 6, 6, 5, 5, 6, 6, 5, 5, 2, 6, 6, 6, 6, 
+    6, 6, 6, 6, 2, 5, 6, 6, 6, 6, 5, 5, 6, 5, 5, 5, 6, 6, 6, 6, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 2, 5, 6, 5, 5, 5, 5, 5, 5, 6, 6, 6, 5, 5, 5, 6, 5, 5, 5, 5, 6, 
+    6, 6, 5, 5, 6, 5, 6, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 5, 6, 6, 6, 
+    5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 6, 6, 6, 5, 5, 2, 5, 5, 6, 6, 
+    6, 5, 5, 5, 6, 5, 5, 5, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 6, 5, 5, 5, 5, 5, 
+    5, 5, 5, 6, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 
+    5, 5, 6, 6, 6, 6, 2, 2, 2, 5, 5, 5, 5, 6, 2, 2, 2, 6, 2, 2, 2, 2, 6, 
+    6, 6, 6, 6, 6, 6, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 6, 6, 6, 6, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 2, 5, 5, 
+    5, 5, 5, 6, 2, 5, 5, 6, 5, 5, 2, 2, 6, 6, 6, 6, 6, 6, 6, 5, 5, 6, 6, 
+    6, 6, 6, 6, 6, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 5, 2, 2, 2, 6, 6, 5, 
+    5, 5, 6, 5, 5, 5, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 6, 5, 5, 5, 
+    5, 5, 5, 5, 6, 6, 6, 2, 6, 6, 6, 6, 5, 5, 5, 2, 2, 2, 6, 2, 6, 5, 5, 
+    5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 
+    2, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 5, 5, 6, 5, 6, 6, 5, 5, 6, 5, 6, 6, 
+    5, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 
+    6, 5, 6, 5, 6, 6, 5, 5, 6, 5, 5, 5, 5, 2, 5, 5, 2, 2, 2, 2, 2, 2, 6, 
+    2, 2, 5, 6, 6, 5, 5, 5, 5, 5, 6, 5, 6, 2, 2, 2, 2, 2, 2, 6, 6, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 
+    2, 5, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 5, 2, 2, 2, 2, 2, 5, 2, 2, 5, 5, 5, 5, 6, 6, 6, 6, 2, 2, 2, 
+    2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 5, 5, 5, 
+    5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 6, 5, 5, 5, 5, 5, 6, 5, 5, 6, 5, 2, 2, 
+    2, 2, 5, 2, 6, 6, 6, 2, 2, 5, 2, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 6, 6, 6, 
+    6, 6, 6, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 6, 6, 6, 6, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 6, 6, 6, 6, 6, 5, 5, 5, 5, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 
+    6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 5, 
+    5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 5, 5, 5, 6, 6, 5, 5, 5, 
+    5, 5, 5, 5, 6, 5, 6, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 
+    6, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 5, 5, 5, 6, 6, 
+    5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 2, 2, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 
+    2, 2, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 2, 2, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 
+    2, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 2, 5, 
+    6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 7, 2, 2, 2, 2, 
+    7, 7, 7, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 
+    6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 5, 9, 5, 9, 5, 9, 
+    5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 5, 5, 5, 5, 5, 48, 6, 
+    6, 6, 6, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 
+    9, 5, 9, 5, 9, 5, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 49, 49, 
+    49, 49, 49, 49, 49, 49, 5, 5, 5, 5, 5, 5, 6, 6, 49, 49, 49, 49, 49, 
+    49, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 49, 49, 49, 49, 49, 49, 49, 49, 5, 
+    5, 5, 5, 5, 5, 5, 5, 49, 49, 49, 49, 49, 49, 49, 49, 5, 5, 5, 5, 5, 
+    5, 6, 6, 49, 49, 49, 49, 49, 49, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 49, 
+    6, 49, 6, 49, 6, 49, 5, 5, 5, 5, 5, 5, 5, 5, 49, 49, 49, 49, 49, 49, 
+    49, 49, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 49, 49, 
+    50, 50, 5, 2, 51, 2, 2, 2, 5, 5, 5, 6, 5, 5, 52, 52, 52, 52, 5, 2, 
+    2, 2, 5, 5, 5, 5, 6, 6, 5, 5, 49, 49, 53, 53, 6, 2, 2, 2, 5, 5, 5, 
+    5, 5, 5, 5, 5, 49, 49, 54, 54, 55, 2, 2, 2, 6, 6, 5, 5, 5, 6, 5, 5, 
+    56, 56, 57, 57, 5, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 58, 58, 
+    58, 59, 47, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 6, 6, 6, 6, 2, 6, 6, 6, 6, 6, 6, 6, 6, 58, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 5, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 5, 2, 60, 
+    2, 5, 2, 61, 62, 5, 5, 2, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 2, 6, 6, 
+    5, 5, 5, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 2, 2, 6, 6, 6, 6, 6, 6, 6, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 63, 
+    63, 63, 63, 63, 63, 63, 63, 63, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 64, 64, 64, 64, 64, 64, 64, 64, 64, 
+    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 
+    6, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 6, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 6, 2, 2, 2, 2, 6, 6, 6, 
+    2, 6, 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 2, 2, 2, 2, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 2, 2, 2, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 
+    6, 2, 2, 2, 2, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 
+    5, 5, 5, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 6, 6, 6, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 
+    6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 
+    6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 59, 59, 59, 59, 59, 59, 59, 
+    59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 
+    59, 59, 59, 59, 59, 59, 59, 59, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 46, 2, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 2, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 6, 46, 46, 46, 46, 46, 6, 46, 6, 46, 46, 6, 46, 46, 6, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 6, 6, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 
+    6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 2, 
+    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 6, 6, 6, 6, 
+    46, 46, 46, 46, 46, 6, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 
+    46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 6, 6, 58, 2, 4, 4, 4, 4, 
+    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 
+    2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 
+    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 
+    6, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 
+    6, 6, 5, 5, 5, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 
+    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
+};
+
+/*
+ * Each group represents a unique set of character attributes.  The attributes
+ * are encoded into a 32-bit value as follows:
+ *
+ * Bit  0      A.1 | C.1.2 | C.2.2 | C.3 -- C.9
+ *
+ * Bit  1      C.1.1
+ *
+ * Bit  2      C.2.1
+ *
+ * Bit  3      B.1
+ *
+ * Bit  4      B.1
+ *
+ * Bit  5      D.1
+ *
+ * Bit  6      D.2
+ *
+ * Bits 7-15   Reserved for future use.
+ *
+ * Bits 16-31  Case delta: delta for case conversions.  This should be the
+ *                         highest field so we can easily sign extend.
+ */
+
+static int groups[] = {
+    4, 2, 0, 64, 2097184, 32, 1, 8, 50790432, 65568, -7929824, -17563616, 
+    13762592, 13500448, 13434912, 5177376, 13238304, 13303840, 13565984, 
+    13828128, 13697056, 13959200, 14024736, 14286880, 14221344, 14352416, 
+    131104, -6356960, -3669984, -8519648, 7602176, 2490400, 2424864, 
+    4194336, 4128800, -1966048, -1638368, -983008, -1441760, -3538912, 
+    -3145696, -3080160, -3932128, -4194272, 5242912, 3145760, 16, 
+    17, -3801056, -524256, -4849632, -470089696, -5636064, -6553568, 
+    -7340000, -458720, -8388576, -8257504, 9, 33, -492634080, -549388256, 
+    -541458400, 1048608, 1703968
+};
+
+/*
+ * The following constants are used to determine the category of a
+ * Unicode character.
+ */
+
+#define ACMask  (1 << 0)
+#define C11Mask (1 << 1)
+#define C21Mask (1 << 2)
+#define B1Mask  (1 << 3)
+#define D1Mask  (1 << 4)
+#define D2Mask  (1 << 5)
+#define XNPMask (1 << 6)
+
+/*
+ * The following macros extract the fields of the character info.  The
+ * GetDelta() macro is complicated because we can't rely on the C compiler
+ * to do sign extension on right shifts.
+ */
+
+#define GetCaseType(info) (((info) & 0xE0) >> 5)
+#define GetCategory(info) ((info) & 0x1F)
+#define GetDelta(info) (((info) > 0) ? ((info) >> 16) : (~(~((info)) >> 16)))
+
+/*
+ * This macro extracts the information about a character from the
+ * Unicode character tables.
+ */
+
+#define GetUniCharInfo(ch) (groups[groupMap[(pageMap[(((int)(ch)) & 0xffff) >> OFFSET_BITS] << OFFSET_BITS) | ((ch) & ((1 << OFFSET_BITS)-1))]])
+
diff --git a/src/stringprep/uni_parse.tcl b/src/stringprep/uni_parse.tcl
new file mode 100644 (file)
index 0000000..7d6458f
--- /dev/null
@@ -0,0 +1,362 @@
+# uni_parse.tcl --
+#
+#      This program parses the UnicodeData file and generates the
+#      corresponding uni_data.c file with compressed character
+#      data tables.  The input to this program should be rfc3454.txt
+#
+# Copyright (c) 1998-1999 by Scriptics Corporation.
+# All rights reserved.
+#
+# Modified for ejabberd by Alexey Shchepin
+# 
+# RCS: @(#) $Id$
+
+
+namespace eval uni {
+    set shift 5;               # number of bits of data within a page
+                               # This value can be adjusted to find the
+                               # best split to minimize table size
+
+    variable pMap;             # map from page to page index, each entry is
+                               # an index into the pages table, indexed by
+                               # page number
+    variable pages;            # map from page index to page info, each
+                               # entry is a list of indices into the groups
+                               # table, the list is indexed by the offset
+    variable groups;           # list of character info values, indexed by
+                               # group number, initialized with the
+                               # unassigned character group
+}
+
+proc uni::getValue {tables delta} {
+    set ac 0
+    set c11 0
+    set c21 0
+    set b1 0
+    set d1 0
+    set d2 0
+    set xnp 0
+
+    foreach tab $tables {
+       switch -glob -- $tab {
+           C.1.1 {set c11 1}
+           C.2.1 {set c21 1}
+           C.*   {set ac 1}
+           A.1   {set ac 1}
+           B.1   {set b1 1}
+           D.1   {set d1 1}
+           D.2   {set d2 1}
+           XNP   {set xnp 1}
+       }
+    }
+
+    set val [expr {($ac  << 0) |
+                  ($c11 << 1) |
+                  ($c21 << 2) |
+                  ($b1  << 3) |
+                  ($d1  << 4) |
+                  ($d2  << 5) |
+                  ($xnp << 6) |
+                  ($delta << 16)}]
+
+    return $val
+}
+
+proc uni::getGroup {value} {
+    variable groups
+
+    set gIndex [lsearch -exact $groups $value]
+    if {$gIndex == -1} {
+       set gIndex [llength $groups]
+       lappend groups $value
+    }
+    return $gIndex
+}
+
+proc uni::addPage {info} {
+    variable pMap
+    variable pages
+    
+    set pIndex [lsearch -exact $pages $info]
+    if {$pIndex == -1} {
+       set pIndex [llength $pages]
+       lappend pages $info
+    }
+    lappend pMap $pIndex
+    return
+}
+
+proc uni::load_tables {data} {
+    variable casemap
+    variable tablemap
+
+    for {set i 0} {$i <= 0xffff} {incr i} {
+       set casemap($i) 0
+       set tablemap($i) {}
+    }
+
+    set table ""
+
+    foreach line [split $data \n] {
+       if {$table == ""} {
+           if {[regexp {   ----- Start Table (.*) -----} $line temp table]} {
+               #puts "Start table '$table'"
+           }
+       } else {
+           if {[regexp {   ----- End Table (.*) -----} $line temp table1]} {
+               set table ""
+           } else {
+               if {$table == "B.1"} {
+                   if {[regexp {^   ([[:xdigit:]]+); ;} $line \
+                            temp val]} {
+                       scan $val %x val
+                       if {$val <= 0xffff} {
+                           lappend tablemap($val) $table
+                       }
+                   }
+               } elseif {$table == "B.3"} {
+                   if {[regexp {^   ([[:xdigit:]]+); ([[:xdigit:]]+);} $line \
+                            temp from to]} {
+                       scan $from %x from
+                       scan $to %x to
+                       if {$from <= 0xffff && $to <= 0xffff} {
+                           set casemap($from) [expr {$to - $from}]
+                       }
+                   } else {
+                       # TODO
+                   }
+                   
+               } elseif {$table != "B.2"} {
+                   if {[regexp {^   ([[:xdigit:]]+)-([[:xdigit:]]+)} $line \
+                            temp from to]} {
+                       scan $from %x from
+                       scan $to %x to
+                       for {set i $from} {$i <= $to && $i <= 0xffff} {incr i} {
+                           lappend tablemap($i) $table
+                       }
+                   } elseif {[regexp {^   ([[:xdigit:]]+)} $line \
+                            temp val]} {
+                       scan $val %x val
+                       if {$val <= 0xffff} {
+                           lappend tablemap($val) $table
+                       }
+                   }
+               }
+           }
+       }
+    }
+
+    # XMPP nodeprep prohibited
+    foreach val {22 26 27 2f 3a 3c 3e 40} {
+       scan $val %x val
+       lappend tablemap($val) XNP
+    }
+}
+
+proc uni::buildTables {} {
+    variable shift
+
+    variable casemap
+    variable tablemap
+
+    variable pMap {}
+    variable pages {}
+    variable groups {}
+    set info {}                        ;# temporary page info
+    
+    set mask [expr {(1 << $shift) - 1}]
+
+    set next 0
+
+    for {set i 0} {$i <= 0xffff} {incr i} {
+       set gIndex [getGroup [getValue $tablemap($i) $casemap($i)]]
+
+       # Split character index into offset and page number
+       set offset [expr {$i & $mask}]
+       set page [expr {($i >> $shift)}]
+
+       # Add the group index to the info for the current page
+       lappend info $gIndex
+
+       # If this is the last entry in the page, add the page
+       if {$offset == $mask} {
+           addPage $info
+           set info {}
+       }
+    }
+    return
+}
+
+proc uni::main {} {
+    global argc argv0 argv
+    variable pMap
+    variable pages
+    variable groups
+    variable shift
+
+    if {$argc != 2} {
+       puts stderr "\nusage: $argv0 <datafile> <outdir>\n"
+       exit 1
+    }
+    set f [open [lindex $argv 0] r]
+    set data [read $f]
+    close $f
+
+    load_tables $data
+    buildTables
+    puts "X = [llength $pMap]  Y= [llength $pages]  A= [llength $groups]"
+    set size [expr {[llength $pMap] + [llength $pages]*(1<<$shift)}]
+    puts "shift = 6, space = $size"
+
+    set f [open [file join [lindex $argv 1] uni_data.c] w]
+    fconfigure $f -translation lf
+    puts $f "/*
+ * uni_data.c --
+ *
+ *     Declarations of Unicode character information tables.  This file is
+ *     automatically generated by the uni_parse.tcl script.  Do not
+ *     modify this file by hand.
+ *
+ * Copyright (c) 1998 by Scriptics Corporation.
+ * All rights reserved.
+ *
+ * Modified for ejabberd by Alexey Shchepin
+ *
+ * RCS: @(#) \$Id\$
+ */
+
+/*
+ * A 16-bit Unicode character is split into two parts in order to index
+ * into the following tables.  The lower OFFSET_BITS comprise an offset
+ * into a page of characters.  The upper bits comprise the page number.
+ */
+
+#define OFFSET_BITS $shift
+
+/*
+ * The pageMap is indexed by page number and returns an alternate page number
+ * that identifies a unique page of characters.  Many Unicode characters map
+ * to the same alternate page number.
+ */
+
+static unsigned char pageMap\[\] = {"
+    set line "    "
+    set last [expr {[llength $pMap] - 1}]
+    for {set i 0} {$i <= $last} {incr i} {
+       append line [lindex $pMap $i]
+       if {$i != $last} {
+           append line ", "
+       }
+       if {[string length $line] > 70} {
+           puts $f $line
+           set line "    "
+       }
+    }
+    puts $f $line
+    puts $f "};
+
+/*
+ * The groupMap is indexed by combining the alternate page number with
+ * the page offset and returns a group number that identifies a unique
+ * set of character attributes.
+ */
+
+static unsigned char groupMap\[\] = {"
+    set line "    "
+    set lasti [expr {[llength $pages] - 1}]
+    for {set i 0} {$i <= $lasti} {incr i} {
+       set page [lindex $pages $i]
+       set lastj [expr {[llength $page] - 1}]
+       for {set j 0} {$j <= $lastj} {incr j} {
+           append line [lindex $page $j]
+           if {$j != $lastj || $i != $lasti} {
+               append line ", "
+           }
+           if {[string length $line] > 70} {
+               puts $f $line
+               set line "    "
+           }
+       }
+    }
+    puts $f $line
+    puts $f "};
+
+/*
+ * Each group represents a unique set of character attributes.  The attributes
+ * are encoded into a 32-bit value as follows:
+ *
+ * Bit  0      A.1 | C.1.2 | C.2.2 | C.3 -- C.9
+ *
+ * Bit  1      C.1.1
+ *
+ * Bit  2      C.2.1
+ *
+ * Bit  3      B.1
+ *
+ * Bit  4      B.1
+ *
+ * Bit  5      D.1
+ *
+ * Bit  6      D.2
+ *
+ * Bits 7-15   Reserved for future use.
+ *
+ * Bits 16-31  Case delta: delta for case conversions.  This should be the
+ *                         highest field so we can easily sign extend.
+ */
+
+static int groups\[\] = {"
+    set line "    "
+    set last [expr {[llength $groups] - 1}]
+    for {set i 0} {$i <= $last} {incr i} {
+       set val [lindex $groups $i]
+
+       append line [format "%d" $val]
+       if {$i != $last} {
+           append line ", "
+       }
+       if {[string length $line] > 65} {
+           puts $f $line
+           set line "    "
+       }
+    }
+    puts $f $line
+    puts $f "};
+
+/*
+ * The following constants are used to determine the category of a
+ * Unicode character.
+ */
+
+#define ACMask  (1 << 0)
+#define C11Mask (1 << 1)
+#define C21Mask (1 << 2)
+#define B1Mask  (1 << 3)
+#define D1Mask  (1 << 4)
+#define D2Mask  (1 << 5)
+#define XNPMask (1 << 6)
+
+/*
+ * The following macros extract the fields of the character info.  The
+ * GetDelta() macro is complicated because we can't rely on the C compiler
+ * to do sign extension on right shifts.
+ */
+
+#define GetCaseType(info) (((info) & 0xE0) >> 5)
+#define GetCategory(info) ((info) & 0x1F)
+#define GetDelta(info) (((info) > 0) ? ((info) >> 16) : (~(~((info)) >> 16)))
+
+/*
+ * This macro extracts the information about a character from the
+ * Unicode character tables.
+ */
+
+#define GetUniCharInfo(ch) (groups\[groupMap\[(pageMap\[(((int)(ch)) & 0xffff) >> OFFSET_BITS\] << OFFSET_BITS) | ((ch) & ((1 << OFFSET_BITS)-1))\]\])
+"
+
+    close $f
+}
+
+uni::main
+
+return