import 'dart:typed_data'; import 'bzip2/bz2_bit_reader.dart'; import 'bzip2/bzip2.dart'; import 'util/archive_exception.dart'; import 'util/byte_order.dart'; import 'util/input_stream.dart'; import 'util/output_stream.dart'; /// Decompress bzip2 compressed data. /// Derived from libbzip2 (http://www.bzip.org). class BZip2Decoder { List decodeBytes(List data, {bool verify = false}) { return decodeBuffer(InputStream(data, byteOrder: BIG_ENDIAN), verify: verify); } void decodeStream(InputStreamBase input, OutputStreamBase output, {bool verify = false}) { final br = Bz2BitReader(input); _groupPos = 0; _groupNo = 0; _gSel = 0; _gMinlen = 0; if (br.readByte() != BZip2.bzhSignature[0] || br.readByte() != BZip2.bzhSignature[1] || br.readByte() != BZip2.bzhSignature[2]) { throw ArchiveException('Invalid Signature'); } _blockSize100k = br.readByte() - BZip2.hdr0; if (_blockSize100k < 0 || _blockSize100k > 9) { throw ArchiveException('Invalid BlockSize'); } _tt = Uint32List(_blockSize100k * 100000); var combinedCrc = 0; while (true) { final type = _readBlockType(br); if (type == blockCompressed) { var storedBlockCrc = 0; storedBlockCrc = (storedBlockCrc << 8) | br.readByte(); storedBlockCrc = (storedBlockCrc << 8) | br.readByte(); storedBlockCrc = (storedBlockCrc << 8) | br.readByte(); storedBlockCrc = (storedBlockCrc << 8) | br.readByte(); var blockCrc = _readCompressed(br, output); blockCrc = BZip2.finalizeCrc(blockCrc); if (verify && blockCrc != storedBlockCrc) { throw ArchiveException('Invalid block checksum.'); } combinedCrc = ((combinedCrc << 1) | (combinedCrc >> 31)) & 0xffffffff; combinedCrc ^= blockCrc; } else if (type == blockEos) { var storedCrc = 0; storedCrc = (storedCrc << 8) | br.readByte(); storedCrc = (storedCrc << 8) | br.readByte(); storedCrc = (storedCrc << 8) | br.readByte(); storedCrc = (storedCrc << 8) | br.readByte(); if (verify && storedCrc != combinedCrc) { throw ArchiveException( 'Invalid combined checksum: $combinedCrc : $storedCrc'); } output.flush(); return; } } } List decodeBuffer(InputStreamBase input, {bool verify = false, OutputStreamBase? output}) { output ??= OutputStream(); final br = Bz2BitReader(input); _groupPos = 0; _groupNo = 0; _gSel = 0; _gMinlen = 0; if (br.readByte() != BZip2.bzhSignature[0] || br.readByte() != BZip2.bzhSignature[1] || br.readByte() != BZip2.bzhSignature[2]) { throw ArchiveException('Invalid Signature'); } _blockSize100k = br.readByte() - BZip2.hdr0; if (_blockSize100k < 0 || _blockSize100k > 9) { throw ArchiveException('Invalid BlockSize'); } _tt = Uint32List(_blockSize100k * 100000); var combinedCrc = 0; while (true) { final type = _readBlockType(br); if (type == blockCompressed) { var storedBlockCrc = 0; storedBlockCrc = (storedBlockCrc << 8) | br.readByte(); storedBlockCrc = (storedBlockCrc << 8) | br.readByte(); storedBlockCrc = (storedBlockCrc << 8) | br.readByte(); storedBlockCrc = (storedBlockCrc << 8) | br.readByte(); var blockCrc = _readCompressed(br, output); blockCrc = BZip2.finalizeCrc(blockCrc); if (verify && blockCrc != storedBlockCrc) { throw ArchiveException('Invalid block checksum.'); } combinedCrc = ((combinedCrc << 1) | (combinedCrc >> 31)) & 0xffffffff; combinedCrc ^= blockCrc; } else if (type == blockEos) { var storedCrc = 0; storedCrc = (storedCrc << 8) | br.readByte(); storedCrc = (storedCrc << 8) | br.readByte(); storedCrc = (storedCrc << 8) | br.readByte(); storedCrc = (storedCrc << 8) | br.readByte(); if (verify && storedCrc != combinedCrc) { throw ArchiveException( 'Invalid combined checksum: $combinedCrc : $storedCrc'); } output.flush(); if (output is! OutputStream) { return []; } return output.getBytes(); } } } int _readBlockType(Bz2BitReader br) { var eos = true; var compressed = true; // .eos_magic:48 0x177245385090 (BCD sqrt(pi)) // .compressed_magic:48 0x314159265359 (BCD (pi)) for (var i = 0; i < 6; ++i) { var b = br.readByte(); if (b != BZip2.compressedMagic[i]) { compressed = false; } if (b != BZip2.eosMagic[i]) { eos = false; } if (!eos && !compressed) { throw ArchiveException('Invalid Block Signature'); } } return (compressed) ? blockCompressed : blockEos; } int _readCompressed(Bz2BitReader br, OutputStreamBase output) { var blockRandomized = br.readBits(1); var origPtr = br.readBits(8); origPtr = (origPtr << 8) | br.readBits(8); origPtr = (origPtr << 8) | br.readBits(8); // Receive the mapping table _inUse16 = Uint8List(16); for (var i = 0; i < 16; ++i) { _inUse16[i] = br.readBits(1); } _inUse = Uint8List(256); for (var i = 0, k = 0; i < 16; ++i, k += 16) { if (_inUse16[i] != 0) { for (var j = 0; j < 16; ++j) { _inUse[k + j] = br.readBits(1); } } } _makeMaps(); if (_numInUse == 0) { throw ArchiveException('Data error'); } var alphaSize = _numInUse + 2; // Now the selectors var numGroups = br.readBits(3); if (numGroups < 2 || numGroups > 6) { throw ArchiveException('Data error'); } _numSelectors = br.readBits(15); if (_numSelectors < 1) { throw ArchiveException('Data error'); } _selectorMtf = Uint8List(bzMaxSelectors); _selector = Uint8List(bzMaxSelectors); for (var i = 0; i < _numSelectors; ++i) { var j = 0; while (true) { var b = br.readBits(1); if (b == 0) { break; } j++; if (j >= numGroups) { throw ArchiveException('Data error'); } } _selectorMtf[i] = j; } // Undo the MTF values for the selectors. final pos = Uint8List(bzNGroups); for (var i = 0; i < numGroups; ++i) { pos[i] = i; } for (var i = 0; i < _numSelectors; ++i) { var v = _selectorMtf[i]; var tmp = pos[v]; while (v > 0) { pos[v] = pos[v - 1]; v--; } pos[0] = tmp; _selector[i] = tmp; } // Now the coding tables _len = List.filled(bzNGroups, BZip2.emptyUint8List); for (var t = 0; t < numGroups; ++t) { _len[t] = Uint8List(bzMaxAlphaSize); var c = br.readBits(5); for (var i = 0; i < alphaSize; ++i) { while (true) { if (c < 1 || c > 20) { throw ArchiveException('Data error'); } var b = br.readBits(1); if (b == 0) { break; } b = br.readBits(1); if (b == 0) { c++; } else { c--; } } _len[t][i] = c; } } // Create the Huffman decoding tables _limit = List.filled(bzNGroups, BZip2.emptyInt32List); _base = List.filled(bzNGroups, BZip2.emptyInt32List); _perm = List.filled(bzNGroups, BZip2.emptyInt32List); _minLens = Int32List(bzNGroups); for (var t = 0; t < numGroups; t++) { _limit[t] = Int32List(bzMaxAlphaSize); _base[t] = Int32List(bzMaxAlphaSize); _perm[t] = Int32List(bzMaxAlphaSize); var minLen = 32; var maxLen = 0; for (var i = 0; i < alphaSize; ++i) { if (_len[t][i] > maxLen) { maxLen = _len[t][i]; } if (_len[t][i] < minLen) { minLen = _len[t][i]; } } _hbCreateDecodeTables( _limit[t], _base[t], _perm[t], _len[t], minLen, maxLen, alphaSize); _minLens[t] = minLen; } // Now the MTF values var eob = _numInUse + 1; var nblockMAX = 100000 * _blockSize100k; _unzftab = Int32List(256); // MTF init _mtfa = Uint8List(mtfaSize); _mtfbase = Int32List(256 ~/ mtflSize); var kk = mtfaSize - 1; for (var ii = 256 ~/ mtflSize - 1; ii >= 0; ii--) { for (var jj = mtflSize - 1; jj >= 0; jj--) { _mtfa[kk] = ii * mtflSize + jj; kk--; } _mtfbase[ii] = kk + 1; } var nblock = 0; _groupPos = 0; _groupNo = -1; var nextSym = _getMtfVal(br); var uc = 0; while (true) { if (nextSym == eob) { break; } if (nextSym == bzRunA || nextSym == bzRunB) { var es = -1; var N = 1; do { // Check that N doesn't get too big, so that es doesn't // go negative. The maximum value that can be // RUNA/RUNB encoded is equal to the block size (post // the initial RLE), viz, 900k, so bounding N at 2 // million should guard against overflow without // rejecting any legitimate inputs. if (N >= 2 * 1024 * 1024) { throw ArchiveException('Data error'); } if (nextSym == bzRunA) { es = es + (0 + 1) * N; } else if (nextSym == bzRunB) { es = es + (1 + 1) * N; } N = N * 2; nextSym = _getMtfVal(br); } while (nextSym == bzRunA || nextSym == bzRunB); es++; uc = _seqToUnseq[_mtfa[_mtfbase[0]]]; _unzftab[uc] += es; while (es > 0) { if (nblock >= nblockMAX) { throw ArchiveException('Data error'); } _tt[nblock] = uc; nblock++; es--; } continue; } else { if (nblock >= nblockMAX) { throw ArchiveException('Data error'); } // uc = MTF ( nextSym-1 ) var nn = nextSym - 1; if (nn < mtflSize) { // avoid general-case expense var pp = _mtfbase[0]; uc = _mtfa[pp + nn]; while (nn > 3) { var z = pp + nn; _mtfa[(z)] = _mtfa[(z) - 1]; _mtfa[(z) - 1] = _mtfa[(z) - 2]; _mtfa[(z) - 2] = _mtfa[(z) - 3]; _mtfa[(z) - 3] = _mtfa[(z) - 4]; nn -= 4; } while (nn > 0) { _mtfa[(pp + nn)] = _mtfa[(pp + nn) - 1]; nn--; } _mtfa[pp] = uc; } else { // general case var lno = nn ~/ mtflSize; var off = nn % mtflSize; var pp = _mtfbase[lno] + off; uc = _mtfa[pp]; while (pp > _mtfbase[lno]) { _mtfa[pp] = _mtfa[pp - 1]; pp--; } _mtfbase[lno]++; while (lno > 0) { _mtfbase[lno]--; _mtfa[_mtfbase[lno]] = _mtfa[_mtfbase[lno - 1] + mtflSize - 1]; lno--; } _mtfbase[0]--; _mtfa[_mtfbase[0]] = uc; if (_mtfbase[0] == 0) { kk = mtfaSize - 1; for (var ii = 256 ~/ mtflSize - 1; ii >= 0; ii--) { for (var jj = mtflSize - 1; jj >= 0; jj--) { _mtfa[kk] = _mtfa[_mtfbase[ii] + jj]; kk--; } _mtfbase[ii] = kk + 1; } } } // end uc = MTF ( nextSym-1 ) _unzftab[_seqToUnseq[uc]]++; _tt[nblock] = (_seqToUnseq[uc]); nblock++; nextSym = _getMtfVal(br); continue; } } // Now we know what nblock is, we can do a better sanity // check on s->origPtr. if (origPtr < 0 || origPtr >= nblock) { throw ArchiveException('Data error'); } // Set up cftab to facilitate generation of T^(-1) // Check: unzftab entries in range. for (var i = 0; i <= 255; i++) { if (_unzftab[i] < 0 || _unzftab[i] > nblock) { throw ArchiveException('Data error'); } } // Actually generate cftab. _cftab = Int32List(257); _cftab[0] = 0; for (var i = 1; i <= 256; i++) { _cftab[i] = _unzftab[i - 1]; } for (var i = 1; i <= 256; i++) { _cftab[i] += _cftab[i - 1]; } // Check: cftab entries in range. for (var i = 0; i <= 256; i++) { if (_cftab[i] < 0 || _cftab[i] > nblock) { // s->cftab[i] can legitimately be == nblock throw ArchiveException('Data error'); } } // Check: cftab entries non-descending. for (var i = 1; i <= 256; i++) { if (_cftab[i - 1] > _cftab[i]) { throw ArchiveException('Data error'); } } // compute the T^(-1) vector for (var i = 0; i < nblock; i++) { uc = (_tt[i] & 0xff); _tt[_cftab[uc]] |= (i << 8); _cftab[uc]++; } var blockCrc = BZip2.initialCrc; var tPos = _tt[origPtr] >> 8; var numBlockUsed = 0; int k0; var rNToGo = 0; var rTPos = 0; if (blockRandomized != 0) { rNToGo = 0; rTPos = 0; if (tPos >= 100000 * _blockSize100k) { throw ArchiveException('Data error'); } tPos = _tt[tPos]; k0 = tPos & 0xff; tPos >>= 8; numBlockUsed++; if (rNToGo == 0) { rNToGo = bz2RNums[rTPos]; rTPos++; if (rTPos == 512) { rTPos = 0; } } rNToGo--; k0 ^= ((rNToGo == 1) ? 1 : 0); } else { // c_tPos is unsigned, hence test < 0 is pointless. if (tPos >= 100000 * _blockSize100k) { return blockCrc; } tPos = _tt[tPos]; k0 = (tPos & 0xff); tPos >>= 8; numBlockUsed++; } // UnRLE to output var cStateOutLen = 0; var cStateOutCh = 0; var sSaveNBlockPP = nblock + 1; var cNBlockUsed = numBlockUsed; var cK0 = k0; int k1; if (blockRandomized != 0) { while (true) { // try to finish existing run while (true) { if (cStateOutLen == 0) { break; } output.writeByte(cStateOutCh); blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc); cStateOutLen--; } // can a run be started? if (cNBlockUsed == sSaveNBlockPP) { return blockCrc; } // Only caused by corrupt data stream? if (cNBlockUsed > sSaveNBlockPP) { throw ArchiveException('Data error.'); } cStateOutLen = 1; cStateOutCh = k0; tPos = _tt[tPos]; k1 = tPos & 0xff; tPos >>= 8; if (rNToGo == 0) { rNToGo = bz2RNums[rTPos]; rTPos++; if (rTPos == 512) { rTPos = 0; } } rNToGo--; k1 ^= ((rNToGo == 1) ? 1 : 0); cNBlockUsed++; if (cNBlockUsed == sSaveNBlockPP) { continue; } if (k1 != k0) { k0 = k1; continue; } cStateOutLen = 2; tPos = _tt[tPos]; k1 = tPos & 0xff; tPos >>= 8; if (rNToGo == 0) { rNToGo = bz2RNums[rTPos]; rTPos++; if (rTPos == 512) { rTPos = 0; } } k1 ^= ((rNToGo == 1) ? 1 : 0); cNBlockUsed++; if (cNBlockUsed == sSaveNBlockPP) { continue; } if (k1 != k0) { k0 = k1; continue; } cStateOutLen = 3; tPos = _tt[tPos]; k1 = tPos & 0xff; tPos >>= 8; if (rNToGo == 0) { rNToGo = bz2RNums[rTPos]; rTPos++; if (rTPos == 512) { rTPos = 0; } } k1 ^= ((rNToGo == 1) ? 1 : 0); cNBlockUsed++; if (cNBlockUsed == sSaveNBlockPP) { continue; } if (k1 != k0) { k0 = k1; continue; } tPos = _tt[tPos]; k1 = tPos & 0xff; tPos >>= 8; if (rNToGo == 0) { rNToGo = bz2RNums[rTPos]; rTPos++; if (rTPos == 512) { rTPos = 0; } } k1 ^= ((rNToGo == 1) ? 1 : 0); cNBlockUsed++; cStateOutLen = k1 + 4; tPos = _tt[tPos]; k0 = tPos & 0xff; tPos >>= 8; if (rNToGo == 0) { rNToGo = bz2RNums[rTPos]; rTPos++; if (rTPos == 512) { rTPos = 0; } } k0 ^= ((rNToGo == 1) ? 1 : 0); cNBlockUsed++; } } else { while (true) { // try to finish existing run if (cStateOutLen > 0) { while (true) { if (cStateOutLen == 1) { break; } output.writeByte(cStateOutCh); blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc); cStateOutLen--; } output.writeByte(cStateOutCh); blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc); } // Only caused by corrupt data stream? if (cNBlockUsed > sSaveNBlockPP) { throw ArchiveException('Data error'); } // can a run be started? if (cNBlockUsed == sSaveNBlockPP) { cStateOutLen = 0; return blockCrc; } cStateOutCh = cK0; int k1; if (tPos >= 100000 * _blockSize100k) { throw ArchiveException('Data Error'); } tPos = _tt[tPos]; k1 = tPos & 0xff; tPos >>= 8; cNBlockUsed++; if (k1 != cK0) { cK0 = k1; output.writeByte(cStateOutCh); blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc); cStateOutLen = 0; continue; } if (cNBlockUsed == sSaveNBlockPP) { output.writeByte(cStateOutCh); blockCrc = BZip2.updateCrc(cStateOutCh, blockCrc); cStateOutLen = 0; continue; } cStateOutLen = 2; if (tPos >= 100000 * _blockSize100k) { throw ArchiveException('Data Error'); } tPos = _tt[tPos]; k1 = tPos & 0xff; tPos >>= 8; cNBlockUsed++; if (cNBlockUsed == sSaveNBlockPP) { continue; } if (k1 != cK0) { cK0 = k1; continue; } cStateOutLen = 3; if (tPos >= 100000 * _blockSize100k) { throw ArchiveException('Data Error'); } tPos = _tt[tPos]; k1 = tPos & 0xff; tPos >>= 8; cNBlockUsed++; if (cNBlockUsed == sSaveNBlockPP) { continue; } if (k1 != cK0) { cK0 = k1; continue; } if (tPos >= 100000 * _blockSize100k) { throw ArchiveException('Data Error'); } tPos = _tt[tPos]; k1 = tPos & 0xff; tPos >>= 8; cNBlockUsed++; cStateOutLen = k1 + 4; if (tPos >= 100000 * _blockSize100k) { throw ArchiveException('Data Error'); } tPos = _tt[tPos]; cK0 = tPos & 0xff; tPos >>= 8; cNBlockUsed++; } } return blockCrc; // ignore: dead_code } int _getMtfVal(Bz2BitReader br) { if (_groupPos == 0) { _groupNo++; if (_groupNo >= _numSelectors) { throw ArchiveException('Data error'); } _groupPos = bzGSize; _gSel = _selector[_groupNo]; _gMinlen = _minLens[_gSel]; _gLimit = _limit[_gSel]; _gPerm = _perm[_gSel]; _gBase = _base[_gSel]; } _groupPos--; var zn = _gMinlen; var zvec = br.readBits(zn); while (true) { if (zn > 20) { throw ArchiveException('Data error'); } if (zvec <= _gLimit[zn]) { break; } zn++; var zj = br.readBits(1); zvec = (zvec << 1) | zj; } if (zvec - _gBase[zn] < 0 || zvec - _gBase[zn] >= bzMaxAlphaSize) { throw ArchiveException('Data error'); } return _gPerm[zvec - _gBase[zn]]; } void _hbCreateDecodeTables(Int32List limit, Int32List base, Int32List perm, Uint8List length, int minLen, int maxLen, int alphaSize) { var pp = 0; for (var i = minLen; i <= maxLen; i++) { for (var j = 0; j < alphaSize; j++) { if (length[j] == i) { perm[pp] = j; pp++; } } } for (var i = 0; i < bzMaxCodeLen; i++) { base[i] = 0; } for (var i = 0; i < alphaSize; i++) { base[length[i] + 1]++; } for (var i = 1; i < bzMaxCodeLen; i++) { base[i] += base[i - 1]; } for (var i = 0; i < bzMaxCodeLen; i++) { limit[i] = 0; } var vec = 0; for (var i = minLen; i <= maxLen; i++) { vec += (base[i + 1] - base[i]); limit[i] = vec - 1; vec <<= 1; } for (var i = minLen + 1; i <= maxLen; i++) { base[i] = ((limit[i - 1] + 1) << 1) - base[i]; } } void _makeMaps() { _numInUse = 0; _seqToUnseq = Uint8List(256); for (var i = 0; i < 256; ++i) { if (_inUse[i] != 0) { _seqToUnseq[_numInUse++] = i; } } } late int _blockSize100k; late Uint32List _tt; late Uint8List _inUse16; late Uint8List _inUse; late Uint8List _seqToUnseq; late Uint8List _mtfa; late Int32List _mtfbase; late Uint8List _selectorMtf; late Uint8List _selector; late List _limit; late List _base; late List _perm; late Int32List _minLens; late Int32List _unzftab; late int _numSelectors; int _groupPos = 0; int _groupNo = -1; int _gSel = 0; int _gMinlen = 0; late Int32List _gLimit; late Int32List _gPerm; late Int32List _gBase; late Int32List _cftab; late List _len; int _numInUse = 0; static const int bzNGroups = 6; static const int bzGSize = 50; static const int bzNIters = 4; static const int bzMaxAlphaSize = 258; static const int bzMaxCodeLen = 23; static const int bzMaxSelectors = (2 + (900000 ~/ bzGSize)); static const int mtfaSize = 4096; static const int mtflSize = 16; static const int bzRunA = 0; static const int bzRunB = 1; static const int blockCompressed = 0; static const int blockEos = 2; static const List bz2RNums = [ 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, 936, 638 ]; }