<commit_before>// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2011-2021 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// ----------------------------------------------------------------------------

/**
 * @brief Functions for finding dominant direction of a set of colors.
 */
#if !defined(ASTCENC_DECOMPRESS_ONLY)

#include "astcenc_internal.h"

#include <cassert>

// For a full block, functions to compute averages and dominant directions. The
// averages and directions are computed separately for each partition.
// We have separate versions for blocks with and without alpha, since the
// processing for blocks with alpha is significantly more expensive. The
// direction vectors it produces are NOT normalized.
void compute_avgs_and_dirs_4_comp(
	const partition_info* pt,
	const imageblock* blk,
	const error_weight_block* ewb,
	partition_metrics pm[4]
) {
	int partition_count = pt->partition_count;
	promise(partition_count > 0);

	for (int partition = 0; partition < partition_count; partition++)
	{
		const uint8_t *weights = pt->texels_of_partition[partition];

		vfloat4 error_sum = vfloat4::zero();
		vfloat4 base_sum = vfloat4::zero();
		vfloat4 rgba_min(1e38f);
		vfloat4 rgba_max(-1e38f);
		float partition_weight = 0.0f;

		int texel_count = pt->partition_texel_count[partition];
		promise(texel_count > 0);

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = ewb->texel_weight[iwt];
			vfloat4 texel_datum = blk->texel(iwt);
			vfloat4 error_weight = ewb->error_weights[iwt];

			if (weight > 1e-10f)
			{
				rgba_min = min(texel_datum, rgba_min);
				rgba_max = max(texel_datum, rgba_max);
			}

			partition_weight += weight;
			base_sum += texel_datum * weight;
			error_sum += error_weight;
		}

		error_sum = error_sum / texel_count;
		vfloat4 csf = normalize(sqrt(error_sum)) * 2.0f;

		vfloat4 average = base_sum * (1.0f / astc::max(partition_weight, 1e-7f));

		pm[partition].error_weight = error_sum;
		pm[partition].avg = average * csf;
		pm[partition].color_scale = csf;
		pm[partition].icolor_scale = 1.0f / max(csf, 1e-7f);
		vfloat4 range = max(rgba_max - rgba_min, 1e-10f);
		pm[partition].range_sq = range * range;

		vfloat4 sum_xp = vfloat4::zero();
		vfloat4 sum_yp = vfloat4::zero();
		vfloat4 sum_zp = vfloat4::zero();
		vfloat4 sum_wp = vfloat4::zero();

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = ewb->texel_weight[iwt];
			vfloat4 texel_datum = blk->texel(iwt);
			texel_datum = (texel_datum - average) * weight;

			vfloat4 zero = vfloat4::zero();

			vmask4 tdm0 = vfloat4(texel_datum.lane<0>()) > zero;
			sum_xp += select(zero, texel_datum, tdm0);

			vmask4 tdm1 = vfloat4(texel_datum.lane<1>()) > zero;
			sum_yp += select(zero, texel_datum, tdm1);

			vmask4 tdm2 = vfloat4(texel_datum.lane<2>()) > zero;
			sum_zp += select(zero, texel_datum, tdm2);

			vmask4 tdm3 = vfloat4(texel_datum.lane<3>()) > zero;
			sum_wp += select(zero, texel_datum, tdm3);
		}

		float prod_xp = dot_s(sum_xp, sum_xp);
		float prod_yp = dot_s(sum_yp, sum_yp);
		float prod_zp = dot_s(sum_zp, sum_zp);
		float prod_wp = dot_s(sum_wp, sum_wp);

		vfloat4 best_vector = sum_xp;
		float best_sum = prod_xp;

		if (prod_yp > best_sum)
		{
			best_vector = sum_yp;
			best_sum = prod_yp;
		}

		if (prod_zp > best_sum)
		{
			best_vector = sum_zp;
			best_sum = prod_zp;
		}

		if (prod_wp > best_sum)
		{
			best_vector = sum_wp;
		}

		pm[partition].dir = best_vector;
	}
}

void compute_avgs_and_dirs_3_comp(
	const partition_info* pt,
	const imageblock* blk,
	const error_weight_block* ewb,
	int omitted_component,
	partition_metrics pm[4]
) {
	const float *texel_weights = ewb->texel_weight_rgb;

	const float* data_vr = blk->data_r;
	const float* data_vg = blk->data_g;
	const float* data_vb = blk->data_b;

	const float* error_vr = ewb->texel_weight_r;
	const float* error_vg = ewb->texel_weight_g;
	const float* error_vb = ewb->texel_weight_b;

	if (omitted_component == 0)
	{
		texel_weights = ewb->texel_weight_gba;

		data_vr = blk->data_g;
		data_vg = blk->data_b;
		data_vb = blk->data_a;

		error_vr = ewb->texel_weight_g;
		error_vg = ewb->texel_weight_b;
		error_vb = ewb->texel_weight_a;
	}
	else if (omitted_component == 1)
	{
		texel_weights = ewb->texel_weight_rba;

		data_vg = blk->data_b;
		data_vb = blk->data_a;

		error_vg = ewb->texel_weight_b;
		error_vb = ewb->texel_weight_a;
	}
	else if (omitted_component == 2)
	{
		texel_weights = ewb->texel_weight_rga;

		data_vb = blk->data_a;

		error_vb = ewb->texel_weight_a;
	}

	int partition_count = pt->partition_count;
	promise(partition_count > 0);

	for (int partition = 0; partition < partition_count; partition++)
	{
		const uint8_t *weights = pt->texels_of_partition[partition];

		vfloat4 error_sum = vfloat4::zero();
		vfloat4 base_sum = vfloat4::zero();
		vfloat4 rgb_min(1e38f);
		vfloat4 rgb_max(-1e38f);
		float partition_weight = 0.0f;

		int texel_count = pt->partition_texel_count[partition];
		promise(texel_count > 0);

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = texel_weights[iwt];

			vfloat4 texel_datum(data_vr[iwt],
			                    data_vg[iwt],
			                    data_vb[iwt],
			                    0.0f);

			vfloat4 error_weight(error_vr[iwt],
			                     error_vg[iwt],
			                     error_vb[iwt],
			                     0.0f);

			if (weight > 1e-10f)
			{
				rgb_min = min(texel_datum, rgb_min);
				rgb_max = max(texel_datum, rgb_max);
			}

			partition_weight += weight;
			base_sum += texel_datum * weight;
			error_sum += error_weight;
		}

		error_sum = error_sum / texel_count;
		vfloat4 csf = normalize(sqrt(error_sum)) * 1.73205080f;

		vfloat4 average = base_sum * (1.0f / astc::max(partition_weight, 1e-7f));

		pm[partition].error_weight = error_sum;
		pm[partition].avg = average * csf;
		pm[partition].color_scale = csf;
		pm[partition].icolor_scale = 1.0f / max(csf, 1e-7f);
		vfloat4 range = max(rgb_max - rgb_min, 1e-10f);
		pm[partition].range_sq = range * range;

		vfloat4 sum_xp = vfloat4::zero();
		vfloat4 sum_yp = vfloat4::zero();
		vfloat4 sum_zp = vfloat4::zero();

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = texel_weights[iwt];
			vfloat4 texel_datum = vfloat4(data_vr[iwt],
			                              data_vg[iwt],
			                              data_vb[iwt],
			                              0.0f);
			texel_datum = (texel_datum - average) * weight;

			vfloat4 zero = vfloat4::zero();

			vmask4 tdm0 = vfloat4(texel_datum.lane<0>()) > zero;
			sum_xp += select(zero, texel_datum, tdm0);

			vmask4 tdm1 = vfloat4(texel_datum.lane<1>()) > zero;
			sum_yp += select(zero, texel_datum, tdm1);

			vmask4 tdm2 = vfloat4(texel_datum.lane<2>()) > zero;
			sum_zp += select(zero, texel_datum, tdm2);
		}

		float prod_xp = dot3_s(sum_xp, sum_xp);
		float prod_yp = dot3_s(sum_yp, sum_yp);
		float prod_zp = dot3_s(sum_zp, sum_zp);

		vfloat4 best_vector = sum_xp;
		float best_sum = prod_xp;

		if (prod_yp > best_sum)
		{
			best_vector = sum_yp;
			best_sum = prod_yp;
		}

		if (prod_zp > best_sum)
		{
			best_vector = sum_zp;
		}

		if (dot3_s(best_vector, best_vector) < 1e-18f)
		{
			best_vector = vfloat4(1.0f, 1.0f, 1.0f, 0.0f);
		}

		pm[partition].dir = best_vector;
	}
}

void compute_avgs_and_dirs_2_comp(
	const partition_info* pt,
	const imageblock* blk,
	const error_weight_block* ewb,
	int component1,
	int component2,
	partition_metrics pm[4]
) {
	const float *texel_weights;

	const float* data_vr = nullptr;
	const float* data_vg = nullptr;

	const float* error_vr = nullptr;
	const float* error_vg = nullptr;

	if (component1 == 0 && component2 == 1)
	{
		texel_weights = ewb->texel_weight_rg;

		data_vr = blk->data_r;
		data_vg = blk->data_g;

		error_vr = ewb->texel_weight_r;
		error_vg = ewb->texel_weight_g;
	}
	else if (component1 == 0 && component2 == 2)
	{
		texel_weights = ewb->texel_weight_rb;

		data_vr = blk->data_r;
		data_vg = blk->data_b;

		error_vr = ewb->texel_weight_r;
		error_vg = ewb->texel_weight_b;
	}
	else // (component1 == 1 && component2 == 2)
	{
		assert(component1 == 1 && component2 == 2);
		texel_weights = ewb->texel_weight_gb;

		data_vr = blk->data_g;
		data_vg = blk->data_b;


		error_vr = ewb->texel_weight_g;
		error_vg = ewb->texel_weight_b;
	}

	int partition_count = pt->partition_count;
	promise(partition_count > 0);

	for (int partition = 0; partition < partition_count; partition++)
	{
		const uint8_t *weights = pt->texels_of_partition[partition];

		float2 error_sum = float2(0.0f);
		float2 base_sum = float2(0.0f);
		float partition_weight = 0.0f;

		int texel_count = pt->partition_texel_count[partition];
		promise(texel_count > 0);

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = texel_weights[iwt];
			float2 texel_datum = float2(data_vr[iwt], data_vg[iwt]) * weight;

			float2 error_weight = float2(error_vr[iwt], error_vg[iwt]);

			partition_weight += weight;
			base_sum += texel_datum;
			error_sum += error_weight;
		}

		vfloat4 error_sum4 = vfloat4(error_sum.r, error_sum.g, 0.0f, 0.0f) * (1.0f / texel_count);
		vfloat4 csf = normalize(sqrt(error_sum4)) * 1.41421356f;
		vfloat4 average4 = vfloat4(base_sum.r, base_sum.g, 0.0f, 0.0f) * (1.0f / astc::max(partition_weight, 1e-7f));


		pm[partition].error_weight = error_sum4;
		pm[partition].avg = average4 * csf;
		float2 average = float2(average4.lane<0>(), average4.lane<1>());
		pm[partition].color_scale = csf;
		pm[partition].icolor_scale = 1.0f / max(csf, 1e-7f);

		float2 sum_xp = float2(0.0f);
		float2 sum_yp = float2(0.0f);

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = texel_weights[iwt];
			float2 texel_datum = float2(data_vr[iwt], data_vg[iwt]);
			texel_datum = (texel_datum - average) * weight;

			if (texel_datum.r > 0.0f)
			{
				sum_xp += texel_datum;
			}

			if (texel_datum.g > 0.0f)
			{
				sum_yp += texel_datum;
			}
		}

		float prod_xp = dot(sum_xp, sum_xp);
		float prod_yp = dot(sum_yp, sum_yp);

		float2 best_vector = sum_xp;
		float best_sum = prod_xp;

		if (prod_yp > best_sum)
		{
			best_vector = sum_yp;
		}

		pm[partition].dir = vfloat4(best_vector.r, best_vector.g, 0.0f, 0.0f);
	}
}

void compute_error_squared_rgba(
	const partition_info* pt,
	const imageblock* blk,
	const error_weight_block* ewb,
	const processed_line4* uncor_plines,<commit_msg>Use vfloat4 not float2<commit_after>// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2011-2021 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// ----------------------------------------------------------------------------

/**
 * @brief Functions for finding dominant direction of a set of colors.
 */
#if !defined(ASTCENC_DECOMPRESS_ONLY)

#include "astcenc_internal.h"

#include <cassert>

// For a full block, functions to compute averages and dominant directions. The
// averages and directions are computed separately for each partition.
// We have separate versions for blocks with and without alpha, since the
// processing for blocks with alpha is significantly more expensive. The
// direction vectors it produces are NOT normalized.
void compute_avgs_and_dirs_4_comp(
	const partition_info* pt,
	const imageblock* blk,
	const error_weight_block* ewb,
	partition_metrics pm[4]
) {
	int partition_count = pt->partition_count;
	promise(partition_count > 0);

	for (int partition = 0; partition < partition_count; partition++)
	{
		const uint8_t *weights = pt->texels_of_partition[partition];

		vfloat4 error_sum = vfloat4::zero();
		vfloat4 base_sum = vfloat4::zero();
		vfloat4 rgba_min(1e38f);
		vfloat4 rgba_max(-1e38f);
		float partition_weight = 0.0f;

		int texel_count = pt->partition_texel_count[partition];
		promise(texel_count > 0);

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = ewb->texel_weight[iwt];
			vfloat4 texel_datum = blk->texel(iwt);
			vfloat4 error_weight = ewb->error_weights[iwt];

			if (weight > 1e-10f)
			{
				rgba_min = min(texel_datum, rgba_min);
				rgba_max = max(texel_datum, rgba_max);
			}

			partition_weight += weight;
			base_sum += texel_datum * weight;
			error_sum += error_weight;
		}

		error_sum = error_sum / texel_count;
		vfloat4 csf = normalize(sqrt(error_sum)) * 2.0f;

		vfloat4 average = base_sum * (1.0f / astc::max(partition_weight, 1e-7f));

		pm[partition].error_weight = error_sum;
		pm[partition].avg = average * csf;
		pm[partition].color_scale = csf;
		pm[partition].icolor_scale = 1.0f / max(csf, 1e-7f);
		vfloat4 range = max(rgba_max - rgba_min, 1e-10f);
		pm[partition].range_sq = range * range;

		vfloat4 sum_xp = vfloat4::zero();
		vfloat4 sum_yp = vfloat4::zero();
		vfloat4 sum_zp = vfloat4::zero();
		vfloat4 sum_wp = vfloat4::zero();

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = ewb->texel_weight[iwt];
			vfloat4 texel_datum = blk->texel(iwt);
			texel_datum = (texel_datum - average) * weight;

			vfloat4 zero = vfloat4::zero();

			vmask4 tdm0 = vfloat4(texel_datum.lane<0>()) > zero;
			sum_xp += select(zero, texel_datum, tdm0);

			vmask4 tdm1 = vfloat4(texel_datum.lane<1>()) > zero;
			sum_yp += select(zero, texel_datum, tdm1);

			vmask4 tdm2 = vfloat4(texel_datum.lane<2>()) > zero;
			sum_zp += select(zero, texel_datum, tdm2);

			vmask4 tdm3 = vfloat4(texel_datum.lane<3>()) > zero;
			sum_wp += select(zero, texel_datum, tdm3);
		}

		float prod_xp = dot_s(sum_xp, sum_xp);
		float prod_yp = dot_s(sum_yp, sum_yp);
		float prod_zp = dot_s(sum_zp, sum_zp);
		float prod_wp = dot_s(sum_wp, sum_wp);

		vfloat4 best_vector = sum_xp;
		float best_sum = prod_xp;

		if (prod_yp > best_sum)
		{
			best_vector = sum_yp;
			best_sum = prod_yp;
		}

		if (prod_zp > best_sum)
		{
			best_vector = sum_zp;
			best_sum = prod_zp;
		}

		if (prod_wp > best_sum)
		{
			best_vector = sum_wp;
		}

		pm[partition].dir = best_vector;
	}
}

void compute_avgs_and_dirs_3_comp(
	const partition_info* pt,
	const imageblock* blk,
	const error_weight_block* ewb,
	int omitted_component,
	partition_metrics pm[4]
) {
	const float *texel_weights = ewb->texel_weight_rgb;

	const float* data_vr = blk->data_r;
	const float* data_vg = blk->data_g;
	const float* data_vb = blk->data_b;

	const float* error_vr = ewb->texel_weight_r;
	const float* error_vg = ewb->texel_weight_g;
	const float* error_vb = ewb->texel_weight_b;

	if (omitted_component == 0)
	{
		texel_weights = ewb->texel_weight_gba;

		data_vr = blk->data_g;
		data_vg = blk->data_b;
		data_vb = blk->data_a;

		error_vr = ewb->texel_weight_g;
		error_vg = ewb->texel_weight_b;
		error_vb = ewb->texel_weight_a;
	}
	else if (omitted_component == 1)
	{
		texel_weights = ewb->texel_weight_rba;

		data_vg = blk->data_b;
		data_vb = blk->data_a;

		error_vg = ewb->texel_weight_b;
		error_vb = ewb->texel_weight_a;
	}
	else if (omitted_component == 2)
	{
		texel_weights = ewb->texel_weight_rga;

		data_vb = blk->data_a;

		error_vb = ewb->texel_weight_a;
	}

	int partition_count = pt->partition_count;
	promise(partition_count > 0);

	for (int partition = 0; partition < partition_count; partition++)
	{
		const uint8_t *weights = pt->texels_of_partition[partition];

		vfloat4 error_sum = vfloat4::zero();
		vfloat4 base_sum = vfloat4::zero();
		vfloat4 rgb_min(1e38f);
		vfloat4 rgb_max(-1e38f);
		float partition_weight = 0.0f;

		int texel_count = pt->partition_texel_count[partition];
		promise(texel_count > 0);

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = texel_weights[iwt];

			vfloat4 texel_datum(data_vr[iwt],
			                    data_vg[iwt],
			                    data_vb[iwt],
			                    0.0f);

			vfloat4 error_weight(error_vr[iwt],
			                     error_vg[iwt],
			                     error_vb[iwt],
			                     0.0f);

			if (weight > 1e-10f)
			{
				rgb_min = min(texel_datum, rgb_min);
				rgb_max = max(texel_datum, rgb_max);
			}

			partition_weight += weight;
			base_sum += texel_datum * weight;
			error_sum += error_weight;
		}

		error_sum = error_sum / texel_count;
		vfloat4 csf = normalize(sqrt(error_sum)) * 1.73205080f;

		vfloat4 average = base_sum * (1.0f / astc::max(partition_weight, 1e-7f));

		pm[partition].error_weight = error_sum;
		pm[partition].avg = average * csf;
		pm[partition].color_scale = csf;
		pm[partition].icolor_scale = 1.0f / max(csf, 1e-7f);
		vfloat4 range = max(rgb_max - rgb_min, 1e-10f);
		pm[partition].range_sq = range * range;

		vfloat4 sum_xp = vfloat4::zero();
		vfloat4 sum_yp = vfloat4::zero();
		vfloat4 sum_zp = vfloat4::zero();

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = texel_weights[iwt];
			vfloat4 texel_datum = vfloat4(data_vr[iwt],
			                              data_vg[iwt],
			                              data_vb[iwt],
			                              0.0f);
			texel_datum = (texel_datum - average) * weight;

			vfloat4 zero = vfloat4::zero();

			vmask4 tdm0 = vfloat4(texel_datum.lane<0>()) > zero;
			sum_xp += select(zero, texel_datum, tdm0);

			vmask4 tdm1 = vfloat4(texel_datum.lane<1>()) > zero;
			sum_yp += select(zero, texel_datum, tdm1);

			vmask4 tdm2 = vfloat4(texel_datum.lane<2>()) > zero;
			sum_zp += select(zero, texel_datum, tdm2);
		}

		float prod_xp = dot3_s(sum_xp, sum_xp);
		float prod_yp = dot3_s(sum_yp, sum_yp);
		float prod_zp = dot3_s(sum_zp, sum_zp);

		vfloat4 best_vector = sum_xp;
		float best_sum = prod_xp;

		if (prod_yp > best_sum)
		{
			best_vector = sum_yp;
			best_sum = prod_yp;
		}

		if (prod_zp > best_sum)
		{
			best_vector = sum_zp;
		}

		if (dot3_s(best_vector, best_vector) < 1e-18f)
		{
			best_vector = vfloat4(1.0f, 1.0f, 1.0f, 0.0f);
		}

		pm[partition].dir = best_vector;
	}
}

void compute_avgs_and_dirs_2_comp(
	const partition_info* pt,
	const imageblock* blk,
	const error_weight_block* ewb,
	int component1,
	int component2,
	partition_metrics pm[4]
) {
	const float *texel_weights;

	const float* data_vr = nullptr;
	const float* data_vg = nullptr;

	const float* error_vr = nullptr;
	const float* error_vg = nullptr;

	if (component1 == 0 && component2 == 1)
	{
		texel_weights = ewb->texel_weight_rg;

		data_vr = blk->data_r;
		data_vg = blk->data_g;

		error_vr = ewb->texel_weight_r;
		error_vg = ewb->texel_weight_g;
	}
	else if (component1 == 0 && component2 == 2)
	{
		texel_weights = ewb->texel_weight_rb;

		data_vr = blk->data_r;
		data_vg = blk->data_b;

		error_vr = ewb->texel_weight_r;
		error_vg = ewb->texel_weight_b;
	}
	else // (component1 == 1 && component2 == 2)
	{
		assert(component1 == 1 && component2 == 2);
		texel_weights = ewb->texel_weight_gb;

		data_vr = blk->data_g;
		data_vg = blk->data_b;


		error_vr = ewb->texel_weight_g;
		error_vg = ewb->texel_weight_b;
	}

	int partition_count = pt->partition_count;
	promise(partition_count > 0);

	for (int partition = 0; partition < partition_count; partition++)
	{
		const uint8_t *weights = pt->texels_of_partition[partition];

		vfloat4 error_sum = vfloat4::zero();
		vfloat4 base_sum = vfloat4::zero();
		float partition_weight = 0.0f;

		int texel_count = pt->partition_texel_count[partition];
		promise(texel_count > 0);

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = texel_weights[iwt];
			vfloat4 texel_datum = vfloat4(data_vr[iwt], data_vg[iwt], 0.0f, 0.0f) * weight;

			vfloat4 error_weight = vfloat4(error_vr[iwt], error_vg[iwt], 0.0f, 0.0f);

			partition_weight += weight;
			base_sum += texel_datum;
			error_sum += error_weight;
		}

		error_sum = error_sum / texel_count;
		vfloat4 csf = normalize(sqrt(error_sum)) * 1.41421356f;
		vfloat4 average = base_sum * (1.0f / astc::max(partition_weight, 1e-7f));


		pm[partition].error_weight = error_sum;
		pm[partition].avg = average * csf;
		pm[partition].color_scale = csf;
		pm[partition].icolor_scale = 1.0f / max(csf, 1e-7f);

		vfloat4 sum_xp = vfloat4::zero();
		vfloat4 sum_yp = vfloat4::zero();

		for (int i = 0; i < texel_count; i++)
		{
			int iwt = weights[i];
			float weight = texel_weights[iwt];
			vfloat4 texel_datum = vfloat4(data_vr[iwt], data_vg[iwt], 0.0f, 0.0f);
			texel_datum = (texel_datum - average) * weight;

			vfloat4 zero = vfloat4::zero();

			vmask4 tdm0 = vfloat4(texel_datum.lane<0>()) > zero;
			sum_xp += select(zero, texel_datum, tdm0);

			vmask4 tdm1 = vfloat4(texel_datum.lane<1>()) > zero;
			sum_yp += select(zero, texel_datum, tdm1);
		}

		float prod_xp = dot_s(sum_xp, sum_xp);
		float prod_yp = dot_s(sum_yp, sum_yp);

		vfloat4 best_vector = sum_xp;
		float best_sum = prod_xp;

		if (prod_yp > best_sum)
		{
			best_vector = sum_yp;
		}

		pm[partition].dir = best_vector;
	}
}

void compute_error_squared_rgba(
	const partition_info* pt,
	const imageblock* blk,
	const error_weight_block* ewb,
	const processed_line4* uncor_plines,
{'fraction_non_alphanumeric': 0.1380896545503743, 'fraction_numerical': 0.025841075133038693, 'mean_word_length': 3.3497449980384464, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 171, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7577560', 'n_tokens_mistral': 9709, 'n_tokens_neox': 8621, 'n_words': 1842}