blob: c823f22567067f873c1e08ad8e2c72e14f6c7218 [file] [log] [blame]
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Given a list of lists of integers, figure out a good way to overlap
// these into a single list, with a list of indices telling where each
// original list started.
import "dart:typed_data";
import "atsp.dart";
import "indirect_table.dart";
/// Takes a set of distinct chunks, and finds a semi-optimal overlapping.
///
/// The overlapping is a single chunk, of minimal length, containing all
/// the original chunk's contents, and an indirection entry pointing
/// to the position in the new table.
IndirectTable combineLists(List<Uint8List> input) {
// See how much chunks are overlapping.
var chunkCount = input.length;
var graph = Graph(chunkCount + 1);
for (var i = 0; i < input.length; i++) {
var firstChunk = input[i];
for (var j = 0; j < input.length; j++) {
if (i == j) continue;
var secondChunk = input[j];
var overlap = _overlap(firstChunk, secondChunk);
graph.setWeight(i, j, secondChunk.length - overlap);
}
}
// Find an optimal(ish) path.
// First create a cycle through the one extra node (index `chunkCount`).
var path = List<int>.filled(chunkCount + 2, chunkCount);
for (var i = 0; i <= chunkCount; i++) {
path[i + 1] = i;
}
while (opt3(graph, path)) {}
// Then break the cycle at the extra node.
// The way we optimize, it's still first/last.
assert(path.last == chunkCount);
assert(path.first == chunkCount);
var chunkLength =
input[path[1]].length + graph.pathWeight(path, 1, path.length - 2);
var chunkData = Uint8List(chunkLength);
var entries = List<TableEntry>.filled(input.length, TableEntry(0, 0, 0));
{
// Handle path chunks.
var prevChunkNum = path[1];
var firstChunk = input[prevChunkNum];
chunkData.setRange(0, firstChunk.length, firstChunk);
entries[prevChunkNum] = TableEntry(0, 0, firstChunk.length);
var index = firstChunk.length;
for (var i = 2; i < path.length - 1; i++) {
var nextChunkNum = path[i];
var chunk = input[nextChunkNum];
var nonOverlap = graph.weight(prevChunkNum, nextChunkNum);
var overlap = chunk.length - nonOverlap;
entries[nextChunkNum] = TableEntry(0, index - overlap, chunk.length);
chunkData.setRange(index, index + nonOverlap, chunk, overlap);
index += nonOverlap;
prevChunkNum = nextChunkNum;
}
}
return IndirectTable([chunkData], entries);
}
/// Finds how much overlap there is between [first] and [second] in that order.
int _overlap(Uint8List first, Uint8List second) {
var maxOverlap =
(first.length < second.length ? first.length : second.length) - 1;
outer:
for (var overlap = maxOverlap; overlap > 0; overlap--) {
var firstStart = first.length - overlap;
for (var j = 0; j < overlap; j++) {
if (first[firstStart + j] != second[j]) continue outer;
}
return overlap;
}
return 0;
}