// This is a sub-set of Galois.c
// Modified by Yutaka Sawada for 8-bit only

/* Galois.c
 * James S. Plank
 * April, 2007

Galois.tar - Fast Galois Field Arithmetic Library in C/C++
Copright (C) 2007 James S. Plank

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License aint with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

James S. Plank
Department of Computer Science
University of Tennessee
Knoxville, TN 37996
plank@cs.utk.edu

*/

#include <stdlib.h>

#include "gf8.h"

#define NW   256
#define NWM1 255
#define PRIM_POLY 0x11D

static int *galois_log_table = NULL;
static int *galois_exp_table;

static int *galois_mult_table = NULL;
static int *galois_div_table;

int galois_create_log_tables(void)
{
	int j, b;

	if (galois_log_table != NULL) return 0;
	galois_log_table = _aligned_malloc(sizeof(int) * NW * 4, 64);
	if (galois_log_table == NULL) return -1;
	galois_exp_table = galois_log_table + NW + 1;	// 後で境界をあわせるため

	b = 1;
	for (j = 0; j < NWM1; j++){
		galois_log_table[b] = j;
		galois_exp_table[j] = b;
		b = b << 1;
		if (b & NW) b ^= PRIM_POLY;
	}
	galois_log_table[0] = -1;

	for (j = 0; j < NWM1; j++){
		galois_exp_table[j + NWM1    ] = galois_exp_table[j];
		galois_exp_table[j + NWM1 * 2] = galois_exp_table[j];
	}
	galois_exp_table += NWM1;
	return 0;
}

int galois_create_mult_tables(void)
{
	int j, x, y, logx;

	if (galois_mult_table != NULL) return 0;
	galois_mult_table = _aligned_malloc(sizeof(int) * NW * NW * 2, 64);
	if (galois_mult_table == NULL) return -1;
	galois_div_table = galois_mult_table + NW * NW;

	if (galois_log_table == NULL) {
		if (galois_create_log_tables() < 0) {
			_aligned_free(galois_mult_table);
			galois_mult_table = NULL;
			return -1;
		}
	}

	/* Set mult/div tables for x = 0 */
	j = 0;
	galois_mult_table[j] = 0;   /* y = 0 */
	galois_div_table[j] = -1;
	j++;
	for (y = 1; y < NW; y++) {   /* y > 0 */
		galois_mult_table[j] = 0;
		galois_div_table[j] = 0;
		j++;
	}

	for (x = 1; x < NW; x++){  /* x > 0 */
		galois_mult_table[j] = 0; /* y = 0 */
		galois_div_table[j] = -1;
		j++;
		logx = galois_log_table[x];
		for (y = 1; y < NW; y++){  /* y > 0 */
			galois_mult_table[j] = galois_exp_table[logx + galois_log_table[y]];
			galois_div_table[j]  = galois_exp_table[logx - galois_log_table[y]];
			j++;
		}
	}
	return 0;
}

int galois_multtable_multiply(int x, int y)
{
  return galois_mult_table[(x << 8) | y];
}

int galois_multtable_divide(int x, int y)
{
  return galois_div_table[(x << 8) | y];
}

// ガロア体上での乗数計算、x の y 乗
int galois_power(int x, int y)
{
	int sum_j;

	if (y == 0) return 1; // x^0 = 1
	sum_j = x;
	while (y > 1){
		sum_j = galois_multtable_multiply(sum_j, x);
		y--;
	}
	return sum_j;
}

void galois_free_tables(void) // テーブルを一括して解放するために追加
{
	if (galois_mult_table != NULL) {
		_aligned_free(galois_mult_table);
		galois_mult_table = NULL;
	}
	if (galois_log_table != NULL) {
		_aligned_free(galois_log_table);
		galois_log_table = NULL;
	}
}

void galois_region_xor(
	unsigned char *r1,	// Region 1
	unsigned char *r2,	// Sum region (r2 = r1 ^ r2)
	int nbytes)			// Number of bytes in region
{
	int i;

	for (i = 0; i < nbytes; i++)
		r2[i] ^= r1[i];
}

void galois_region_multiply(
	unsigned char *r1,	// Region to multiply
	unsigned char *r2,	// products go here.
	int nbytes,			// Number of bytes in region
	int multby)			// Number to multiply by
{
	int i, *table;

	table = galois_mult_table + (multby << 8);
	for (i = 0; i < nbytes; i++)
		r2[i] ^= table[ r1[i] ];
}

// チェックサムを計算する
void checksum4(unsigned char *data, unsigned char *hash, int byte_size)
{
	int i, count;
	unsigned int *data4, temp, prev, mask;

	data4 = (unsigned int *)data;
	count = byte_size / 4;
	prev = 0;

	while (count > 0){	// HASH_RANGE バイトごとに
		// 4バイトに XOR する
		temp = 0;
		for (i = 0; i < HASH_RANGE / 4; i++){
			temp ^= data4[i];
			count--;
			if (count == 0)
				break;
		}
		data4 += HASH_RANGE / 4;

		// 前回の値を 2倍して、今回の値を追加する
		mask = (prev & 0x80808080) >> 7;	// 0x01010101 or 0x00000000
		prev = (prev & 0x7F7F7F7F) << 1;	// 3倍する場合は「^=」にすればいい
		prev ^= mask * 0x1D;	// 0x1D1D1D1D or 0x00000000

		prev ^= temp;
	}
	*((unsigned int *)hash) = prev;
}

