blob: 128ab3b4aab782937ce64d37a14ca9cacfdf448f [file] [log] [blame]
import 'dart:typed_data';
import 'bzip2/bzip2.dart';
import 'bzip2/bz2_bit_reader.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<int> decodeBytes(List<int> 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<int> 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<Uint8List>.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<Int32List>.filled(bzNGroups, BZip2.emptyInt32List);
_base = List<Int32List>.filled(bzNGroups, BZip2.emptyInt32List);
_perm = List<Int32List>.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<Int32List> _limit;
late List<Int32List> _base;
late List<Int32List> _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<Uint8List> _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<int> 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
];
}