_applyPrune(extremasResultsT) {
		const nBuckets = NUM_BUCKETS_PER_DIMENSION * NUM_BUCKETS_PER_DIMENSION;
		const nFeatures = MAX_FEATURES_PER_BUCKET;
		/*
		if (!this.kernelCaches.applyPrune) {
		  const reductionKernels = [];
	
		  // to reduce to amount of data that need to sync back to CPU by 4 times, we apply this trick:
		  // the fact that there is not possible to have consecutive maximum/minimum, we can safe combine 4 pixels into 1
		  for (let k = 0; k < extremasResultsT.length; k++) {
			const extremaHeight = extremasResultsT[k].shape[0];
			const extremaWidth = extremasResultsT[k].shape[1];
	
			const kernel = {
				variableNames: ['extrema'],
				outputShape: [Math.floor(extremaHeight/2), Math.floor(extremaWidth/2)],
				userCode: `
					void main() {
						ivec2 coords = getOutputCoords();
						int y = coords[0] * 2;
						int x = coords[1] * 2;
	
						float location = 0.0;
						float values = getExtrema(y, x);
	
						if (getExtrema(y+1, x) != 0.0) {
							location = 1.0;
						values = getExtrema(y+1, x);
						}
						else if (getExtrema(y, x+1) != 0.0) {
							location = 2.0;
						values = getExtrema(y, x+1);
						}
						else if (getExtrema(y+1, x+1) != 0.0) {
							location = 3.0;
						values = getExtrema(y+1, x+1);
						}
	
						if (values < 0.0) {
							setOutput(location * -1000.0 + values);
						} else {
							setOutput(location * 1000.0 + values);
						}
					}
				`
			}
			reductionKernels.push(kernel);
		  }
		  this.kernelCaches.applyPrune = {reductionKernels};
		}
		*/
		// combine results into a tensor of:
		//   nBuckets x nFeatures x [score, octave, y, x]
		const curAbsScores = [];
		/** @type {number[][][]} */
		const result = [];
		for (let i = 0; i < nBuckets; i++) {
			result.push([]);
			curAbsScores.push([]);
			for (let j = 0; j < nFeatures; j++) {
				result[i].push([0, 0, 0, 0]);
				curAbsScores[i].push(0);
			}
		}

		tf.tidy(() => {
			//const {reductionKernels} = this.kernelCaches.applyPrune;

			for (let k = 0; k < extremasResultsT.length; k++) {
				//const program = reductionKernels[k];
				//const reducedT = this._compileAndRun(program, [extremasResultsT[k]]);
				const reducedT = tf.engine().runKernel('ExtremaReduction', { extremasResultT: extremasResultsT[k] });
				const octave = k + 1; // extrema starts from second octave

				const reduced = reducedT.arraySync();
				const height = reducedT.shape[0];
				const width = reducedT.shape[1];

				const bucketWidth = width * 2 / NUM_BUCKETS_PER_DIMENSION;
				const bucketHeight = height * 2 / NUM_BUCKETS_PER_DIMENSION;

				for (let j = 0; j < height; j++) {
					for (let i = 0; i < width; i++) {
						const encoded = reduced[j][i];
						if (encoded == 0) continue;


						const score = encoded % 1000;
						const loc = Math.floor(Math.abs(encoded) / 1000);
						const x = i * 2 + (loc === 2 || loc === 3 ? 1 : 0);
						const y = j * 2 + (loc === 1 || loc === 3 ? 1 : 0);

						const bucketX = Math.floor(x / bucketWidth);
						const bucketY = Math.floor(y / bucketHeight);
						const bucket = bucketY * NUM_BUCKETS_PER_DIMENSION + bucketX;

						const absScore = Math.abs(score);

						let tIndex = nFeatures;
						while (tIndex >= 1 && absScore > curAbsScores[bucket][tIndex - 1]) {
							tIndex -= 1;
						}

						if (tIndex < nFeatures) {
							for (let t = nFeatures - 1; t >= tIndex + 1; t--) {
								curAbsScores[bucket][t] = curAbsScores[bucket][t - 1];
								result[bucket][t][0] = result[bucket][t - 1][0];
								result[bucket][t][1] = result[bucket][t - 1][1];
								result[bucket][t][2] = result[bucket][t - 1][2];
								result[bucket][t][3] = result[bucket][t - 1][3];
							}
							curAbsScores[bucket][tIndex] = absScore;
							result[bucket][tIndex][0] = score;
							result[bucket][tIndex][1] = octave;
							result[bucket][tIndex][2] = y;
							result[bucket][tIndex][3] = x;
						}
					}//for j<height
				}//for i<width
			}
		});

		// combine all buckets into a single list
		const list = [];
		for (let i = 0; i < nBuckets; i++) {
			for (let j = 0; j < nFeatures; j++) {
				list.push(result[i][j]);
			}
		}
		return list;
	}
@param {tf.Tensor<tf.Rank>[]} extremasResultsT 
@returns