/*
 * Copyright 2012 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#import "ZXEncoderTestCase.h"

@implementation ZXEncoderTestCase

- (void)testGetAlphanumericCode {
  // The first ten code points are numbers.
  for (int i = 0; i < 10; ++i) {
    STAssertEquals([ZXEncoder alphanumericCode:'0' + i], i, @"Expected %d", i);
  }

  // The next 26 code points are capital alphabet letters.
  for (int i = 10; i < 36; ++i) {
    STAssertEquals([ZXEncoder alphanumericCode:'A' + i - 10], i, @"Expected %d", i);
  }

  // Others are symbol letters
  STAssertEquals([ZXEncoder alphanumericCode:' '], 36, @"Expected %d", 36);
  STAssertEquals([ZXEncoder alphanumericCode:'$'], 37, @"Expected %d", 37);
  STAssertEquals([ZXEncoder alphanumericCode:'%'], 38, @"Expected %d", 38);
  STAssertEquals([ZXEncoder alphanumericCode:'*'], 39, @"Expected %d", 39);
  STAssertEquals([ZXEncoder alphanumericCode:'+'], 40, @"Expected %d", 40);
  STAssertEquals([ZXEncoder alphanumericCode:'-'], 41, @"Expected %d", 41);
  STAssertEquals([ZXEncoder alphanumericCode:'.'], 42, @"Expected %d", 42);
  STAssertEquals([ZXEncoder alphanumericCode:'/'], 43, @"Expected %d", 43);
  STAssertEquals([ZXEncoder alphanumericCode:':'], 44, @"Expected %d", 44);

  // Should return -1 for other letters;
  STAssertEquals([ZXEncoder alphanumericCode:'a'], -1, @"Expected -1");
  STAssertEquals([ZXEncoder alphanumericCode:'#'], -1, @"Expected -1");
  STAssertEquals([ZXEncoder alphanumericCode:'\0'], -1, @"Expected -1");
}

- (void)testChooseMode {
  // Numeric mode.
  STAssertEqualObjects([ZXEncoder chooseMode:@"0"], [ZXMode numericMode], @"Expected numeric mode");
  STAssertEqualObjects([ZXEncoder chooseMode:@"0123456789"], [ZXMode numericMode], @"Expected numeric mode");
  // Alphanumeric mode.
  STAssertEqualObjects([ZXEncoder chooseMode:@"A"], [ZXMode alphanumericMode], @"Expected alphanumeric mode");
  STAssertEqualObjects([ZXEncoder chooseMode:@"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"],
                       [ZXMode alphanumericMode], @"Expected alphanumeric mode");
  // 8-bit byte mode.
  STAssertEqualObjects([ZXEncoder chooseMode:@"a"], [ZXMode byteMode], @"Expected byte mode");
  STAssertEqualObjects([ZXEncoder chooseMode:@"#"], [ZXMode byteMode], @"Expected byte mode");
  STAssertEqualObjects([ZXEncoder chooseMode:@""], [ZXMode byteMode], @"Expected byte mode");
  // Kanji mode.  We used to use MODE_KANJI for these, but we stopped
  // doing that as we cannot distinguish Shift_JIS from other encodings
  // from data bytes alone.  See also comments in qrcode_encoder.h.

  // AIUE in Hiragana in Shift_JIS
  int8_t hiraganaBytes[8] = {0x8, 0xa, 0x8, 0xa, 0x8, 0xa, 0x8, 0xa6};
  STAssertEqualObjects([ZXEncoder chooseMode:[self shiftJISString:hiraganaBytes bytesLen:8]], [ZXMode byteMode],
                       @"Expected byte mode");

  // Nihon in Kanji in Shift_JIS.
  int8_t kanjiBytes[4] = {0x9, 0xf, 0x9, 0x7b};
  STAssertEqualObjects([ZXEncoder chooseMode:[self shiftJISString:kanjiBytes bytesLen:4]], [ZXMode byteMode],
                       @"Expected byte mode");

  // Sou-Utsu-Byou in Kanji in Shift_JIS.
  int8_t kanjiBytes2[6] = {0xe, 0x4, 0x9, 0x5, 0x9, 0x61};
  STAssertEqualObjects([ZXEncoder chooseMode:[self shiftJISString:kanjiBytes2 bytesLen:6]], [ZXMode byteMode],
                       @"Expected byte mode");
}

- (void)testEncode {
  ZXQRCode *qrCode = [ZXEncoder encode:@"ABCDEF" ecLevel:[ZXErrorCorrectionLevel errorCorrectionLevelH] error:nil];
  // The following is a valid QR Code that can be read by cell phones.
  NSString *expected =
    @"<<\n"
    " mode: ALPHANUMERIC\n"
    " ecLevel: H\n"
    " version: 1\n"
    " maskPattern: 0\n"
    " matrix:\n"
    " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n"
    " 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n"
    " 1 0 1 1 1 0 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1\n"
    " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1\n"
    " 1 0 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1\n"
    " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1\n"
    " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
    " 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0\n"
    " 0 0 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1\n"
    " 1 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0\n"
    " 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 0\n"
    " 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0\n"
    " 0 0 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 1 1 1 0\n"
    " 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0\n"
    " 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 1\n"
    " 1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1\n"
    " 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1\n"
    " 1 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0 1 0\n"
    " 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1 0 1\n"
    " 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1\n"
    " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1\n"
    ">>\n";
  STAssertEqualObjects([qrCode description], expected, @"Expected qr code to equal %@", expected);
}

- (void)testSimpleUTF8ECI {
  ZXEncodeHints *hints = [ZXEncodeHints hints];
  hints.encoding = NSUTF8StringEncoding;
  ZXQRCode *qrCode = [ZXEncoder encode:@"hello" ecLevel:[ZXErrorCorrectionLevel errorCorrectionLevelH] hints:hints error:nil];
  NSString *expected =
    @"<<\n"
    " mode: BYTE\n"
    " ecLevel: H\n"
    " version: 1\n"
    " maskPattern: 3\n"
    " matrix:\n"
    " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n"
    " 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1\n"
    " 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 1 1 0 1\n"
    " 1 0 1 1 1 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 1\n"
    " 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1\n"
    " 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1\n"
    " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n"
    " 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0\n"
    " 0 0 1 1 0 0 1 1 1 1 0 0 0 1 1 0 1 0 0 0 0\n"
    " 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 0 1 1 1 0\n"
    " 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 1 1 1\n"
    " 1 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 1 0\n"
    " 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 1 0 0\n"
    " 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 1\n"
    " 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0\n"
    " 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1\n"
    " 1 0 1 1 1 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0\n"
    " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0\n"
    " 1 0 1 1 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0\n"
    " 1 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0\n"
    " 1 1 1 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 0 0 0\n"
    ">>\n";
  STAssertEqualObjects([qrCode description], expected, @"Expected qr code to equal %@", expected);
}

- (void)testAppendModeInfo {
  ZXBitArray *bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendModeInfo:[ZXMode numericMode] bits:bits];
  NSString *expected = @" ...X";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
}

- (void)testAppendLengthInfo {
  ZXBitArray *bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendLengthInfo:1  // 1 letter (1/1).
                      version:[ZXQRCodeVersion versionForNumber:1]
                         mode:[ZXMode numericMode]
                         bits:bits
                        error:nil];
  NSString *expected = @" ........ .X";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);  // 10 bits.
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendLengthInfo:2  // 2 letter (2/1).
                      version:[ZXQRCodeVersion versionForNumber:10]
                         mode:[ZXMode alphanumericMode]
                         bits:bits
                        error:nil];
  expected = @" ........ .X.";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);  // 11 bits.
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendLengthInfo:255  // 255 letter (255/1).
                      version:[ZXQRCodeVersion versionForNumber:27]
                         mode:[ZXMode byteMode]
                         bits:bits
                        error:nil];
  expected = @" ........ XXXXXXXX";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);  // 16 bits.
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendLengthInfo:512  // 512 letter (1024/2).
                      version:[ZXQRCodeVersion versionForNumber:40]
                         mode:[ZXMode kanjiMode]
                         bits:bits
                        error:nil];
  expected = @" ..X..... ....";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);  // 12 bits.
}

- (void)testAppendBytes {
  // Should use appendNumericBytes.
  // 1 = 01 = 0001 in 4 bits.
  ZXBitArray *bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendBytes:@"1" mode:[ZXMode numericMode] bits:bits encoding:DEFAULT_BYTE_MODE_ENCODING error:nil];
  NSString *expected = @" ...X";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // Should use appendAlphanumericBytes.
  // A = 10 = 0xa = 001010 in 6 bits
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendBytes:@"A" mode:[ZXMode alphanumericMode] bits:bits encoding:DEFAULT_BYTE_MODE_ENCODING error:nil];
  expected = @" ..X.X.";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC.
  NSError *error;
  if ([ZXEncoder appendBytes:@"a" mode:[ZXMode alphanumericMode] bits:bits encoding:DEFAULT_BYTE_MODE_ENCODING error:&error] ||
      error.code != ZXWriterError) {
    STFail(@"Expected ZXWriterError");
  }
  // Should use append8BitBytes.
  // 0x61, 0x62, 0x63
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendBytes:@"abc" mode:[ZXMode byteMode] bits:bits encoding:DEFAULT_BYTE_MODE_ENCODING error:nil];
  expected = @" .XX....X .XX...X. .XX...XX";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // Anything can be encoded in QRCode.MODE_8BIT_BYTE.
  [ZXEncoder appendBytes:@"\0" mode:[ZXMode byteMode] bits:bits encoding:DEFAULT_BYTE_MODE_ENCODING error:nil];
  // Should use appendKanjiBytes.
  // 0x93, 0x5f
  bits = [[ZXBitArray alloc] init];
  int8_t bytes[2] = {0x93, 0x5f};
  [ZXEncoder appendBytes:[self shiftJISString:bytes bytesLen:2] mode:[ZXMode kanjiMode] bits:bits encoding:DEFAULT_BYTE_MODE_ENCODING error:nil];
  expected = @" .XX.XX.. XXXXX";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
}

- (void)testTerminateBits {
  ZXBitArray *v = [[ZXBitArray alloc] init];
  [ZXEncoder terminateBits:0 bits:v error:nil];
  STAssertEqualObjects([v description], @"", @"Expected v to equal \"\"");
  v = [[ZXBitArray alloc] init];
  [ZXEncoder terminateBits:1 bits:v error:nil];
  NSString *expected = @" ........";
  STAssertEqualObjects([v description], expected, @"Expected v to equal %@");
  v = [[ZXBitArray alloc] init];
  [v appendBits:0 numBits:3];  // Append 000
  [ZXEncoder terminateBits:1 bits:v error:nil];
  expected = @" ........";
  STAssertEqualObjects([v description], expected, @"Expected v to equal %@");
  v = [[ZXBitArray alloc] init];
  [v appendBits:0 numBits:5];  // Append 00000
  [ZXEncoder terminateBits:1 bits:v error:nil];
  expected = @" ........";
  STAssertEqualObjects([v description], expected, @"Expected v to equal %@");
  v = [[ZXBitArray alloc] init];
  [v appendBits:0 numBits:8];  // Append 00000000
  [ZXEncoder terminateBits:1 bits:v error:nil];
  expected = @" ........";
  STAssertEqualObjects([v description], expected, @"Expected v to equal %@");
  v = [[ZXBitArray alloc] init];
  [ZXEncoder terminateBits:2 bits:v error:nil];
  expected = @" ........ XXX.XX..";
  STAssertEqualObjects([v description], expected, @"Expected v to equal %@");
  v = [[ZXBitArray alloc] init];
  [v appendBits:0 numBits:1];  // Append 0
  [ZXEncoder terminateBits:3 bits:v error:nil];
  expected = @" ........ XXX.XX.. ...X...X";
  STAssertEqualObjects([v description], expected, @"Expected v to equal %@");
}

- (void)testGetNumDataBytesAndNumECBytesForBlockID {
  int numDataBytes[1] = {0};
  int numEcBytes[1] = {0};
  // Version 1-H.
  [ZXEncoder numDataBytesAndNumECBytesForBlockID:26 numDataBytes:9 numRSBlocks:1 blockID:0
                             numDataBytesInBlock:numDataBytes numECBytesInBlock:numEcBytes error:nil];
  STAssertEquals(numDataBytes[0], 9, @"Expected numDataBytes[0] to equal %d", 9);
  STAssertEquals(numEcBytes[0], 17, @"Expected numEcBytes[0] to equal %d", 17);

  // Version 3-H.  2 blocks.
  [ZXEncoder numDataBytesAndNumECBytesForBlockID:70 numDataBytes:26 numRSBlocks:2 blockID:0
                             numDataBytesInBlock:numDataBytes numECBytesInBlock:numEcBytes error:nil];
  STAssertEquals(numDataBytes[0], 13, @"Expected numDataBytes[0] to equal %d", 13);
  STAssertEquals(numEcBytes[0], 22, @"Expected numEcBytes[0] to equal %d", 22);
  [ZXEncoder numDataBytesAndNumECBytesForBlockID:70 numDataBytes:26 numRSBlocks:2 blockID:1
                             numDataBytesInBlock:numDataBytes numECBytesInBlock:numEcBytes error:nil];
  STAssertEquals(numDataBytes[0], 13, @"Expected numDataBytes[0] to equal %d", 13);
  STAssertEquals(numEcBytes[0], 22, @"Expected numEcBytes[0] to equal %d", 22);

  // Version 7-H. (4 + 1) blocks.
  [ZXEncoder numDataBytesAndNumECBytesForBlockID:196 numDataBytes:66 numRSBlocks:5 blockID:0
                             numDataBytesInBlock:numDataBytes numECBytesInBlock:numEcBytes error:nil];
  STAssertEquals(numDataBytes[0], 13, @"Expected numDataBytes[0] to equal %d", 13);
  STAssertEquals(numEcBytes[0], 26, @"Expected numEcBytes[0] to equal %d", 26);
  [ZXEncoder numDataBytesAndNumECBytesForBlockID:196 numDataBytes:66 numRSBlocks:5 blockID:4
                             numDataBytesInBlock:numDataBytes numECBytesInBlock:numEcBytes error:nil];
  STAssertEquals(numDataBytes[0], 14, @"Expected numDataBytes[0] to equal %d", 14);
  STAssertEquals(numEcBytes[0], 26, @"Expected numEcBytes[0] to equal %d", 22);

  // Version 40-H. (20 + 61) blocks.
  [ZXEncoder numDataBytesAndNumECBytesForBlockID:3706 numDataBytes:1276 numRSBlocks:81 blockID:0
                             numDataBytesInBlock:numDataBytes numECBytesInBlock:numEcBytes error:nil];
  STAssertEquals(numDataBytes[0], 15, @"Expected numDataBytes[0] to equal %d", 15);
  STAssertEquals(numEcBytes[0], 30, @"Expected numEcBytes[0] to equal %d", 30);
  [ZXEncoder numDataBytesAndNumECBytesForBlockID:3706 numDataBytes:1276 numRSBlocks:81 blockID:20
                             numDataBytesInBlock:numDataBytes numECBytesInBlock:numEcBytes error:nil];
  STAssertEquals(numDataBytes[0], 16, @"Expected numDataBytes[0] to equal %d", 16);
  STAssertEquals(numEcBytes[0], 30, @"Expected numEcBytes[0] to equal %d", 30);
  [ZXEncoder numDataBytesAndNumECBytesForBlockID:3706 numDataBytes:1276 numRSBlocks:81 blockID:80
                             numDataBytesInBlock:numDataBytes numECBytesInBlock:numEcBytes error:nil];
  STAssertEquals(numDataBytes[0], 16, @"Expected numDataBytes[0] to equal %d", 16);
  STAssertEquals(numEcBytes[0], 30, @"Expected numEcBytes[0] to equal %d", 30);
}

- (void)testInterleaveWithECBytes {
  const int dataBytesLen = 9;
  int8_t dataBytes[dataBytesLen] = {32, 65, 205, 69, 41, 220, 46, 128, 236};
  ZXBitArray *in = [[ZXBitArray alloc] init];
  for (int i = 0; i < dataBytesLen; i++) {
    [in appendBits:dataBytes[i] numBits:8];
  }
  ZXBitArray *out = [ZXEncoder interleaveWithECBytes:in numTotalBytes:26 numDataBytes:9 numRSBlocks:1 error:nil];
  const int expectedLen = 26;
  int8_t expected[expectedLen] = {
    // Data bytes.
    32, 65, 205, 69, 41, 220, 46, 128, 236,
    // Error correction bytes.
    42, 159, 74, 221, 244, 169, 239, 150, 138, 70,
    237, 85, 224, 96, 74, 219, 61,
  };
  STAssertEquals(out.sizeInBytes, expectedLen, @"Expected out sizeInBytes to equal %d", expectedLen);
  int8_t outArray[expectedLen];
  memset(outArray, 0, expectedLen * sizeof(int8_t));
  [out toBytes:0 array:outArray offset:0 numBytes:expectedLen];
  for (int x = 0; x < expectedLen; x++) {
    STAssertEquals(outArray[x], expected[x], @"Expected outArray[%d] to equal %d", x, expected[x]);
  }
  const int dataBytesLen2 = 62;
  int8_t dataBytes2[dataBytesLen2] = {
    67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182,
    198, 214, 230, 247, 7, 23, 39, 55, 71, 87, 103, 119, 135,
    151, 166, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166,
    182, 198, 214, 230, 247, 7, 23, 39, 55, 71, 87, 103, 119,
    135, 151, 160, 236, 17, 236, 17, 236, 17, 236,
    17
  };
  in = [[ZXBitArray alloc] init];
  for (int i = 0; i < dataBytesLen2; i++) {
    [in appendBits:dataBytes2[i] numBits:8];
  }
  out = [ZXEncoder interleaveWithECBytes:in numTotalBytes:134 numDataBytes:62 numRSBlocks:4 error:nil];
  const int expectedLen2 = 134;
  int8_t expected2[expectedLen2] = {
    // Data bytes.
    67, 230, 54, 55, 70, 247, 70, 71, 22, 7, 86, 87, 38, 23, 102, 103, 54, 39,
    118, 119, 70, 55, 134, 135, 86, 71, 150, 151, 102, 87, 166,
    160, 118, 103, 182, 236, 134, 119, 198, 17, 150,
    135, 214, 236, 166, 151, 230, 17, 182,
    166, 247, 236, 198, 22, 7, 17, 214, 38, 23, 236, 39,
    17,
    // Error correction bytes.
    175, 155, 245, 236, 80, 146, 56, 74, 155, 165,
    133, 142, 64, 183, 132, 13, 178, 54, 132, 108, 45,
    113, 53, 50, 214, 98, 193, 152, 233, 147, 50, 71, 65,
    190, 82, 51, 209, 199, 171, 54, 12, 112, 57, 113, 155, 117,
    211, 164, 117, 30, 158, 225, 31, 190, 242, 38,
    140, 61, 179, 154, 214, 138, 147, 87, 27, 96, 77, 47,
    187, 49, 156, 214,
  };
  STAssertEquals(out.sizeInBytes, expectedLen2, @"Expected out sizeInBytes to equal %d", expectedLen2);
  int8_t outArray2[expectedLen2];
  memset(outArray2, 0, expectedLen2 * sizeof(int8_t));
  [out toBytes:0 array:outArray2 offset:0 numBytes:expectedLen2];
  for (int x = 0; x < expectedLen2; x++) {
    STAssertEquals(outArray2[x], expected2[x], @"Expected outArray[%d] to equal %d", x, expected2[x]);
  }
}

- (void)testAppendNumericBytes {
  // 1 = 01 = 0001 in 4 bits.
  ZXBitArray *bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendNumericBytes:@"1" bits:bits];
  NSString *expected = @" ...X";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // 12 = 0xc = 0001100 in 7 bits.
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendNumericBytes:@"12" bits:bits];
  expected = @" ...XX..";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // 123 = 0x7b = 0001111011 in 10 bits.
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendNumericBytes:@"123" bits:bits];
  expected = @" ...XXXX. XX";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // 1234 = "123" + "4" = 0001111011 + 0100
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendNumericBytes:@"1234" bits:bits];
  expected = @" ...XXXX. XX.X..";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // Empty.
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendNumericBytes:@"" bits:bits];
  STAssertEqualObjects([bits description], @"", @"Expected bits to equal \"\"");
}

- (void)testAppendAlphanumericBytes {
  // A = 10 = 0xa = 001010 in 6 bits
  ZXBitArray *bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendAlphanumericBytes:@"A" bits:bits error:nil];
  NSString *expected = @" ..X.X.";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendAlphanumericBytes:@"AB" bits:bits error:nil];
  expected = @" ..XXX..X X.X";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // ABC = "AB" + "C" = 00111001101 + 001100
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendAlphanumericBytes:@"ABC" bits:bits error:nil];
  expected = @" ..XXX..X X.X..XX. .";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // Empty.
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder appendAlphanumericBytes:@"" bits:bits error:nil];
  STAssertEqualObjects([bits description], @"", @"Expected bits to equal \"\"");
  // Invalid data.
  NSError *error;
  if ([ZXEncoder appendAlphanumericBytes:@"abc" bits:[[ZXBitArray alloc] init] error:&error] || error.code != ZXWriterError) {
    STFail(@"Expected ZXWriterError");
  }
}

- (void)testAppend8BitBytes {
  // 0x61, 0x62, 0x63
  ZXBitArray *bits = [[ZXBitArray alloc] init];
  [ZXEncoder append8BitBytes:@"abc" bits:bits encoding:DEFAULT_BYTE_MODE_ENCODING];
  NSString *expected = @" .XX....X .XX...X. .XX...XX";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  // Empty.
  bits = [[ZXBitArray alloc] init];
  [ZXEncoder append8BitBytes:@"" bits:bits encoding:DEFAULT_BYTE_MODE_ENCODING];
  STAssertEqualObjects([bits description], @"", @"Expected bits to equal \"\"");
}

// Numbers are from page 21 of JISX0510:2004
- (void)testAppendKanjiBytes {
  ZXBitArray *bits = [[ZXBitArray alloc] init];
  int8_t bytes[2] = {0x93,0x5f};
  [ZXEncoder appendKanjiBytes:[self shiftJISString:bytes bytesLen:2] bits:bits error:nil];
  NSString *expected = @" .XX.XX.. XXXXX";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
  int8_t bytes2[2] = {0xe4,0xaa};
  [ZXEncoder appendKanjiBytes:[self shiftJISString:bytes2 bytesLen:2] bits:bits error:nil];
  expected = @" .XX.XX.. XXXXXXX. X.X.X.X. X.";
  STAssertEqualObjects([bits description], expected, @"Expected bits to equal %@", expected);
}

// Numbers are from http://www.swetake.com/qr/qr3.html and
// http://www.swetake.com/qr/qr9.html
- (void)testGenerateECBytes {
  const int dataBytesLen = 9;
  int8_t dataBytes[dataBytesLen] = {32, 65, 205, 69, 41, 220, 46, 128, 236};
  int8_t *ecBytes = [ZXEncoder generateECBytes:dataBytes numDataBytes:dataBytesLen numEcBytesInBlock:17];
  const int expectedLen = 17;
  int expected[expectedLen] = {
    42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61
  };
  for (int x = 0; x < expectedLen; x++) {
    STAssertEquals(ecBytes[x] & 0xFF, expected[x], @"Expected exBytes[%d] to equal %d", x, expected[x]);
  }
  free(ecBytes);
  const int dataBytesLen2 = 15;
  int8_t dataBytes2[dataBytesLen2] = {67, 70, 22, 38, 54, 70, 86, 102, 118,
    134, 150, 166, 182, 198, 214};
  int8_t *ecBytes2 = [ZXEncoder generateECBytes:dataBytes2 numDataBytes:dataBytesLen2 numEcBytesInBlock:18];
  const int expectedLen2 = 18;
  int expected2[expectedLen2] = {
    175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187
  };
  for (int x = 0; x < expectedLen2; x++) {
    STAssertEquals(ecBytes2[x] & 0xFF, expected2[x], @"Expected exBytes[%d] to equal %d", x, expected2[x]);
  }
  free(ecBytes2);
  // High-order zero coefficient case.
  const int dataBytesLen3 = 9;
  int8_t dataBytes3[dataBytesLen3] = {32, 49, 205, 69, 42, 20, 0, 236, 17};
  int8_t *ecBytes3 = [ZXEncoder generateECBytes:dataBytes3 numDataBytes:dataBytesLen3 numEcBytesInBlock:17];
  const int expectedLen3 = 17;
  int expected3[expectedLen3] = {
    0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213
  };
  for (int x = 0; x < expectedLen3; x++) {
    STAssertEquals(ecBytes3[x] & 0xFF, expected3[x], @"Expected exBytes[%d] to equal %d", x, expected3[x]);
  }
  free(ecBytes3);
}

- (void)testBugInBitVectorNumBytes {
  // There was a bug in BitVector.sizeInBytes() that caused it to return a
  // smaller-by-one value (ex. 1465 instead of 1466) if the number of bits
  // in the vector is not 8-bit aligned.  In QRCodeEncoder::InitQRCode(),
  // BitVector::sizeInBytes() is used for finding the smallest QR Code
  // version that can fit the given data.  Hence there were corner cases
  // where we chose a wrong QR Code version that cannot fit the given
  // data.  Note that the issue did not occur with MODE_8BIT_BYTE, as the
  // bits in the bit vector are always 8-bit aligned.
  //
  // Before the bug was fixed, the following test didn't pass, because:
  //
  // - MODE_NUMERIC is chosen as all bytes in the data are '0'
  // - The 3518-byte numeric data needs 1466 bytes
  //   - 3518 / 3 * 10 + 7 = 11727 bits = 1465.875 bytes
  //   - 3 numeric bytes are encoded in 10 bits, hence the first
  //     3516 bytes are encoded in 3516 / 3 * 10 = 11720 bits.
  //   - 2 numeric bytes can be encoded in 7 bits, hence the last
  //     2 bytes are encoded in 7 bits.
  // - The version 27 QR Code with the EC level L has 1468 bytes for data.
  //   - 1828 - 360 = 1468
  // - In InitQRCode(), 3 bytes are reserved for a header.  Hence 1465 bytes
  //   (1468 -3) are left for data.
  // - Because of the bug in BitVector::sizeInBytes(), InitQRCode() determines
  //   the given data can fit in 1465 bytes, despite it needs 1466 bytes.
  // - Hence QRCodeEncoder.encode() failed and returned false.
  //   - To be precise, it needs 11727 + 4 (getMode info) + 14 (length info) =
  //     11745 bits = 1468.125 bytes are needed (i.e. cannot fit in 1468
  //     bytes).
  NSMutableString *builder = [NSMutableString stringWithCapacity:3518];
  for (int x = 0; x < 3518; x++) {
    [builder appendString:@"0"];
  }
  ZXQRCode *qrCode = [ZXEncoder encode:builder ecLevel:[ZXErrorCorrectionLevel errorCorrectionLevelL] error:nil];
  STAssertNotNil(qrCode, @"Excepted QR code");
}

- (NSString *)shiftJISString:(int8_t *)bytes bytesLen:(int)bytesLen {
  return [[NSString alloc] initWithBytes:bytes length:bytesLen encoding:NSShiftJISStringEncoding];
}

@end
