/* Copyright 2014-2016 Jan Huwald, Stephan Richter This file is part of HRTC. HRTC is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. HRTC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program (see file LICENSE). If not, see . */ #pragma once #include "common.hpp" #include "num_util.hpp" // state of one trajectory during compression template struct TrajState { Real x0, x1, vmin, vmax; int64_t qx0; // store the quantized x0 as reference so that // numerical error of support vector position does not // accumulate uint32_t dt; // The first point added initialises the data structure. // The quantised integer to be stored is returned uint32_t add_first(Real x, Real, Real quantum) { qx0 = quantize(x, quantum); x0 = quant2real(qx0, quantum), x1 = x, vmin = -numeric_limits::infinity(), vmax = numeric_limits::infinity(), dt = 0; return signed2unsigned(quantize(x, quantum)); } optional add(Real x, Real e, Real quantum) { // compute new error bound Real vmin2((x - x0 - e) / (dt + 1)), vmax2((x - x0 + e) / (dt + 1)); vmin2 = max(vmin, vmin2); vmax2 = min(vmax, vmax2); if (vmin2 > vmax2) { // if new point does not fit in the existing error bound, store // a linear segment up to the previous point and start a new // segment SVI res = flush(quantum); // qx0 and x0 are set by flush x1 = x; dt = 1; vmin = x1 - x0 - e; vmax = x1 - x0 + e; return res; }else{ // extend the linear segment by the current point otherwise x1 = x; vmin = vmin2; vmax = vmax2; ++dt; return optional(); } } SVI flush(Real quantum) { // Compute new support vector: the point sv that is closest to x1 // while maintaining the derivate bounds vmin/vmax Real sv; if (x1 - x0 < vmin * dt) { sv = x0 + vmin * dt; } else if (x1 - x0 > vmax * dt) { sv = x0 + vmax * dt; } else { sv = x1; } // create integer support vector (the data struct to VLI-compress) SVI svi; assert(dt > 0); svi.dt = dt - 1; svi.v = signed2unsigned(quantize(sv - x0, quantum)); // start new segment from sv, not from x1 qx0 = quantize(sv, quantum); x0 = quant2real(qx0, quantum); return svi; } }; // state of the compressor template struct CompressorState { // Input config TId numTraj; Real error, bound, quantum; // Output config int chunkSize; // maximal number of support vectors (SVI) // Store the order in which support vectors are expected and in // which we know them respectively. Only the later might store more // than one support vector for a trajectory priority_queue::container_type, std::greater> expectedSegment; map knownSegment; Time curTime; TrajState *trajState; // Current chunk of support vectors to be written int curSV; SplitSVIBuffer buf; // function which is called with the compressedSV function sink; /// the functions of the compressor in order // 0. init compressor CompressorState(TId numTraj, Real error, Real bound, Real quantum, int chunkSize, EncodingPtr encoder, function sink) : numTraj(numTraj), error(error), bound(bound), quantum(quantum), chunkSize(chunkSize), curTime(0), trajState(new TrajState[numTraj]), curSV(0), buf(encoder, chunkSize), sink(sink) {} // 1. add another frame of trajectory data void addFrame(Real *trajVal) { if (curTime) { addLaterFrame(trajVal); } else { addFirstFrame(trajVal); } } // Use the first frame for late initialization of TrajState and // expected segment queue. void addFirstFrame(Real *trajVal) { // Instead of compressed support vectors, initial value (x) is // stored uncompressed with the minimal number of bits given bound // and quantum (+1 for sign) uint bit_count = 2 + ceil(log2(bound / quantum)); dynamic_bitset iv(bit_count * numTraj); for (int traj=0; traj> i) & 1; } // write data to stream // TODO: use buffer of interal representation of dynamic_bitset ChunkSize sz; sz.raw = bit_count * numTraj; sz.compressed = (sz.raw + 7) / 8; uint8_t *raw_iv = new uint8_t[sz.compressed]; to_block_range(iv, raw_iv); sink((char*) raw_iv, sz); delete[] raw_iv; // Add all expected segments curTime = 1; for (int traj=0; trajdt + 1); stp.id = traj; knownSegment.insert(make_pair(stp, *maybePoint)); // Test if we know the next required support vector. Add it to // the raw chunk if so. Push the chunk once it is full. while (expectedSegment.size() && (expectedSegment.top() == knownSegment.begin()->first)) { auto segIter = knownSegment.begin(); auto expectedTraj = expectedSegment.top().id; // add new expected segemnt STP newSeg; newSeg.time = segIter->first.time + segIter->second.dt + 1; newSeg.id = expectedTraj; expectedSegment.push(newSeg); // write out known segment buf.set(curSV++, segIter->second); knownSegment.erase(segIter); expectedSegment.pop(); // write out chunk once full if (curSV >= chunkSize) pushChunk(); } } } assert(curTime++ < maxTime); } // X. compress chunk, push it to sink, reset it void pushChunk() { uint32_t *cbuf; ChunkSize sz; sz.raw = curSV * 2 * 4; // two 4-byte int per support vector if (curSV) { tie(cbuf, sz.compressed) = buf.encode(curSV); sz.compressed *= sizeof(uint32_t); }else{ sz.compressed = 0; } sink((char*) cbuf, sz); curSV = 0; } void finish() { // The data structure must have been initialized. assert(curTime); // We only have full trajectory (x and v) if at least two frames // have been seen. Only then we need to flush the unfinished // trajectories. if (curTime > 1) { for (; expectedSegment.size(); expectedSegment.pop()) { auto es = expectedSegment.top(); auto fks = knownSegment.begin(); assert(es.time < curTime); // If we already have a support vector for the point, use it; // otherwise we have to create one by flushing it if ((fks != knownSegment.end()) & (es == fks->first)) { // Construct the next segment to wait for. Note: // - An SVI is only known if it terminates before curTime. // - There may be more than one pending SVI for each // trajectory. STP newSeg; newSeg.time = fks->first.time + fks->second.dt + 1; newSeg.id = fks->first.id; assert(newSeg.time < curTime); expectedSegment.push(newSeg); buf.set(curSV++, fks->second); knownSegment.erase(fks); }else{ buf.set(curSV++, trajState[es.id].flush(quantum)); } if (curSV >= chunkSize) pushChunk(); } } // Flush non-empty buffers. if (curSV) pushChunk(); // Add one empty chunk to signal the end of this stream. pushChunk(); } ~CompressorState() { delete[] trajState; } };