text: /*  gridmtx.c - Grid Matrix
  
      libzint - the open source barcode library
      Copyright (C) 2009-2021 Robin Stuart <rstuart114@gmail.com>
  
      Redistribution and use in source and binary forms, with or without
      modification, are permitted provided that the following conditions
      are met:
  
      1. Redistributions of source code must retain the above copyright
         notice, this list of conditions and the following disclaimer.
      2. Redistributions in binary form must reproduce the above copyright
         notice, this list of conditions and the following disclaimer in the
         documentation and/or other materials provided with the distribution.
      3. Neither the name of the project nor the names of its contributors
         may be used to endorse or promote products derived from this software
         without specific prior written permission.
  
      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
      ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
      FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      SUCH DAMAGE.
   */
  /* vim: set ts=4 sw=4 et : */
  
  /* This file implements Grid Matrix as specified in
     AIM Global Document Number AIMD014 Rev. 1.63 Revised 9 Dec 2008 */
  
  #include <stdio.h>
  #ifdef _MSC_VER
  #include <malloc.h>
  #endif
  #include "common.h"
  #include "reedsol.h"
  #include "gridmtx.h"
  #include "gb2312.h"
  #include "eci.h"
  
  /* define_mode() stuff */
  
  /* Bits multiplied by this for costs, so as to be whole integer divisible by 2 and 3 */
  #define GM_MULT 6
  
  static const char numeral_nondigits[] = " +-.,"; /* Non-digit numeral set, excluding EOL (carriage return/linefeed) */
  
  /* Whether in numeral or not. If in numeral, *p_numeral_end is set to position after numeral,
   * and *p_numeral_cost is set to per-numeral cost */
  static int in_numeral(const unsigned int gbdata[], const int length, const int in_posn,
              unsigned int *p_numeral_end, unsigned int *p_numeral_cost) {
      int i, digit_cnt, nondigit, nondigit_posn;
  
      if (in_posn < (int) *p_numeral_end) {
          return 1;
      }
  
      /* Attempt to calculate the average 'cost' of using numeric mode in number of bits (times GM_MULT) */
      /* Also ensures that numeric mode is not selected when it cannot be used: for example in
         a string which has "2.2.0" (cannot have more than one non-numeric character for each
         block of three numeric characters) */
      for (i = in_posn, digit_cnt = 0, nondigit = 0, nondigit_posn = 0; i < length && i < in_posn + 4 && digit_cnt < 3;
              i++) {
          if (gbdata[i] >= '0' && gbdata[i] <= '9') {
              digit_cnt++;
          } else if (strchr(numeral_nondigits, gbdata[i])) {
              if (nondigit) {
                  break;
              }
              nondigit = 1;
              nondigit_posn = i;
          } else if (i < length - 1 && gbdata[i] == 13 && gbdata[i + 1] == 10) {
              if (nondigit) {
                  break;
              }
              i++;
              nondigit = 2;
              nondigit_posn = i;
          } else {
              break;
          }
      }
      if (digit_cnt == 0) { /* Must have at least one digit */
          *p_numeral_end = 0;
          return 0;
      }
      if (nondigit && nondigit_posn == i - 1) { /* Non-digit can't be at end */
          nondigit = 0;
      }
      *p_numeral_end = in_posn + digit_cnt + nondigit;
      /* Calculate per-numeral cost where 120 == (10 + 10) * GM_MULT, 60 == 10 * GM_MULT */
      if (digit_cnt == 3) {
          *p_numeral_cost = nondigit == 2 ? 24 /* (120 / 5) */ : nondigit == 1 ? 30 /* (120 / 4) */ : 20 /* (60 / 3) */;
      } else if (digit_cnt == 2) {
          *p_numeral_cost = nondigit == 2 ? 30 /* (120 / 4) */ : nondigit == 1 ? 40 /* (120 / 3) */ : 30 /* (60 / 2) */;
      } else {
          *p_numeral_cost = nondigit == 2 ? 40 /* (120 / 3) */ : nondigit == 1 ? 60 /* (120 / 2) */ : 60 /* (60 / 1) */;
      }
      return 1;
  }
  
  /* Encoding modes */
  #define GM_CHINESE  'H'
  #define GM_NUMBER   'N'
  #define GM_LOWER    'L'
  #define GM_UPPER    'U'
  #define GM_MIXED    'M'
  #define GM_BYTE     'B'
  /* Note Control is a submode of Lower, Upper and Mixed modes */
  
  /* Indexes into mode_types array */
  #define GM_H   0 /* Chinese (Hanzi) */
  #define GM_N   1 /* Numeral */
  #define GM_L   2 /* Lower case */
  #define GM_U   3 /* Upper case */
  #define GM_M   4 /* Mixed */
  #define GM_B   5 /* Byte */
  
  #define GM_NUM_MODES 6
  
  /* Calculate optimized encoding modes. Adapted from Project Nayuki */
  /* Copyright (c) Project Nayuki. (MIT License) See qr.c for detailed notice */
  static void define_mode(char *mode, const unsigned int gbdata[], const int length, const int debug) {
      /* Must be in same order as GM_H etc */
      static const char mode_types[] = { GM_CHINESE, GM_NUMBER, GM_LOWER, GM_UPPER, GM_MIXED, GM_BYTE, '\0' };
  
      /* Initial mode costs */
      static unsigned int head_costs[GM_NUM_MODES] = {
      /*  H            N (+pad prefix)    L            U            M            B (+byte count) */
          4 * GM_MULT, (4 + 2) * GM_MULT, 4 * GM_MULT, 4 * GM_MULT, 4 * GM_MULT, (4 + 9) * GM_MULT
      };
  
      /* Cost of switching modes from k to j - see AIMD014 Rev. 1.63 Table 9 – Type conversion codes */
      static const unsigned int switch_costs[GM_NUM_MODES][GM_NUM_MODES] = {
          /*      H             N                   L             U             M             B  */
          /*H*/ {            0, (13 + 2) * GM_MULT, 13 * GM_MULT, 13 * GM_MULT, 13 * GM_MULT, (13 + 9) * GM_MULT },
          /*N*/ { 10 * GM_MULT,                  0, 10 * GM_MULT, 10 * GM_MULT, 10 * GM_MULT, (10 + 9) * GM_MULT },
          /*L*/ {  5 * GM_MULT,  (5 + 2) * GM_MULT,            0,  5 * GM_MULT,  7 * GM_MULT,  (7 + 9) * GM_MULT },
          /*U*/ {  5 * GM_MULT,  (5 + 2) * GM_MULT,  5 * GM_MULT,            0,  7 * GM_MULT,  (7 + 9) * GM_MULT },
          /*M*/ { 10 * GM_MULT, (10 + 2) * GM_MULT, 10 * GM_MULT, 10 * GM_MULT,            0, (10 + 9) * GM_MULT },
          /*B*/ {  4 * GM_MULT,  (4 + 2) * GM_MULT,  4 * GM_MULT,  4 * GM_MULT,  4 * GM_MULT,                  0 },
      };
  
      /* Final end-of-data cost - see AIMD014 Rev. 1.63 Table 9 – Type conversion codes */
      static const unsigned int eod_costs[GM_NUM_MODES] = {
      /*  H             N             L            U            M             B  */
          13 * GM_MULT, 10 * GM_MULT, 5 * GM_MULT, 5 * GM_MULT, 10 * GM_MULT, 4 * GM_MULT
      };
  
      unsigned int numeral_end = 0, numeral_cost = 0, byte_count = 0; /* State */
      int double_byte, space, numeric, lower, upper, control, double_digit, eol;
  
      int i, j, k, cm_i;
      unsigned int min_cost;
      char cur_mode;
      unsigned int prev_costs[GM_NUM_MODES];
      unsigned int cur_costs[GM_NUM_MODES];
  #ifndef _MSC_VER
      char char_modes[length * GM_NUM_MODES];
  #else
      char *char_modes = (char *) _alloca(length * GM_NUM_MODES);
  #endif
  
      /* char_modes[i * GM_NUM_MODES + j] represents the mode to encode the code point at index i such that the final
       * segment ends in mode_types[j] and the total number of bits is minimized over all possible choices */
      memset(char_modes, 0, length * GM_NUM_MODES);
  
      /* At the beginning of each iteration of the loop below, prev_costs[j] is the minimum number of 1/6 (1/XX_MULT)
       * bits needed to encode the entire string prefix of length i, and end in mode_types[j] */
      memcpy(prev_costs, head_costs, GM_NUM_MODES * sizeof(unsigned int));
  
      /* Calculate costs using dynamic programming */
      for (i = 0, cm_i = 0; i < length; i++, cm_i += GM_NUM_MODES) {
          memset(cur_costs, 0, GM_NUM_MODES * sizeof(unsigned int));
  
          space = numeric = lower = upper = control = double_digit = eol = 0;
  
          double_byte = gbdata[i] > 0xFF;
          if (!double_byte) {
              space = gbdata[i] == ' ';
              if (!space) {
                  numeric = gbdata[i] >= '0' && gbdata[i] <= '9';
                  if (!numeric) {
                      lower = gbdata[i] >= 'a' && gbdata[i] <= 'z';
                      if (!lower) {
                          upper = gbdata[i] >= 'A' && gbdata[i] <= 'Z';
                          if (!upper) {
                              control = gbdata[i] < 0x7F; /* Exclude DEL */
                              if (control && i + 1 < length) {
                                  eol = gbdata[i] == 13 && gbdata[i + 1] == 10;
                              }
                          }
                      }
                  } else if (i + 1 < length) {
                      double_digit = gbdata[i + 1] >= '0' && gbdata[i + 1] <= '9';
                  }
              }
          }
  
          /* Hanzi mode can encode anything */
          cur_costs[GM_H] = prev_costs[GM_H] + (double_digit || eol ? 39 : 78); /* (6.5 : 13) * GM_MULT */
          char_modes[cm_i + GM_H] = GM_CHINESE;
  
          /* Byte mode can encode anything */
          if (byte_count == 512 || (double_byte && byte_count == 511)) {
              cur_costs[GM_B] = head_costs[GM_B];
              if (double_byte && byte_count == 511) {
                  cur_costs[GM_B] += 48; /* 8 * GM_MULT */
                  double_byte = 0; /* Splitting double-byte so mark as single */
              }
              byte_count = 0;
          }
          cur_costs[GM_B] += prev_costs[GM_B] + (double_byte ? 96 : 48); /* (16 : 8) * GM_MULT */
          char_modes[cm_i + GM_B] = GM_BYTE;
          byte_count += double_byte ? 2 : 1;
  
          if (in_numeral(gbdata, length, i, &numeral_end, &numeral_cost)) {
              cur_costs[GM_N] = prev_costs[GM_N] + numeral_cost;
              char_modes[cm_i + GM_N] = GM_NUMBER;
          }
  
          if (control) {
              cur_costs[GM_L] = prev_costs[GM_L] + 78; /* (7 + 6) * GM_MULT */
              char_modes[cm_i + GM_L] = GM_LOWER;
              cur_costs[GM_U] = prev_costs[GM_U] + 78; /* (7 + 6) * GM_MULT */
              char_modes[cm_i + GM_U] = GM_UPPER;
              cur_costs[GM_M] = prev_costs[GM_M] + 96; /* (10 + 6) * GM_MULT */
              char_modes[cm_i + GM_M] = GM_MIXED;
          } else {
              if (lower || space) {
                  cur_costs[GM_L] = prev_costs[GM_L] + 30; /* 5 * GM_MULT */
                  char_modes[cm_i + GM_L] = GM_LOWER;
              }
              if (upper || space) {
                  cur_costs[GM_U] = prev_costs[GM_U] + 30; /* 5 * GM_MULT */
                  char_modes[cm_i + GM_U] = GM_UPPER;
              }
              if (numeric || lower || upper || space) {
                  cur_costs[GM_M] = prev_costs[GM_M] + 36; /* 6 * GM_MULT */
                  char_modes[cm_i + GM_M] = GM_MIXED;
              }
          }
  
          if (i == length - 1) { /* Add end of data costs if last character */
              for (j = 0; j < GM_NUM_MODES; j++) {
                  if (char_modes[cm_i + j]) {
                      cur_costs[j] += eod_costs[j];
                  }
              }
          }
  
          /* Start new segment at the end to switch modes */
          for (j = 0; j < GM_NUM_MODES; j++) { /* To mode */
              for (k = 0; k < GM_NUM_MODES; k++) { /* From mode */
                  if (j != k && char_modes[cm_i + k]) {
                      unsigned int new_cost = cur_costs[k] + switch_costs[k][j];
                      if (!char_modes[cm_i + j] || new_cost < cur_costs[j]) {
                          cur_costs[j] = new_cost;
                          char_modes[cm_i + j] = mode_types[k];
                      }
                  }
              }
          }
  
          memcpy(prev_costs, cur_costs, GM_NUM_MODES * sizeof(unsigned int));
      }
  
      /* Find optimal ending mode */
      min_cost = prev_costs[0];
      cur_mode = mode_types[0];
      for (i = 1; i < GM_NUM_MODES; i++) {
          if (prev_costs[i] < min_cost) {
              min_cost = prev_costs[i];
              cur_mode = mode_types[i];
          }
      }
  
      /* Get optimal mode for each code point by tracing backwards */
      for (i = length - 1, cm_i = i * GM_NUM_MODES; i >= 0; i--, cm_i -= GM_NUM_MODES) {
          j = strchr(mode_types, cur_mode) - mode_types;
          cur_mode = char_modes[cm_i + j];
          mode[i] = cur_mode;
      }
  
      if (debug & ZINT_DEBUG_PRINT) {
          printf("  Mode: %.*s\n", length, mode);
      }
  }
  
  /* Add the length indicator for byte encoded blocks */
  static void add_byte_count(char binary[], const int byte_count_posn, const int byte_count) {
      /* AIMD014 6.3.7: "Let L be the number of bytes of input data to be encoded in the 8-bit binary data set.
       * First output (L-1) as a 9-bit binary prefix to record the number of bytes..." */
      bin_append_posn(byte_count - 1, 9, binary, byte_count_posn);
  }
  
  /* Add a control character to the data stream */
  static int add_shift_char(char binary[], int bp, int shifty, int debug) {
      int i;
      int glyph = 0;
  
      if (shifty < 32) {
          glyph = shifty;
      } else {
          for (i = 32; i < 64; i++) {
              if (shift_set[i] == shifty) {
                  glyph = i;
                  break;
              }
          }
      }
  
      if (debug & ZINT_DEBUG_PRINT) {
          printf("SHIFT [%d] ", glyph);
      }
  
      bp = bin_append_posn(glyph, 6, binary, bp);
  
      return bp;
  }
  
  static int gm_encode(unsigned int gbdata[], const int length, char binary[], const int reader,
              const struct zint_structapp *p_structapp, const int eci, int *bin_len, int debug) {
      /* Create a binary stream representation of the input data.
         7 sets are defined - Chinese characters, Numerals, Lower case letters, Upper case letters,
         Mixed numerals and latters, Control characters and 8-bit binary data */
      int sp;
      int current_mode, last_mode;
      unsigned int glyph = 0;
      int c1, c2, done;
      int p = 0, ppos;
      int numbuf[3], punt = 0;
      int number_pad_posn, byte_count_posn = 0;
      int byte_count = 0;
      int shift;
      int bp;
  #ifndef _MSC_VER
      char mode[length];
  #else
      char *mode = (char *) _alloca(length);
  #endif
  
      *binary = '\0';
      bp = 0;
  
      sp = 0;
      current_mode = 0;
      number_pad_posn = 0;
  
      if (reader && (!p_structapp || p_structapp->index == 1)) { /* Appears only in 1st symbol if Structured Append */
          bp = bin_append_posn(10, 4, binary, bp); /* FNC3 - Reader Initialisation */
      }
  
      if (p_structapp) {
          bp = bin_append_posn(9, 4, binary, bp); /* FNC2 - Structured Append */
          bp = bin_append_posn(to_int((const unsigned char *) p_structapp->id, (int) strlen(p_structapp->id)), 8,
                  binary, bp); /* File signature */
          bp = bin_append_posn(p_structapp->count - 1, 4, binary, bp);
          bp = bin_append_posn(p_structapp->index - 1, 4, binary, bp);
      }
  
      if (eci != 0) {
          /* ECI assignment according to Table 8 */
          bp = bin_append_posn(12, 4, binary, bp); /* ECI */
          if (eci <= 1023) {
              bp = bin_append_posn(eci, 11, binary, bp);
          } else if (eci <= 32767) {
              bp = bin_append_posn(2, 2, binary, bp);
              bp = bin_append_posn(eci, 15, binary, bp);
          } else {
              bp = bin_append_posn(3, 2, binary, bp);
              bp = bin_append_posn(eci, 20, binary, bp);
          }
      }
  
      define_mode(mode, gbdata, length, debug);
  
      do {
          int next_mode = mode[sp];
  
          if (next_mode != current_mode) {
              switch (current_mode) {
                  case 0:
                      switch (next_mode) {
                          case GM_CHINESE: bp = bin_append_posn(1, 4, binary, bp);
                              break;
                          case GM_NUMBER: bp = bin_append_posn(2, 4, binary, bp);
                              break;
                          case GM_LOWER: bp = bin_append_posn(3, 4, binary, bp);
                              break;
                          case GM_UPPER: bp = bin_append_posn(4, 4, binary, bp);
                              break;
                          case GM_MIXED: bp = bin_append_posn(5, 4, binary, bp);
                              break;
                          case GM_BYTE: bp = bin_append_posn(6, 4, binary, bp);
                              break;
                      }
                      break;
                  case GM_CHINESE:
                      switch (next_mode) {
                          case GM_NUMBER: bp = bin_append_posn(8161, 13, binary, bp);
                              break;
                          case GM_LOWER: bp = bin_append_posn(8162, 13, binary, bp);
                              break;
                          case GM_UPPER: bp = bin_append_posn(8163, 13, binary, bp);
                              break;
                          case GM_MIXED: bp = bin_append_posn(8164, 13, binary, bp);
                              break;
                          case GM_BYTE: bp = bin_append_posn(8165, 13, binary, bp);
                              break;
                      }
                      break;
                  case GM_NUMBER:
                      /* add numeric block padding value */
                      switch (p) {
                          case 1: binary[number_pad_posn] = '1';
                              binary[number_pad_posn + 1] = '0';
                              break; // 2 pad digits
                          case 2: binary[number_pad_posn] = '0';
                              binary[number_pad_posn + 1] = '1';
                              break; // 1 pad digits
                          case 3: binary[number_pad_posn] = '0';
                              binary[number_pad_posn + 1] = '0';
                              break; // 0 pad digits
                      }
                      switch (next_mode) {
                          case GM_CHINESE: bp = bin_append_posn(1019, 10, binary, bp);
                              break;
                          case GM_LOWER: bp = bin_append_posn(1020, 10, binary, bp);
                              break;
                          case GM_UPPER: bp = bin_append_posn(1021, 10, binary, bp);
                              break;
                          case GM_MIXED: bp = bin_append_posn(1022, 10, binary, bp);
                              break;
                          case GM_BYTE: bp = bin_append_posn(1023, 10, binary, bp);
                              break;
                      }
                      break;
                  case GM_LOWER:
                  case GM_UPPER:
                      switch (next_mode) {
                          case GM_CHINESE: bp = bin_append_posn(28, 5, binary, bp);
                              break;
                          case GM_NUMBER: bp = bin_append_posn(29, 5, binary, bp);
                              break;
                          case GM_LOWER:
                          case GM_UPPER: bp = bin_append_posn(30, 5, binary, bp);
                              break;
                          case GM_MIXED: bp = bin_append_posn(124, 7, binary, bp);
                              break;
                          case GM_BYTE: bp = bin_append_posn(126, 7, binary, bp);
                              break;
                      }
                      break;
                  case GM_MIXED:
                      switch (next_mode) {
                          case GM_CHINESE: bp = bin_append_posn(1009, 10, binary, bp);
                              break;
                          case GM_NUMBER: bp = bin_append_posn(1010, 10, binary, bp);
                              break;
                          case GM_LOWER: bp = bin_append_posn(1011, 10, binary, bp);
                              break;
                          case GM_UPPER: bp = bin_append_posn(1012, 10, binary, bp);
                              break;
                          case GM_BYTE: bp = bin_append_posn(1015, 10, binary, bp);
                              break;
                      }
                      break;
                  case GM_BYTE:
                      /* add byte block length indicator */
                      add_byte_count(binary, byte_count_posn, byte_count);
                      byte_count = 0;
                      switch (next_mode) {
                          case GM_CHINESE: bp = bin_append_posn(1, 4, binary, bp);
                              break;
                          case GM_NUMBER: bp = bin_append_posn(2, 4, binary, bp);
                              break;
                          case GM_LOWER: bp = bin_append_posn(3, 4, binary, bp);
                              break;
                          case GM_UPPER: bp = bin_append_posn(4, 4, binary, bp);
                              break;
                          case GM_MIXED: bp = bin_append_posn(5, 4, binary, bp);
                              break;
                      }
                      break;
              }
              if (debug & ZINT_DEBUG_PRINT) {
                  switch (next_mode) {
                      case GM_CHINESE: printf("CHIN ");
                          break;
                      case GM_NUMBER: printf("NUMB ");
                          break;
                      case GM_LOWER: printf("LOWR ");
                          break;
                      case GM_UPPER: printf("UPPR ");
                          break;
                      case GM_MIXED: printf("MIXD ");
                          break;
                      case GM_BYTE: printf("BYTE ");
                          break;
                  }
              }
          }
          last_mode = current_mode;
          current_mode = next_mode;
  
          switch (current_mode) {
              case GM_CHINESE:
                  done = 0;
                  if (gbdata[sp] > 0xff) {
                      /* GB2312 character */
                      c1 = (gbdata[sp] & 0xff00) >> 8;
                      c2 = gbdata[sp] & 0xff;
  
                      if ((c1 >= 0xa1) && (c1 <= 0xa9)) {
                          glyph = (0x60 * (c1 - 0xa1)) + (c2 - 0xa0);
                      } else if ((c1 >= 0xb0) && (c1 <= 0xf7)) {
                          glyph = (0x60 * (c1 - 0xb0 + 9)) + (c2 - 0xa0);
                      }
                      done = 1; /* GB 2312 always within above ranges */
                  }
                  if (!(done)) {
                      if (sp != (length - 1)) {
                          if ((gbdata[sp] == 13) && (gbdata[sp + 1] == 10)) {
                              /* End of Line */
                              glyph = 7776;
                              sp++;
                              done = 1;
                          }
                      }
                  }
                  if (!(done)) {
                      if (sp != (length - 1)) {
                          if (((gbdata[sp] >= '0') && (gbdata[sp] <= '9')) &&
                                  ((gbdata[sp + 1] >= '0') && (gbdata[sp + 1] <= '9'))) {
                              /* Two digits */
                              glyph = 8033 + (10 * (gbdata[sp] - '0')) + (gbdata[sp + 1] - '0');
                              sp++;
                              done = 1;
                          }
                      }
                  }
                  if (!(done)) {
                      /* Byte value */
                      glyph = 7777 + gbdata[sp];
                  }
  
                  if (debug & ZINT_DEBUG_PRINT) {
                      printf("[%d] ", (int) glyph);
                  }
  
                  bp = bin_append_posn(glyph, 13, binary, bp);
                  sp++;
                  break;
  
              case GM_NUMBER:
                  if (last_mode != current_mode) {
                      /* Reserve a space for numeric digit padding value (2 bits) */
                      number_pad_posn = bp;
                      bp = bin_append_posn(0, 2, binary, bp);
                  }
                  p = 0;
                  ppos = -1;
  
                  /* Numeric compression can also include certain combinations of
                     non-numeric character */
  
                  numbuf[0] = '0';
                  numbuf[1] = '0';
                  numbuf[2] = '0';
                  do {
                      if ((gbdata[sp] >= '0') && (gbdata[sp] <= '9')) {
                          numbuf[p] = gbdata[sp];
                          p++;
                      } else if (strchr(numeral_nondigits, gbdata[sp])) {
                          if (ppos != -1) {
                              break;
                          }
                          punt = gbdata[sp];
                          ppos = p;
                      } else if (sp < (length - 1) && (gbdata[sp] == 13) && (gbdata[sp + 1] == 10)) {
                          /* <end of line> */
                          if (ppos != -1) {
                              break;
                          }
                          punt = gbdata[sp];
                          sp++;
                          ppos = p;
                      } else {
                          break;
                      }
                      sp++;
                  } while ((p < 3) && (sp < length) && mode[sp] == GM_NUMBER);
  
                  if (ppos != -1) {
                      switch (punt) {
                          case ' ': glyph = 0;
                              break;
                          case '+': glyph = 3;
                              break;
                          case '-': glyph = 6;
                              break;
                          case '.': glyph = 9;
                              break;
                          case ',': glyph = 12;
                              break;
                          case 13: glyph = 15;
                              break;
                      }
                      glyph += ppos;
                      glyph += 1000;
  
                      if (debug & ZINT_DEBUG_PRINT) {
                          printf("[%d] ", (int) glyph);
                      }
  
                      bp = bin_append_posn(glyph, 10, binary, bp);
                  }
  
                  glyph = (100 * (numbuf[0] - '0')) + (10 * (numbuf[1] - '0')) + (numbuf[2] - '0');
                  if (debug & ZINT_DEBUG_PRINT) {
                      printf("[%d] ", (int) glyph);
                  }
  
                  bp = bin_append_posn(glyph, 10, binary, bp);
                  break;
  
              case GM_BYTE:
                  if (last_mode != current_mode) {
                      /* Reserve space for byte block length indicator (9 bits) */
                      byte_count_posn = bp;
                      bp = bin_append_posn(0, 9, binary, bp);
                  }
                  glyph = gbdata[sp];
                  if (byte_count == 512 || (glyph > 0xFF && byte_count == 511)) {
                      /* Maximum byte block size is 512 bytes. If longer is needed then start a new block */
                      if (glyph > 0xFF && byte_count == 511) { /* Split double-byte */
                          bp = bin_append_posn(glyph >> 8, 8, binary, bp);
                          glyph &= 0xFF;
                          byte_count++;
                      }
                      add_byte_count(binary, byte_count_posn, byte_count);
                      bp = bin_append_posn(7, 4, binary, bp);
                      byte_count_posn = bp;
                      bp = bin_append_posn(0, 9, binary, bp);
                      byte_count = 0;
                  }
  
                  if (debug & ZINT_DEBUG_PRINT) {
                      printf("[%d] ", (int) glyph);
                  }
                  bp = bin_append_posn(glyph, glyph > 0xFF ? 16 : 8, binary, bp);
                  sp++;
                  byte_count++;
                  if (glyph > 0xFF) {
                      byte_count++;
                  }
                  break;
  
              case GM_MIXED:
                  shift = 1;
                  if ((gbdata[sp] >= '0') && (gbdata[sp] <= '9')) {
                      shift = 0;
                  } else if ((gbdata[sp] >= 'A') && (gbdata[sp] <= 'Z')) {
                      shift = 0;
                  } else if ((gbdata[sp] >= 'a') && (gbdata[sp] <= 'z')) {
                      shift = 0;
                  } else if (gbdata[sp] == ' ') {
                      shift = 0;
                  }
  
                  if (shift == 0) {
                      /* Mixed Mode character */
                      glyph = posn(EUROPIUM, (const char) gbdata[sp]);
                      if (debug & ZINT_DEBUG_PRINT) {
                          printf("[%d] ", (int) glyph);
                      }
  
                      bp = bin_append_posn(glyph, 6, binary, bp);
                  } else {
                      /* Shift Mode character */
                      bp = bin_append_posn(1014, 10, binary, bp); /* shift indicator */
                      bp = add_shift_char(binary, bp, gbdata[sp], debug);
                  }
  
                  sp++;
                  break;
  
              case GM_UPPER:
                  shift = 1;
                  if ((gbdata[sp] >= 'A') && (gbdata[sp] <= 'Z')) {
                      shift = 0;
                  } else if (gbdata[sp] == ' ') {
                      shift = 0;
                  }
  
                  if (shift == 0) {
                      /* Upper Case character */
                      glyph = posn("ABCDEFGHIJKLMNOPQRSTUVWXYZ ", (const char) gbdata[sp]);
                      if (debug & ZINT_DEBUG_PRINT) {
                          printf("[%d] ", (int) glyph);
                      }
  
                      bp = bin_append_posn(glyph, 5, binary, bp);
                  } else {
                      /* Shift Mode character */
                      bp = bin_append_posn(125, 7, binary, bp); /* shift indicator */
                      bp = add_shift_char(binary, bp, gbdata[sp], debug);
                  }
  
                  sp++;
                  break;
  
              case GM_LOWER:
                  shift = 1;
                  if ((gbdata[sp] >= 'a') && (gbdata[sp] <= 'z')) {
                      shift = 0;
                  } else if (gbdata[sp] == ' ') {
                      shift = 0;
                  }
  
                  if (shift == 0) {
                      /* Lower Case character */
                      glyph = posn("abcdefghijklmnopqrstuvwxyz ", (const char) gbdata[sp]);
                      if (debug & ZINT_DEBUG_PRINT) {
                          printf("[%d] ", (int) glyph);
                      }
  
                      bp = bin_append_posn(glyph, 5, binary, bp);
                  } else {
                      /* Shift Mode character */
                      bp = bin_append_posn(125, 7, binary, bp); /* shift indicator */
                      bp = add_shift_char(binary, bp, gbdata[sp], debug);
                  }
  
                  sp++;
                  break;
          }
          if (bp > 9191) {
              return ZINT_ERROR_TOO_LONG;
          }
  
      } while (sp < length);
  
      if (current_mode == GM_NUMBER) {
          /* add numeric block padding value */
          switch (p) {
              case 1: binary[number_pad_posn] = '1';
                  binary[number_pad_posn + 1] = '0';
                  break; // 2 pad digits
              case 2: binary[number_pad_posn] = '0';
                  binary[number_pad_posn + 1] = '1';
                  break; // 1 pad digit
              case 3: binary[number_pad_posn] = '0';
                  binary[number_pad_posn + 1] = '0';
                  break; // 0 pad digits
          }
      }
  
      if (current_mode == GM_BYTE) {
          /* Add byte block length indicator */
          add_byte_count(binary, byte_count_posn, byte_count);
      }
  
      /* Add "end of data" character */
      switch (current_mode) {
          case GM_CHINESE: bp = bin_append_posn(8160, 13, binary, bp);
              break;
          case GM_NUMBER: bp = bin_append_posn(1018, 10, binary, bp);
              break;
          case GM_LOWER:
          case GM_UPPER: bp = bin_append_posn(27, 5, binary, bp);
              break;
          case GM_MIXED: bp = bin_append_posn(1008, 10, binary, bp);
              break;
          case GM_BYTE: bp = bin_append_posn(0, 4, binary, bp);
              break;
      }
  
      /* Add padding bits if required */
      p = 7 - (bp % 7);
      if (p % 7) {
          bp = bin_append_posn(0, p, binary, bp);
      }
  
      if (bp > 9191) {
          return ZINT_ERROR_TOO_LONG;
      }
      binary[bp] = '\0';
      *bin_len = bp;
  
      if (debug & ZINT_DEBUG_PRINT) {
          printf("\nBinary (%d): %s\n", bp, binary);
      }
  
      return 0;
  }
  
  static void gm_add_ecc(const char binary[], const int data_posn, const int layers, const int ecc_level,
              unsigned char word[]) {
      int data_cw, i, j, wp, p;
      int n1, b1, n2, b2, e1, b3, e2;
      int block_size, ecc_size;
      unsigned char data[1320], block[130];
      unsigned char data_block[115], ecc_block[70];
      rs_t rs;
  
      data_cw = gm_data_codewords[((layers - 1) * 5) + (ecc_level - 1)];
  
      for (i = 0; i < 1320; i++) {
          data[i] = 0;
      }
  
      /* Convert from binary stream to 7-bit codewords */
      for (i = 0; i < data_posn; i++) {
          for (p = 0; p < 7; p++) {
              if (binary[i * 7 + p] == '1') {
                  data[i] += (0x40 >> p);
              }
          }
      }
  
      /* Add padding codewords */
      data[data_posn] = 0x00;
      for (i = (data_posn + 1); i < data_cw; i++) {
          if (i & 1) {
              data[i] = 0x7e;
          } else {
              data[i] = 0x00;
          }
      }
  
      /* Get block sizes */
      n1 = gm_n1[(layers - 1)];
      b1 = gm_b1[(layers - 1)];
      n2 = n1 - 1;
      b2 = gm_b2[(layers - 1)];
      e1 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4)];
      b3 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 1];
      e2 = gm_ebeb[((layers - 1) * 20) + ((ecc_level - 1) * 4) + 2];
  
      rs_init_gf(&rs, 0x89);
  
      /* Split the data into blocks */
      wp = 0;
      for (i = 0; i < (b1 + b2); i++) {
          int data_size;
          if (i < b1) {
              block_size = n1;
          } else {
              block_size = n2;
          }
          if (i < b3) {
              ecc_size = e1;
          } else {
              ecc_size = e2;
          }
          data_size = block_size - ecc_size;
  
          /* printf("block %d/%d: data %d / ecc %d\n", i + 1, (b1 + b2), data_size, ecc_size);*/
  
          for (j = 0; j < data_size; j++) {
              data_block[j] = data[wp];
              wp++;
          }
  
          /* Calculate ECC data for this block */
          rs_init_code(&rs, ecc_size, 1);
          rs_encode(&rs, data_size, data_block, ecc_block);
  
          /* Correct error correction data but in reverse order */
          for (j = 0; j < data_size; j++) {
              block[j] = data_block[j];
          }
          for (j = 0; j < ecc_size; j++) {
              block[(j + data_size)] = ecc_block[ecc_size - j - 1];
          }
  
          for (j = 0; j < n2; j++) {
              word[((b1 + b2) * j) + i] = block[j];
          }
          if (block_size == n1) {
              word[((b1 + b2) * (n1 - 1)) + i] = block[(n1 - 1)];
          }
      }
  }
  
  static void place_macromodule(char grid[], int x, int y, int word1, int word2, int size) {
      int i, j;
  
      i = (x * 6) + 1;
      j = (y * 6) + 1;
  
      if (word2 & 0x40) {
          grid[(j * size) + i + 2] = '1';
      }
      if (word2 & 0x20) {
          grid[(j * size) + i + 3] = '1';
      }
      if (word2 & 0x10) {
          grid[((j + 1) * size) + i] = '1';
      }
      if (word2 & 0x08) {
          grid[((j + 1) * size) + i + 1] = '1';
      }
      if (word2 & 0x04) {
          grid[((j + 1) * size) + i + 2] = '1';
      }
      if (word2 & 0x02) {
          grid[((j + 1) * size) + i + 3] = '1';
      }
      if (word2 & 0x01) {
          grid[((j + 2) * size) + i] = '1';
      }
      if (word1 & 0x40) {
          grid[((j + 2) * size) + i + 1] = '1';
      }
      if (word1 & 0x20) {
          grid[((j + 2) * size) + i + 2] = '1';
      }
      if (word1 & 0x10) {
          grid[((j + 2) * size) + i + 3] = '1';
      }
      if (word1 & 0x08) {
          grid[((j + 3) * size) + i] = '1';
      }
      if (word1 & 0x04) {
          grid[((j + 3) * size) + i + 1] = '1';
      }
      if (word1 & 0x02) {
          grid[((j + 3) * size) + i + 2] = '1';
      }
      if (word1 & 0x01) {
          grid[((j + 3) * size) + i + 3] = '1';
      }
  }
  
  static void place_data_in_grid(unsigned char word[], char grid[], int modules, int size) {
      int x, y, macromodule, offset;
  
      offset = 13 - ((modules - 1) / 2);
      for (y = 0; y < modules; y++) {
          for (x = 0; x < modules; x++) {
              macromodule = gm_macro_matrix[((y + offset) * 27) + (x + offset)];
              place_macromodule(grid, x, y, word[macromodule * 2], word[(macromodule * 2) + 1], size);
          }
      }
  }
  
  /* Place the layer ID into each macromodule */
  static void place_layer_id(char *grid, int size, int layers, int modules, int ecc_level) {
      int i, j, layer, start, stop;
  
  #ifndef _MSC_VER
      int layerid[layers + 1];
      int id[modules * modules];
  #else
      int *layerid = (int *) _alloca((layers + 1) * sizeof(int));
      int *id = (int *) _alloca((modules * modules) * sizeof(int));
  #endif
  
      /* Calculate Layer IDs */
      for (i = 0; i <= layers; i++) {
          if (ecc_level == 1) {
              layerid[i] = 3 - (i % 4);
          } else {
              layerid[i] = (i + 5 - ecc_level) % 4;
          }
      }
  
      for (i = 0; i < modules; i++) {
          for (j = 0; j < modules; j++) {
              id[(i * modules) + j] = 0;
          }
      }
  
      /* Calculate which value goes in each macromodule */
      start = modules / 2;
      stop = modules / 2;
      for (layer = 0; layer <= layers; layer++) {
          for (i = start; i <= stop; i++) {
              id[(start * modules) + i] = layerid[layer];
              id[(i * modules) + start] = layerid[layer];
              id[((modules - start - 1) * modules) + i] = layerid[layer];
              id[(i * modules) + (modules - start - 1)] = layerid[layer];
          }
          start--;
          stop++;
      }
  
      /* Place the data in the grid */
      for (i = 0; i < modules; i++) {
          for (j = 0; j < modules; j++) {
              if (id[(i * modules) + j] & 0x02) {
                  grid[(((i * 6) + 1) * size) + (j * 6) + 1] = '1';
              }
              if (id[(i * modules) + j] & 0x01) {
                  grid[(((i * 6) + 1) * size) + (j * 6) + 2] = '1';
              }
          }
      }
  }
  
  INTERNAL int grid_matrix(struct zint_symbol *symbol, unsigned char source[], int length) {
      int size, modules, error_number;
      int auto_layers, min_layers, layers, auto_ecc_level, min_ecc_level, ecc_level;
      int x, y, i;
      int full_multibyte;
      char binary[9300];
      int data_cw, input_latch = 0;
      unsigned char word[1460] = {0};
      int data_max, reader = 0;
      const struct zint_structapp *p_structapp = NULL;
      int size_squared;
      int bin_len;
      int eci_length = get_eci_length(symbol->eci, source, length);
  
  #ifndef _MSC_VER
      unsigned int gbdata[eci_length + 1];
  #else
      char *grid;
      unsigned int *gbdata = (unsigned int *) _alloca((eci_length + 1) * sizeof(unsigned int));
  #endif
  
      /* If ZINT_FULL_MULTIBYTE set use Hanzi mode in DATA_MODE or for non-GB 2312 in UNICODE_MODE */
      full_multibyte = (symbol->option_3 & 0xFF) == ZINT_FULL_MULTIBYTE;
  
      if ((symbol->input_mode & 0x07) == DATA_MODE) {
          gb2312_cpy(source, &length, gbdata, full_multibyte);
      } else {
          int done = 0;
          if (symbol->eci != 29) { /* Unless ECI 29 (GB) */
              /* Try other conversions (ECI 0 defaults to ISO/IEC 8859-1) */
              error_number = gb2312_utf8_to_eci(symbol->eci, source, &length, gbdata, full_multibyte);
              if (error_number == 0) {
                  done = 1;
              } else if (symbol->eci) {
                  sprintf(symbol->errtxt, "535: Invalid character in input data for ECI %d", symbol->eci);
                  return error_number;
              }
          }
          if (!done) {
              /* Try GB 2312 (EUC-CN) */
              error_number = gb2312_utf8(symbol, source, &length, gbdata);
              if (error_number != 0) {
                  return error_number;
              }
          }
      }
  
      if (symbol->output_options & READER_INIT) reader = 1;
  
      if (symbol->structapp.count) {
          if (symbol->structapp.count < 2 || symbol->structapp.count > 16) {
              strcpy(symbol->errtxt, "536: Structured Append count out of range (2-16)");
              return ZINT_ERROR_INVALID_OPTION;
          }
          if (symbol->structapp.index < 1 || symbol->structapp.index > symbol->structapp.count) {
              sprintf(symbol->errtxt, "537: Structured Append index out of range (1-%d)", symbol->structapp.count);
              return ZINT_ERROR_INVALID_OPTION;
          }
          if (symbol->structapp.id[0]) {
              int id, id_len;
  
              for (id_len = 0; id_len < 32 && symbol->structapp.id[id_len]; id_len++);
  
              if (id_len > 3) { /* 255 (8 bits) */
                  strcpy(symbol->errtxt, "538: Structured Append ID too long (3 digit maximum)");
                  return ZINT_ERROR_INVALID_OPTION;
              }
  
              id = to_int((const unsigned char *) symbol->structapp.id, id_len);
              if (id == -1) {
                  strcpy(symbol->errtxt, "539: Invalid Structured Append ID (digits only)");
                  return ZINT_ERROR_INVALID_OPTION;
              }
              if (id > 255) {
                  sprintf(symbol->errtxt, "530: Structured Append ID '%d' out of range (0-255)", id);
                  return ZINT_ERROR_INVALID_OPTION;
              }
          }
          p_structapp = &symbol->structapp;
      }
  
      if (symbol->eci > 811799) {
          strcpy(symbol->errtxt, "533: Invalid ECI");
          return ZINT_ERROR_INVALID_OPTION;
      }
  
      error_number = gm_encode(gbdata, length, binary, reader, p_structapp, symbol->eci, &bin_len, symbol->debug);
      if (error_number != 0) {
          strcpy(symbol->errtxt, "531: Input data too long");
          return error_number;
      }
  
      /* Determine the size of the symbol */
      data_cw = bin_len / 7; /* Binary length always a multiple of 7 */
  
      auto_layers = 13;
      for (i = 12; i > 0; i--) {
          if (gm_recommend_cw[(i - 1)] >= data_cw) {
              auto_layers = i;
          }
      }
      min_layers = 13;
      for (i = 12; i > 0; i--) {
          if (gm_max_cw[(i - 1)] >= data_cw) {
              min_layers = i;
          }
      }
      layers = auto_layers;
  
      if ((symbol->option_2 >= 1) && (symbol->option_2 <= 13)) {
          input_latch = 1;
          if (symbol->option_2 >= min_layers) {
              layers = symbol->option_2;
          } else {
              strcpy(symbol->errtxt, "534: Input data too long for selected symbol size");
              return ZINT_ERROR_TOO_LONG;
          }
      }
  
      auto_ecc_level = 3;
      if (layers == 1) {
          auto_ecc_level = 5;
      } else if ((layers == 2) || (layers == 3)) {
          auto_ecc_level = 4;
      }
      ecc_level = auto_ecc_level;
  
      min_ecc_level = 1;
      if (layers == 1) {
          min_ecc_level = 4;
      } else if (layers == 2) {
          min_ecc_level = 2;
      }
  
      if ((symbol->option_1 >= 1) && (symbol->option_1 <= 5)) {
          if (symbol->option_1 >= min_ecc_level) {
              ecc_level = symbol->option_1;
          } else {
              ecc_level = min_ecc_level;
          }
      }
      if (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)]) {
          /* If layers user-specified (option_2), try reducing ECC level first */
          if (input_latch && ecc_level > min_ecc_level) {
              do {
                  ecc_level--;
              } while ((data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)])
                          && (ecc_level > min_ecc_level));
          }
          while (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)] && (layers < 13)) {
              layers++;
          }
          /* ECC min level 1 for layers > 2 */
          while (data_cw > gm_data_codewords[(5 * (layers - 1)) + (ecc_level - 1)] && ecc_level > 1) {
              ecc_level--;
          }
      }
  
      data_max = 1313;
      switch (ecc_level) {
          case 2: data_max = 1167;
              break;
          case 3: data_max = 1021;
              break;
          case 4: data_max = 875;
              break;
          case 5: data_max = 729;
              break;
      }
  
      if (data_cw > data_max) {
          strcpy(symbol->errtxt, "532: Input data too long");
          return ZINT_ERROR_TOO_LONG;
      }
  
      gm_add_ecc(binary, data_cw, layers, ecc_level, word);
  #ifdef ZINT_TEST
      if (symbol->debug & ZINT_DEBUG_TEST) debug_test_codeword_dump(symbol, word, data_cw);
  #endif
      size = 6 + (layers * 12);
      modules = 1 + (layers * 2);
      size_squared = size * size;
  
  #ifndef _MSC_VER
      char grid[size_squared];
  #else
      grid = (char *) _alloca(size_squared);
  #endif
  
      memset(grid, '0', size_squared);
  
      place_data_in_grid(word, grid, modules, size);
      place_layer_id(grid, size, layers, modules, ecc_level);
  
      /* Add macromodule frames */
      for (x = 0; x < modules; x++) {
          int dark = 1 - (x & 1);
          for (y = 0; y < modules; y++) {
              if (dark == 1) {
                  for (i = 0; i < 5; i++) {
                      grid[((y * 6) * size) + (x * 6) + i] = '1';
                      grid[(((y * 6) + 5) * size) + (x * 6) + i] = '1';
                      grid[(((y * 6) + i) * size) + (x * 6)] = '1';
                      grid[(((y * 6) + i) * size) + (x * 6) + 5] = '1';
                  }
                  grid[(((y * 6) + 5) * size) + (x * 6) + 5] = '1';
                  dark = 0;
              } else {
                  dark = 1;
              }
          }
      }
  
      /* Copy values to symbol */
      symbol->width = size;
      symbol->rows = size;
  
      for (x = 0; x < size; x++) {
          for (y = 0; y < size; y++) {
              if (grid[(y * size) + x] == '1') {
                  set_module(symbol, y, x);
              }
          }
          symbol->row_height[x] = 1;
      }
      symbol->height = size;
  
      return 0;
  }
  