#include "hbclass.ch"

CREATE CLASS TBarcodeUtil

   METHOD New() CONSTRUCTOR
   METHOD Crear( uTexto, uTipo )
   METHOD NormalizarTipo( cTipo )

   HIDDEN:
   METHOD CrearEAN13( cTexto )
   METHOD DigitoControlEAN13( cDoceDigitos )
   METHOD CrearEAN8( cTexto )
   METHOD DigitoControlEAN8( cSieteDigitos )
   METHOD CrearCode128( cTexto, lGs1 )
   METHOD Code128Bits( cTexto, lGs1 )
   METHOD DigitRun( cTexto, nOffset )
   METHOD PatternBits( cPattern )
   METHOD Ean13Bits( cTexto )
   METHOD Ean8Bits( cTexto )
   METHOD Error( cMensaje )
   METHOD ValorAString( uValor )
   METHOD EsDigitos( cTexto )
   METHOD SoloLetrasNumerosAscii( cTexto )

ENDCLASS

METHOD New() CLASS TBarcodeUtil

RETURN Self

METHOD Crear( uTexto, uTipo ) CLASS TBarcodeUtil

   LOCAL cTipo := ::NormalizarTipo( ::ValorAString( uTipo ) )
   LOCAL cTexto
   LOCAL aMatrix

   IF Empty( cTipo )
      RETURN ::Error( "Tipo de codigo de barras no soportado." )
   ENDIF

   cTexto := ::ValorAString( uTexto )

   IF cTipo == "EAN13"
      RETURN ::CrearEAN13( cTexto )
   ENDIF
   IF cTipo == "EAN8"
      RETURN ::CrearEAN8( cTexto )
   ENDIF

   IF cTexto == ""
      RETURN ::Error( "Contenido de codigo de barras vacio." )
   ENDIF

   IF cTipo == "CODE128" .OR. cTipo == "EAN128"
      RETURN ::CrearCode128( cTexto, cTipo == "EAN128" )
   ELSEIF cTipo == "DATAMATRIX"
      aMatrix := LLDomDataMatrix( cTexto )
      IF Len( aMatrix ) == 0
         RETURN ::Error( "Contenido Data Matrix demasiado largo." )
      ENDIF
      RETURN { ;
         "tipo" => "barcode", ;
         "barcodeTipo" => cTipo, ;
         "texto" => cTexto, ;
         "matriz" => aMatrix, ;
         "margenModulos" => 1, ;
         "mostrarTexto" => .T. }
   ELSEIF cTipo == "QR"
      aMatrix := LLDomQrMatrix( cTexto )
      IF Len( aMatrix ) == 0
         RETURN ::Error( "QR admite hasta 271 bytes en esta version." )
      ENDIF
      RETURN { ;
         "tipo" => "barcode", ;
         "barcodeTipo" => cTipo, ;
         "texto" => cTexto, ;
         "matriz" => aMatrix, ;
         "margenModulos" => 4, ;
         "mostrarTexto" => .T. }
   ENDIF

RETURN { ;
   "tipo" => "barcode", ;
   "barcodeTipo" => cTipo, ;
   "texto" => cTexto, ;
   "mostrarTexto" => .T. }

METHOD NormalizarTipo( cTipo ) CLASS TBarcodeUtil

   LOCAL cNormalizado := Upper( ::SoloLetrasNumerosAscii( ::ValorAString( cTipo ) ) )

   DO CASE
   CASE cNormalizado == "EAN13"
      RETURN "EAN13"
   CASE cNormalizado == "EAN8"
      RETURN "EAN8"
   CASE cNormalizado == "CODE128"
      RETURN "CODE128"
   CASE cNormalizado == "EAN128" .OR. cNormalizado == "GS1128"
      RETURN "EAN128"
   CASE cNormalizado == "DATAMATRIX"
      RETURN "DATAMATRIX"
   CASE cNormalizado == "QR" .OR. cNormalizado == "QRCODE"
      RETURN "QR"
   ENDCASE

RETURN ""

METHOD CrearEAN13( cTexto ) CLASS TBarcodeUtil

   LOCAL nEsperado

   IF Len( cTexto ) == 12 .AND. ::EsDigitos( cTexto )
      cTexto += ::ValorAString( ::DigitoControlEAN13( cTexto ) )
   ELSEIF Len( cTexto ) == 13 .AND. ::EsDigitos( cTexto )
      nEsperado := ::DigitoControlEAN13( SubStr( cTexto, 1, 12 ) )
      IF Val( SubStr( cTexto, 13, 1 ) ) != nEsperado
         RETURN ::Error( "Digito de control EAN-13 no valido." )
      ENDIF
   ELSE
      RETURN ::Error( "EAN-13 debe contener 12 o 13 digitos." )
   ENDIF

RETURN { ;
   "tipo" => "barcode", ;
   "barcodeTipo" => "EAN13", ;
   "texto" => cTexto, ;
   "bits" => ::Ean13Bits( cTexto ), ;
   "margenIzquierdoModulos" => 11, ;
   "margenDerechoModulos" => 7, ;
   "mostrarTexto" => .T. }

METHOD DigitoControlEAN13( cDoceDigitos ) CLASS TBarcodeUtil

   LOCAL nSuma := 0
   LOCAL nIndex
   LOCAL nDigito

   FOR nIndex := 1 TO 12
      nDigito := Val( SubStr( cDoceDigitos, nIndex, 1 ) )
      IF ( nIndex - 1 ) % 2 == 0
         nSuma += nDigito
      ELSE
         nSuma += nDigito * 3
      ENDIF
   NEXT

RETURN ( 10 - ( nSuma % 10 ) ) % 10

METHOD CrearEAN8( cTexto ) CLASS TBarcodeUtil

   LOCAL nEsperado

   IF Len( cTexto ) == 7 .AND. ::EsDigitos( cTexto )
      cTexto += ::ValorAString( ::DigitoControlEAN8( cTexto ) )
   ELSEIF Len( cTexto ) == 8 .AND. ::EsDigitos( cTexto )
      nEsperado := ::DigitoControlEAN8( SubStr( cTexto, 1, 7 ) )
      IF Val( SubStr( cTexto, 8, 1 ) ) != nEsperado
         RETURN ::Error( "Digito de control EAN-8 no valido." )
      ENDIF
   ELSE
      RETURN ::Error( "EAN-8 debe contener 7 u 8 digitos." )
   ENDIF

RETURN { ;
   "tipo" => "barcode", ;
   "barcodeTipo" => "EAN8", ;
   "texto" => cTexto, ;
   "bits" => ::Ean8Bits( cTexto ), ;
   "margenIzquierdoModulos" => 7, ;
   "margenDerechoModulos" => 7, ;
   "mostrarTexto" => .T. }

METHOD DigitoControlEAN8( cSieteDigitos ) CLASS TBarcodeUtil

   LOCAL nSuma := 0
   LOCAL nIndex
   LOCAL nDigito

   FOR nIndex := 1 TO 7
      nDigito := Val( SubStr( cSieteDigitos, nIndex, 1 ) )
      IF ( nIndex - 1 ) % 2 == 0
         nSuma += nDigito * 3
      ELSE
         nSuma += nDigito
      ENDIF
   NEXT

RETURN ( 10 - ( nSuma % 10 ) ) % 10

METHOD CrearCode128( cTexto, lGs1 ) CLASS TBarcodeUtil

   LOCAL cBits
   LOCAL cTipo

   lGs1 := HB_ISLOGICAL( lGs1 ) .AND. lGs1
   cTipo := iif( lGs1, "EAN128", "CODE128" )

   IF cTexto == ""
      RETURN ::Error( "Contenido de codigo de barras vacio." )
   ENDIF
   cBits := ::Code128Bits( cTexto, lGs1 )
   IF cBits == ""
      RETURN ::Error( iif( lGs1, ;
         "EAN128 admite ASCII imprimible y separadores GS.", ;
         "CODE128 admite caracteres ASCII imprimibles." ) )
   ENDIF

RETURN { ;
   "tipo" => "barcode", ;
   "barcodeTipo" => cTipo, ;
   "texto" => cTexto, ;
   "bits" => cBits, ;
   "margenIzquierdoModulos" => 10, ;
   "margenDerechoModulos" => 10, ;
   "mostrarTexto" => .T. }

METHOD Code128Bits( cTexto, lGs1 ) CLASS TBarcodeUtil

   LOCAL aPatterns := { ;
      "212222","222122","222221","121223","121322","131222","122213","122312","132212","221213", ;
      "221312","231212","112232","122132","122231","113222","123122","123221","223211","221132", ;
      "221231","213212","223112","312131","311222","321122","321221","312212","322112","322211", ;
      "212123","212321","232121","111323","131123","131321","112313","132113","132311","211313", ;
      "231113","231311","112133","112331","132131","113123","113321","133121","313121","211331", ;
      "231131","213113","213311","213131","311123","311321","331121","312113","312311","332111", ;
      "314111","221411","431111","111224","111422","121124","121421","141122","141221","112214", ;
      "112412","122114","122411","142112","142211","241211","221114","413111","241112","134111", ;
      "111242","121142","121241","114212","124112","124211","411212","421112","421211","212141", ;
      "214121","412121","111143","111341","131141","114113","114311","411113","411311","113141", ;
      "114131","311141","411131","211412","211214","211232","2331112" }
   LOCAL aCodes := {}
   LOCAL nIndex := 1
   LOCAL nRun
   LOCAL lCodeC := ::DigitRun( cTexto, 1 ) >= 4
   LOCAL nChecksum
   LOCAL nPosition
   LOCAL nCode
   LOCAL cBits := ""
   LOCAL nChar

   lGs1 := HB_ISLOGICAL( lGs1 ) .AND. lGs1
   AAdd( aCodes, iif( lCodeC, 105, 104 ) )
   IF lGs1
      AAdd( aCodes, 102 )
   ENDIF
   DO WHILE nIndex <= Len( cTexto )
      nChar := Asc( SubStr( cTexto, nIndex, 1 ) )
      IF nChar == 29 .AND. lGs1
         AAdd( aCodes, 102 )
         nIndex++
         LOOP
      ENDIF
      IF nChar < 32 .OR. nChar > 126
         RETURN ""
      ENDIF
      IF lCodeC
         IF ::DigitRun( cTexto, nIndex ) >= 2
            AAdd( aCodes, Val( SubStr( cTexto, nIndex, 2 ) ) )
            nIndex += 2
            LOOP
         ENDIF
         AAdd( aCodes, 100 )
         lCodeC := .F.
         LOOP
      ENDIF
      nRun := ::DigitRun( cTexto, nIndex )
      IF nRun >= 4
         IF nRun % 2 == 1
            AAdd( aCodes, nChar - 32 )
            nIndex++
         ENDIF
         AAdd( aCodes, 99 )
         lCodeC := .T.
         LOOP
      ENDIF
      AAdd( aCodes, nChar - 32 )
      nIndex++
   ENDDO
   nChecksum := aCodes[ 1 ]
   FOR nPosition := 2 TO Len( aCodes )
      nChecksum += aCodes[ nPosition ] * ( nPosition - 1 )
   NEXT
   AAdd( aCodes, nChecksum % 103 )
   AAdd( aCodes, 106 )
   FOR EACH nCode IN aCodes
      cBits += ::PatternBits( aPatterns[ nCode + 1 ] )
   NEXT

RETURN cBits

METHOD DigitRun( cTexto, nOffset ) CLASS TBarcodeUtil

   LOCAL nRun := 0
   LOCAL nChar

   DO WHILE nOffset + nRun <= Len( cTexto )
      nChar := Asc( SubStr( cTexto, nOffset + nRun, 1 ) )
      IF nChar < 48 .OR. nChar > 57
         EXIT
      ENDIF
      nRun++
   ENDDO

RETURN nRun

METHOD PatternBits( cPattern ) CLASS TBarcodeUtil

   LOCAL cBits := ""
   LOCAL nIndex

   FOR nIndex := 1 TO Len( cPattern )
      cBits += Replicate( iif( nIndex % 2 == 1, "1", "0" ), Val( SubStr( cPattern, nIndex, 1 ) ) )
   NEXT

RETURN cBits

METHOD Ean13Bits( cTexto ) CLASS TBarcodeUtil

   LOCAL aL := { "0001101","0011001","0010011","0111101","0100011","0110001","0101111","0111011","0110111","0001011" }
   LOCAL aG := { "0100111","0110011","0011011","0100001","0011101","0111001","0000101","0010001","0001001","0010111" }
   LOCAL aR := { "1110010","1100110","1101100","1000010","1011100","1001110","1010000","1000100","1001000","1110100" }
   LOCAL aParity := { "LLLLLL","LLGLGG","LLGGLG","LLGGGL","LGLLGG","LGGLLG","LGGGLL","LGLGLG","LGLGGL","LGGLGL" }
   LOCAL cBits := "101"
   LOCAL nIndex
   LOCAL nDigit
   LOCAL nFirst := Val( Left( cTexto, 1 ) ) + 1

   FOR nIndex := 2 TO 7
      nDigit := Val( SubStr( cTexto, nIndex, 1 ) ) + 1
      cBits += iif( SubStr( aParity[ nFirst ], nIndex - 1, 1 ) == "G", aG[ nDigit ], aL[ nDigit ] )
   NEXT
   cBits += "01010"
   FOR nIndex := 8 TO 13
      cBits += aR[ Val( SubStr( cTexto, nIndex, 1 ) ) + 1 ]
   NEXT

RETURN cBits + "101"

METHOD Ean8Bits( cTexto ) CLASS TBarcodeUtil

   LOCAL aL := { "0001101","0011001","0010011","0111101","0100011","0110001","0101111","0111011","0110111","0001011" }
   LOCAL aR := { "1110010","1100110","1101100","1000010","1011100","1001110","1010000","1000100","1001000","1110100" }
   LOCAL cBits := "101"
   LOCAL nIndex

   FOR nIndex := 1 TO 4
      cBits += aL[ Val( SubStr( cTexto, nIndex, 1 ) ) + 1 ]
   NEXT
   cBits += "01010"
   FOR nIndex := 5 TO 8
      cBits += aR[ Val( SubStr( cTexto, nIndex, 1 ) ) + 1 ]
   NEXT

RETURN cBits + "101"

METHOD Error( cMensaje ) CLASS TBarcodeUtil

RETURN { ;
   "tipo" => "barcode", ;
   "barcodeTipo" => "", ;
   "texto" => "", ;
   "error" => cMensaje }

METHOD ValorAString( uValor ) CLASS TBarcodeUtil

   DO CASE
   CASE uValor == NIL
      RETURN ""
   CASE HB_ISSTRING( uValor )
      RETURN uValor
   CASE HB_ISNUMERIC( uValor )
      IF uValor == Int( uValor )
         RETURN AllTrim( Str( uValor, 20, 0 ) )
      ENDIF
      RETURN AllTrim( Str( uValor ) )
   CASE HB_ISLOGICAL( uValor )
      RETURN iif( uValor, "1", "" )
   ENDCASE

RETURN AllTrim( hb_ValToStr( uValor ) )

METHOD EsDigitos( cTexto ) CLASS TBarcodeUtil

   LOCAL nIndex
   LOCAL nChar
   LOCAL nLen

   IF Empty( cTexto )
      RETURN .F.
   ENDIF

   nLen := Len( cTexto )
   FOR nIndex := 1 TO nLen
      nChar := Asc( SubStr( cTexto, nIndex, 1 ) )
      IF nChar < 48 .OR. nChar > 57
         RETURN .F.
      ENDIF
   NEXT

RETURN .T.

METHOD SoloLetrasNumerosAscii( cTexto ) CLASS TBarcodeUtil

   LOCAL cResultado := ""
   LOCAL nIndex
   LOCAL nChar
   LOCAL nLen

   nLen := Len( cTexto )
   FOR nIndex := 1 TO nLen
      nChar := Asc( SubStr( cTexto, nIndex, 1 ) )
      IF ( nChar >= 48 .AND. nChar <= 57 ) .OR. ;
            ( nChar >= 65 .AND. nChar <= 90 ) .OR. ;
            ( nChar >= 97 .AND. nChar <= 122 )
         cResultado += SubStr( cTexto, nIndex, 1 )
      ENDIF
   NEXT

RETURN cResultado

#pragma BEGINDUMP

#include "hbapi.h"
#include "hbapiitm.h"
#include <stdlib.h>
#include <string.h>

typedef struct
{
   int size;
   int data;
   int ecc;
   int region;
} LLDomDMSymbol;

static const LLDomDMSymbol s_symbols[] = {
   { 10, 3, 5, 8 }, { 12, 5, 7, 10 }, { 14, 8, 10, 12 },
   { 16, 12, 12, 14 }, { 18, 18, 14, 16 }, { 20, 22, 18, 18 },
   { 22, 30, 20, 20 }, { 24, 36, 24, 22 }, { 26, 44, 28, 24 },
   { 32, 62, 36, 14 }, { 36, 86, 42, 16 }, { 40, 114, 48, 18 },
   { 44, 144, 56, 20 }
};

static unsigned char s_dm_exp[ 512 ];
static unsigned char s_dm_log[ 256 ];

typedef struct { int count, total, data; } LLDomQRGroup;
typedef struct { int groups; LLDomQRGroup group[ 2 ]; } LLDomQRConfig;

static const LLDomQRConfig s_qr_l[ 10 ] = {
   { 1, { { 1,26,19 }, { 0,0,0 } } }, { 1, { { 1,44,34 }, { 0,0,0 } } },
   { 1, { { 1,70,55 }, { 0,0,0 } } }, { 1, { { 1,100,80 }, { 0,0,0 } } },
   { 1, { { 1,134,108 }, { 0,0,0 } } }, { 1, { { 2,86,68 }, { 0,0,0 } } },
   { 1, { { 2,98,78 }, { 0,0,0 } } }, { 1, { { 2,121,97 }, { 0,0,0 } } },
   { 1, { { 2,146,116 }, { 0,0,0 } } }, { 2, { { 2,86,68 }, { 2,87,69 } } }
};

static unsigned char lldom_qr_mul( unsigned char x, unsigned char y )
{
   int z = 0, i;
   for( i = 7; i >= 0; --i )
   {
      z = ( ( z << 1 ) ^ ( ( ( z >> 7 ) & 1 ) * 0x11D ) ) & 0xFF;
      z ^= ( ( y >> i ) & 1 ) * x;
   }
   return ( unsigned char ) z;
}

static void lldom_qr_divisor( int degree, unsigned char *out )
{
   int i, j, root = 1;
   memset( out, 0, degree ); out[ degree - 1 ] = 1;
   for( i = 0; i < degree; ++i )
   {
      for( j = 0; j < degree; ++j )
      {
         out[ j ] = lldom_qr_mul( out[ j ], ( unsigned char ) root );
         if( j + 1 < degree ) out[ j ] ^= out[ j + 1 ];
      }
      root = lldom_qr_mul( ( unsigned char ) root, 2 );
   }
}

static void lldom_qr_remainder( const unsigned char *data, int dataLen, const unsigned char *divisor, int degree, unsigned char *out )
{
   int i, j;
   memset( out, 0, degree );
   for( i = 0; i < dataLen; ++i )
   {
      unsigned char factor = data[ i ] ^ out[ 0 ];
      memmove( out, out + 1, degree - 1 ); out[ degree - 1 ] = 0;
      for( j = 0; j < degree; ++j ) out[ j ] ^= lldom_qr_mul( divisor[ j ], factor );
   }
}

static void lldom_qr_append( unsigned char *bits, int *length, unsigned int value, int count )
{
   int i;
   for( i = count - 1; i >= 0; --i ) bits[ ( *length )++ ] = ( unsigned char ) ( ( value >> i ) & 1 );
}

static void lldom_qr_set( unsigned char *modules, unsigned char *function, int size, int x, int y, int dark )
{
   modules[ y * size + x ] = ( unsigned char ) dark;
   function[ y * size + x ] = 1;
}

static void lldom_qr_finder( unsigned char *modules, unsigned char *function, int size, int cx, int cy )
{
   int x, y, xx, yy, dist;
   for( y = -4; y <= 4; ++y ) for( x = -4; x <= 4; ++x )
   {
      xx = cx + x; yy = cy + y;
      if( xx < 0 || xx >= size || yy < 0 || yy >= size ) continue;
      dist = abs( x ) > abs( y ) ? abs( x ) : abs( y );
      lldom_qr_set( modules,function,size,xx,yy,dist != 2 && dist != 4 );
   }
}

static int lldom_qr_align_positions( int version, int size, int *positions )
{
   int count, step, pos, index;
   if( version == 1 ) return 0;
   count = version / 7 + 2;
   step = version == 32 ? 26 : ( ( version * 4 + count * 2 + 1 ) / ( count * 2 - 2 ) ) * 2;
   positions[ 0 ] = 6;
   index = count - 1;
   for( pos = size - 7; index >= 1; pos -= step ) positions[ index-- ] = pos;
   return count;
}

static void lldom_qr_format( unsigned char *modules, unsigned char *function, int size, int mask )
{
   int data = ( 1 << 3 ) | mask, rem = data, bits, i;
   for( i = 0; i < 10; ++i ) rem = ( rem << 1 ) ^ ( ( ( rem >> 9 ) & 1 ) * 0x537 );
   bits = ( ( data << 10 ) | rem ) ^ 0x5412;
   for( i = 0; i <= 5; ++i ) lldom_qr_set( modules,function,size,8,i,( bits >> i ) & 1 );
   lldom_qr_set( modules,function,size,8,7,( bits >> 6 ) & 1 );
   lldom_qr_set( modules,function,size,8,8,( bits >> 7 ) & 1 );
   lldom_qr_set( modules,function,size,7,8,( bits >> 8 ) & 1 );
   for( i = 9; i < 15; ++i ) lldom_qr_set( modules,function,size,14-i,8,( bits >> i ) & 1 );
   for( i = 0; i < 8; ++i ) lldom_qr_set( modules,function,size,size-1-i,8,( bits >> i ) & 1 );
   for( i = 8; i < 15; ++i ) lldom_qr_set( modules,function,size,8,size-15+i,( bits >> i ) & 1 );
   lldom_qr_set( modules,function,size,8,size-8,1 );
}

static void lldom_qr_functions( unsigned char *modules, unsigned char *function, int version, int size )
{
   int i, j, x, y, p[ 7 ], count, rem, bits, a, b;
   for( i = 0; i < size; ++i ) { lldom_qr_set( modules,function,size,6,i,i%2==0 ); lldom_qr_set( modules,function,size,i,6,i%2==0 ); }
   lldom_qr_finder( modules,function,size,3,3 ); lldom_qr_finder( modules,function,size,size-4,3 ); lldom_qr_finder( modules,function,size,3,size-4 );
   count = lldom_qr_align_positions( version,size,p );
   for( i = 0; i < count; ++i ) for( j = 0; j < count; ++j )
   {
      if( ( i==0 && j==0 ) || ( i==0 && j==count-1 ) || ( i==count-1 && j==0 ) ) continue;
      for( y=-2; y<=2; ++y ) for( x=-2; x<=2; ++x ) lldom_qr_set( modules,function,size,p[i]+x,p[j]+y,( abs(x)>abs(y)?abs(x):abs(y) ) != 1 );
   }
   lldom_qr_format( modules,function,size,0 );
   if( version >= 7 )
   {
      rem = version;
      for( i=0; i<12; ++i ) rem = ( rem << 1 ) ^ ( ( ( rem >> 11 ) & 1 ) * 0x1F25 );
      bits = ( version << 12 ) | rem;
      for( i=0; i<18; ++i ) { a=size-11+i%3; b=i/3; lldom_qr_set(modules,function,size,a,b,(bits>>i)&1); lldom_qr_set(modules,function,size,b,a,(bits>>i)&1); }
   }
}

static int lldom_qr_maskbit( int mask, int x, int y )
{
   int product = x * y;
   switch( mask )
   {
      case 0: return ( x + y ) % 2 == 0; case 1: return y % 2 == 0; case 2: return x % 3 == 0;
      case 3: return ( x + y ) % 3 == 0; case 4: return ( x / 3 + y / 2 ) % 2 == 0;
      case 5: return product % 2 + product % 3 == 0; case 6: return ( product % 2 + product % 3 ) % 2 == 0;
      default: return ( ( x + y ) % 2 + product % 3 ) % 2 == 0;
   }
}

static void lldom_qr_mask( unsigned char *modules, const unsigned char *function, int size, int mask )
{
   int x,y;
   for( y=0; y<size; ++y ) for( x=0; x<size; ++x ) if( !function[y*size+x] && lldom_qr_maskbit(mask,x,y) ) modules[y*size+x] ^= 1;
}

static int lldom_qr_line_penalty( const unsigned char *line, int size, int step )
{
   int result=0, run=1, i, j, match1, match2;
   for( i=1; i<size; ++i )
   {
      if( line[i*step] == line[(i-1)*step] ) { ++run; if(run==5) result+=3; else if(run>5) result++; } else run=1;
   }
   for( i=0; i<=size-11; ++i )
   {
      match1=1; match2=1;
      for( j=0; j<11; ++j )
      {
         static const char p1[]="00001011101", p2[]="10111010000";
         if( line[(i+j)*step] != (unsigned char)(p1[j]-'0') ) match1=0;
         if( line[(i+j)*step] != (unsigned char)(p2[j]-'0') ) match2=0;
      }
      if(match1||match2) result+=40;
   }
   return result;
}

static int lldom_qr_penalty( const unsigned char *modules, int size )
{
   int result=0,x,y,dark=0,total=size*size;
   unsigned char column[57];
   for(y=0;y<size;++y) result+=lldom_qr_line_penalty(modules+y*size,size,1);
   for(x=0;x<size;++x){for(y=0;y<size;++y)column[y]=modules[y*size+x];result+=lldom_qr_line_penalty(column,size,1);}
   for(y=0;y<size-1;++y)for(x=0;x<size-1;++x)if(modules[y*size+x]==modules[y*size+x+1]&&modules[y*size+x]==modules[(y+1)*size+x]&&modules[y*size+x]==modules[(y+1)*size+x+1])result+=3;
   for(x=0;x<total;++x)dark+=modules[x]!=0;
   return result + ( abs(dark*20-total*10)/total )*10;
}

HB_FUNC( LLDOMQRMATRIX )
{
   const unsigned char *text=(const unsigned char*)hb_parc(1); int textLen=(int)hb_parclen(1);
   int version,capacity=0,countBits,size,bitLen=0,i,j,g,b,offset=0,blockCount=0,maxData=0,eccLen=0,outLen=0,right,vert,y,x,col,bitIndex=0,bestMask=0,bestPenalty=0x7FFFFFFF,penalty;
   unsigned char bits[2200],data[274],blocks[4][116],blockLens[4],eccBlocks[4][30],all[346],divisor[30],modules[57*57],function[57*57],best[57*57];
   PHB_ITEM result;
   const LLDomQRConfig *cfg=NULL;
   for(version=1;version<=10;++version){cfg=&s_qr_l[version-1];capacity=0;for(g=0;g<cfg->groups;++g)capacity+=cfg->group[g].count*cfg->group[g].data;countBits=version<=9?8:16;if(4+countBits+textLen*8<=capacity*8)break;}
   if(version>10){hb_reta(0);return;}
   lldom_qr_append(bits,&bitLen,4,4); lldom_qr_append(bits,&bitLen,(unsigned int)textLen,countBits);
   for(i=0;i<textLen;++i)lldom_qr_append(bits,&bitLen,text[i],8);
   for(i=0;i<4&&bitLen<capacity*8;++i)bits[bitLen++]=0; while(bitLen%8)bits[bitLen++]=0;
   for(i=0;i<bitLen/8;++i){data[i]=0;for(j=0;j<8;++j)data[i]=(unsigned char)((data[i]<<1)|bits[i*8+j]);}
   for(i=bitLen/8;i<capacity;++i)data[i]=(unsigned char)(((i-bitLen/8)%2)==0?0xEC:0x11);
   for(g=0;g<cfg->groups;++g)for(b=0;b<cfg->group[g].count;++b){int dl=cfg->group[g].data,el=cfg->group[g].total-dl;memcpy(blocks[blockCount],data+offset,dl);blockLens[blockCount]=(unsigned char)dl;offset+=dl;lldom_qr_divisor(el,divisor);lldom_qr_remainder(blocks[blockCount],dl,divisor,el,eccBlocks[blockCount]);if(dl>maxData)maxData=dl;eccLen=el;blockCount++;}
   for(i=0;i<maxData;++i)for(b=0;b<blockCount;++b)if(i<blockLens[b])all[outLen++]=blocks[b][i];
   for(i=0;i<eccLen;++i)for(b=0;b<blockCount;++b)all[outLen++]=eccBlocks[b][i];
   size=version*4+17;memset(modules,0,sizeof(modules));memset(function,0,sizeof(function));lldom_qr_functions(modules,function,version,size);
   for(right=size-1;right>=1;right-=2){if(right==6)right=5;for(vert=0;vert<size;++vert){y=(((right+1)&2)==0)?size-1-vert:vert;for(col=0;col<2;++col){x=right-col;if(function[y*size+x]||bitIndex>=outLen*8)continue;modules[y*size+x]=(all[bitIndex/8]>>(7-(bitIndex&7)))&1;bitIndex++;}}}
   for(i=0;i<8;++i){lldom_qr_mask(modules,function,size,i);lldom_qr_format(modules,function,size,i);penalty=lldom_qr_penalty(modules,size);if(penalty<bestPenalty){bestPenalty=penalty;bestMask=i;memcpy(best,modules,size*size);}lldom_qr_mask(modules,function,size,i);}
   memcpy(modules,best,size*size);lldom_qr_format(modules,function,size,bestMask);
   result=hb_itemArrayNew(size);for(y=0;y<size;++y){char row[58];for(x=0;x<size;++x)row[x]=modules[y*size+x]?'1':'0';hb_arraySetCL(result,y+1,row,size);}hb_itemReturnRelease(result);
}

static void lldom_dm_galois( void )
{
   int i;
   int value = 1;
   memset( s_dm_exp, 0, sizeof( s_dm_exp ) );
   memset( s_dm_log, 0, sizeof( s_dm_log ) );
   for( i = 0; i < 255; ++i )
   {
      s_dm_exp[ i ] = ( unsigned char ) value;
      s_dm_log[ value ] = ( unsigned char ) i;
      value <<= 1;
      if( value & 0x100 )
         value ^= 0x12D;
   }
   for( i = 255; i < 512; ++i )
      s_dm_exp[ i ] = s_dm_exp[ i - 255 ];
}

static unsigned char lldom_dm_mul( unsigned char left, unsigned char right )
{
   if( left == 0 || right == 0 )
      return 0;
   return s_dm_exp[ s_dm_log[ left ] + s_dm_log[ right ] ];
}

static void lldom_dm_module( signed char *bits, int rows, int cols, const unsigned char *words, int position, int bit, int row, int col )
{
   if( row < 0 )
   {
      row += rows;
      col += 4 - ( ( rows + 4 ) % 8 );
   }
   if( col < 0 )
   {
      col += cols;
      row += 4 - ( ( cols + 4 ) % 8 );
   }
   bits[ row * cols + col ] = ( signed char ) ( ( words[ position ] >> ( 8 - bit ) ) & 1 );
}

static void lldom_dm_utah( signed char *b, int rows, int cols, const unsigned char *w, int p, int r, int c )
{
   lldom_dm_module( b, rows, cols, w, p, 1, r - 2, c - 2 ); lldom_dm_module( b, rows, cols, w, p, 2, r - 2, c - 1 );
   lldom_dm_module( b, rows, cols, w, p, 3, r - 1, c - 2 ); lldom_dm_module( b, rows, cols, w, p, 4, r - 1, c - 1 );
   lldom_dm_module( b, rows, cols, w, p, 5, r - 1, c ); lldom_dm_module( b, rows, cols, w, p, 6, r, c - 2 );
   lldom_dm_module( b, rows, cols, w, p, 7, r, c - 1 ); lldom_dm_module( b, rows, cols, w, p, 8, r, c );
}

static void lldom_dm_corner1( signed char *b, int rows, int cols, const unsigned char *w, int p )
{
   lldom_dm_module( b,rows,cols,w,p,1,rows-1,0 ); lldom_dm_module( b,rows,cols,w,p,2,rows-1,1 ); lldom_dm_module( b,rows,cols,w,p,3,rows-1,2 ); lldom_dm_module( b,rows,cols,w,p,4,0,cols-2 );
   lldom_dm_module( b,rows,cols,w,p,5,0,cols-1 ); lldom_dm_module( b,rows,cols,w,p,6,1,cols-1 ); lldom_dm_module( b,rows,cols,w,p,7,2,cols-1 ); lldom_dm_module( b,rows,cols,w,p,8,3,cols-1 );
}

static void lldom_dm_corner2( signed char *b, int rows, int cols, const unsigned char *w, int p )
{
   lldom_dm_module( b,rows,cols,w,p,1,rows-3,0 ); lldom_dm_module( b,rows,cols,w,p,2,rows-2,0 ); lldom_dm_module( b,rows,cols,w,p,3,rows-1,0 ); lldom_dm_module( b,rows,cols,w,p,4,0,cols-4 );
   lldom_dm_module( b,rows,cols,w,p,5,0,cols-3 ); lldom_dm_module( b,rows,cols,w,p,6,0,cols-2 ); lldom_dm_module( b,rows,cols,w,p,7,0,cols-1 ); lldom_dm_module( b,rows,cols,w,p,8,1,cols-1 );
}

static void lldom_dm_corner3( signed char *b, int rows, int cols, const unsigned char *w, int p )
{
   lldom_dm_module( b,rows,cols,w,p,1,rows-3,0 ); lldom_dm_module( b,rows,cols,w,p,2,rows-2,0 ); lldom_dm_module( b,rows,cols,w,p,3,rows-1,0 ); lldom_dm_module( b,rows,cols,w,p,4,0,cols-2 );
   lldom_dm_module( b,rows,cols,w,p,5,0,cols-1 ); lldom_dm_module( b,rows,cols,w,p,6,1,cols-1 ); lldom_dm_module( b,rows,cols,w,p,7,2,cols-1 ); lldom_dm_module( b,rows,cols,w,p,8,3,cols-1 );
}

static void lldom_dm_corner4( signed char *b, int rows, int cols, const unsigned char *w, int p )
{
   lldom_dm_module( b,rows,cols,w,p,1,rows-1,0 ); lldom_dm_module( b,rows,cols,w,p,2,rows-1,cols-1 ); lldom_dm_module( b,rows,cols,w,p,3,0,cols-3 ); lldom_dm_module( b,rows,cols,w,p,4,0,cols-2 );
   lldom_dm_module( b,rows,cols,w,p,5,0,cols-1 ); lldom_dm_module( b,rows,cols,w,p,6,1,cols-3 ); lldom_dm_module( b,rows,cols,w,p,7,1,cols-2 ); lldom_dm_module( b,rows,cols,w,p,8,1,cols-1 );
}

HB_FUNC( LLDOMDATAMATRIX )
{
   const unsigned char *text = ( const unsigned char * ) hb_parc( 1 );
   HB_SIZE length = hb_parclen( 1 );
   unsigned char data[ 144 ], words[ 200 ], generator[ 57 ], next[ 57 ], ecc[ 56 ];
   signed char placement[ 1600 ];
   char matrix[ 44 * 44 ];
   int dataCount = 0, i, j, root, factor, position, row, col, dataRows, dataCols, regions, outRow, outCol, regionY, regionX, localY, localX, top, left;
   const LLDomDMSymbol *symbol = NULL;
   PHB_ITEM result;

   for( i = 0; i < ( int ) length && dataCount < 144; ++i )
   {
      if( i + 1 < ( int ) length && text[ i ] >= '0' && text[ i ] <= '9' && text[ i + 1 ] >= '0' && text[ i + 1 ] <= '9' )
      {
         data[ dataCount++ ] = ( unsigned char ) ( 130 + ( text[ i ] - '0' ) * 10 + text[ i + 1 ] - '0' );
         ++i;
      }
      else if( text[ i ] <= 127 )
         data[ dataCount++ ] = ( unsigned char ) ( text[ i ] + 1 );
      else if( dataCount + 1 < 144 )
      {
         data[ dataCount++ ] = 235;
         data[ dataCount++ ] = ( unsigned char ) ( text[ i ] - 127 );
      }
   }
   for( i = 0; i < ( int ) ( sizeof( s_symbols ) / sizeof( s_symbols[ 0 ] ) ); ++i )
      if( dataCount <= s_symbols[ i ].data ) { symbol = &s_symbols[ i ]; break; }
   if( symbol == NULL ) { hb_reta( 0 ); return; }
   if( dataCount < symbol->data ) data[ dataCount++ ] = 129;
   while( dataCount < symbol->data )
   {
      position = dataCount + 1;
      factor = 129 + ( ( 149 * position ) % 253 ) + 1;
      data[ dataCount++ ] = ( unsigned char ) ( factor <= 254 ? factor : factor - 254 );
   }

   lldom_dm_galois();
   memset( generator, 0, sizeof( generator ) ); generator[ 0 ] = 1;
   for( root = 1; root <= symbol->ecc; ++root )
   {
      memset( next, 0, sizeof( next ) );
      for( j = 0; j < root; ++j )
      {
         next[ j ] ^= generator[ j ];
         next[ j + 1 ] ^= lldom_dm_mul( generator[ j ], s_dm_exp[ root ] );
      }
      memcpy( generator, next, ( root + 1 ) * sizeof( unsigned char ) );
   }
   memset( ecc, 0, sizeof( ecc ) );
   for( i = 0; i < symbol->data; ++i )
   {
      factor = data[ i ] ^ ecc[ 0 ];
      memmove( ecc, ecc + 1, ( symbol->ecc - 1 ) * sizeof( unsigned char ) ); ecc[ symbol->ecc - 1 ] = 0;
      for( j = 0; j < symbol->ecc; ++j ) ecc[ j ] ^= lldom_dm_mul( ( unsigned char ) factor, generator[ j + 1 ] );
   }
   memcpy( words, data, symbol->data ); memcpy( words + symbol->data, ecc, symbol->ecc );

   regions = symbol->size / ( symbol->region + 2 );
   dataRows = dataCols = symbol->size - 2 * regions;
   memset( placement, -1, sizeof( placement ) ); position = 0; row = 4; col = 0;
   do
   {
      if( row == dataRows && col == 0 ) lldom_dm_corner1( placement,dataRows,dataCols,words,position++ );
      if( row == dataRows - 2 && col == 0 && dataCols % 4 != 0 ) lldom_dm_corner2( placement,dataRows,dataCols,words,position++ );
      if( row == dataRows - 2 && col == 0 && dataCols % 8 == 4 ) lldom_dm_corner3( placement,dataRows,dataCols,words,position++ );
      if( row == dataRows + 4 && col == 2 && dataCols % 8 == 0 ) lldom_dm_corner4( placement,dataRows,dataCols,words,position++ );
      do { if( row < dataRows && col >= 0 && placement[ row * dataCols + col ] < 0 ) lldom_dm_utah( placement,dataRows,dataCols,words,position++,row,col ); row -= 2; col += 2; } while( row >= 0 && col < dataCols );
      row++; col += 3;
      do { if( row >= 0 && col < dataCols && placement[ row * dataCols + col ] < 0 ) lldom_dm_utah( placement,dataRows,dataCols,words,position++,row,col ); row += 2; col -= 2; } while( row < dataRows && col >= 0 );
      row += 3; col++;
   } while( row < dataRows || col < dataCols );
   if( placement[ dataRows * dataCols - 1 ] < 0 ) { placement[ dataRows * dataCols - 1 ] = 1; placement[ ( dataRows - 2 ) * dataCols + dataCols - 2 ] = 1; }

   memset( matrix, '0', sizeof( matrix ) );
   for( regionY = 0; regionY < regions; ++regionY )
   {
      top = regionY * ( symbol->region + 2 );
      for( col = 0; col < symbol->size; ++col ) matrix[ top * symbol->size + col ] = col % 2 == 0 ? '1' : '0';
      for( localY = 0; localY < symbol->region; ++localY )
      {
         row = regionY * symbol->region + localY; outRow = top + 1 + localY;
         for( regionX = 0; regionX < regions; ++regionX )
         {
            left = regionX * ( symbol->region + 2 );
            matrix[ outRow * symbol->size + left ] = '1';
            matrix[ outRow * symbol->size + left + symbol->region + 1 ] = localY % 2 == 0 ? '1' : '0';
            for( localX = 0; localX < symbol->region; ++localX )
            {
               col = regionX * symbol->region + localX; outCol = left + 1 + localX;
               if( placement[ row * dataCols + col ] == 1 ) matrix[ outRow * symbol->size + outCol ] = '1';
            }
         }
      }
      memset( matrix + ( top + symbol->region + 1 ) * symbol->size, '1', symbol->size );
   }
   result = hb_itemArrayNew( symbol->size );
   for( row = 0; row < symbol->size; ++row ) hb_arraySetCL( result, row + 1, matrix + row * symbol->size, symbol->size );
   hb_itemReturnRelease( result );
}

#pragma ENDDUMP
