// Verify that a given 'step_move' matches the actual step times int check_line(ref step_move move) { if (!Stepcompress.CHECK_LINES) { return(0); } if (move.count == 0 || (move.interval == 0 && move.add == 0 && move.count > 1) || move.interval >= 0x80000000) { //errorf("stepcompress o=%d i=%d c=%d a=%d: Invalid sequence" // , sc->oid, move.interval, move.count, move.add); return(Stepcompress.ERROR_RET); } uint interval = move.interval, p = 0; ushort i; for (i = 0; i < move.count; i++) { points point = minmax_point(queue_pos + i); p += interval; if (p < point.minp || p > point.maxp) { //errorf("stepcompress o=%d i=%d c=%d a=%d: Point %d: %d not in %d:%d" // , sc.oid, move.interval, move.count, move.add // , i + 1, p, point.minp, point.maxp); return(Stepcompress.ERROR_RET); } if (interval >= 0x80000000) { //errorf("stepcompress o=%d i=%d c=%d a=%d:"+ // " Point %d: interval overflow %d" // , sc.oid, move.interval, move.count, move.add // , i + 1, interval); return(Stepcompress.ERROR_RET); } interval += (ushort)move.add; } return(0); }
// Find a 'step_move' that covers a series of step times step_move compress_bisect_add() { uint *qlast = queue_next; if (qlast > queue_pos + 65535) { qlast = queue_pos + 65535; } points point = minmax_point(queue_pos); int outer_mininterval = point.minp, outer_maxinterval = point.maxp; int add = 0, minadd = -0x8000, maxadd = 0x7fff; int bestinterval = 0, bestcount = 1, bestadd = 1, bestreach = int.MinValue; int zerointerval = 0, zerocount = 0; int count, addfactor, nextaddfactor, c; while (true) { // Find longest valid sequence with the given 'add' points nextpoint; int nextmininterval = outer_mininterval; int nextmaxinterval = outer_maxinterval, interval = nextmaxinterval; int nextcount = 1; for (; ;) { nextcount++; if (&queue_pos[nextcount - 1] >= qlast) { count = nextcount - 1; return(new step_move((uint)interval, (ushort)count, (short)add)); } nextpoint = minmax_point(queue_pos + nextcount - 1); nextaddfactor = nextcount * (nextcount - 1) / 2; c = add * nextaddfactor; if (nextmininterval * nextcount < nextpoint.minp - c) { nextmininterval = Stepcompress.DIV_ROUND_UP(nextpoint.minp - c, nextcount); } if (nextmaxinterval * nextcount > nextpoint.maxp - c) { nextmaxinterval = (nextpoint.maxp - c) / nextcount; } if (nextmininterval > nextmaxinterval) { break; } interval = nextmaxinterval; } // Check if this is the best sequence found so far count = nextcount - 1; addfactor = count * (count - 1) / 2; int reach = add * addfactor + interval * count; if (reach > bestreach || (reach == bestreach && interval > bestinterval)) { bestinterval = interval; bestcount = count; bestadd = add; bestreach = reach; if (add == 0) { zerointerval = interval; zerocount = count; } if (count > 0x200) { // No 'add' will improve sequence; avoid integer overflow break; } } // Check if a greater or lesser add could extend the sequence nextaddfactor = nextcount * (nextcount - 1) / 2; int nextreach = add * nextaddfactor + interval * nextcount; if (nextreach < nextpoint.minp) { minadd = add + 1; outer_maxinterval = nextmaxinterval; } else { maxadd = add - 1; outer_mininterval = nextmininterval; } // The maximum valid deviation between two quadratic sequences // can be calculated and used to further limit the add range. if (count > 1) { int errdelta = (int)max_error * Stepcompress.QUADRATIC_DEV / (count * count); if (minadd < add - errdelta) { minadd = add - errdelta; } if (maxadd > add + errdelta) { maxadd = add + errdelta; } } // See if next point would further limit the add range c = outer_maxinterval * nextcount; if (minadd * nextaddfactor < nextpoint.minp - c) { minadd = Stepcompress.idiv_up(nextpoint.minp - c, nextaddfactor); } c = outer_mininterval * nextcount; if (maxadd * nextaddfactor > nextpoint.maxp - c) { maxadd = Stepcompress.idiv_down(nextpoint.maxp - c, nextaddfactor); } // Bisect valid add range and try again with new 'add' if (minadd > maxadd) { break; } add = maxadd - (maxadd - minadd) / 4; } if (zerocount + zerocount / 16 >= bestcount) { // Prefer add=0 if it's similar to the best found sequence return(new step_move((uint)zerointerval, (ushort)zerocount, 0)); } return(new step_move((uint)bestinterval, (ushort)bestcount, (short)bestadd)); }