private void optimiseSelectorsAndHuffmanTables (final boolean storeSelectors) {

		final char[] mtfBlock = this.mtfBlock;
		final byte[] selectors = this.selectors;
		final int[][] huffmanCodeLengths = this.huffmanCodeLengths;
		final int mtfLength = this.mtfLength;
		final int mtfAlphabetSize = this.mtfAlphabetSize;

		final int totalTables = huffmanCodeLengths.length;
		final int[][] tableFrequencies = new int[totalTables][mtfAlphabetSize];

		int selectorIndex = 0;

		// Find the best table for each group of 50 block bytes based on the current Huffman code lengths
		for (int groupStart = 0; groupStart < mtfLength;) {

			final int groupEnd = Math.min (groupStart + BZip2Constants.HUFFMAN_GROUP_RUN_LENGTH, mtfLength) - 1;

			// Calculate the cost of this group when encoded by each table
			short[] cost = new short[totalTables];
			for (int i = groupStart; i <= groupEnd; i++) {
				final int value = mtfBlock[i];
				for (int j = 0; j < totalTables; j++) {
					cost[j] += huffmanCodeLengths[j][value];
				}
			}

			// Find the table with the least cost for this group
			byte bestTable = 0;
			int bestCost = cost[0];
			for (byte i = 1 ; i < totalTables; i++) {
				final int tableCost = cost[i];
				if (tableCost < bestCost) {
					bestCost = tableCost;
					bestTable = i;
				}
			}

			// Accumulate symbol frequencies for the table chosen for this block
			final int[] bestGroupFrequencies = tableFrequencies[bestTable];
			for (int i = groupStart; i <= groupEnd; i++) {
				bestGroupFrequencies[mtfBlock[i]]++;
			}

			// Store a selector indicating the table chosen for this block
			if (storeSelectors) {
				selectors[selectorIndex++] = bestTable;
			}

			groupStart = groupEnd + 1;

		}

		// Generate new Huffman code lengths based on the frequencies for each table accumulated in this iteration
		for (int i = 0; i < totalTables; i++) {
			generateHuffmanCodeLengths (mtfAlphabetSize, tableFrequencies[i], huffmanCodeLengths[i]);
		}

	}