#include "KernelSmoothing.h"

inline double sq (double z) {
	return z*z;
}

// =====================================================================
// ЯДРА -- ЧИСТЫЙ ИЗУМРУД!
// =====================================================================

// Прямоугольное ядро (самое фиговое)
double KSimple (double r) {
	return fabs(r)<1 ? 0.5 : 0;
}

// Гауссовское ядро (носитель не финитный => нельзя сэкономить на просмотре только ближайших)
double KGauss (double r){
	const double PI = 2*asin(1);
	return exp(-r*r/2) / sqrt(2*PI);
}

// Ядро Епанечникова (якобы оптимальное, но производная разрывна)
double KEpanechnikov (double r) {
	return fabs(r)<1 ? 0.75 * (1-r*r) : 0;
}

// Квадратическое ядро (рекомендация маститых ядерщиков)
double KQuadratic (double r) {
	return fabs(r)<1 ? 0.9375 * (1-r*r) * (1-r*r) : 0;
}

// =====================================================================
// Ядерное сглаживание с вычислением евклидовой метрики
// =====================================================================
CKernelSmoothing::CKernelSmoothing () {
	Kernel = KQuadratic;
	h = 1.0;
}

CKernelSmoothing::~CKernelSmoothing () {
} 

// Метод обучения
int CKernelSmoothing::Train (
	CmaMatrix &Features, // матрица Объекты * Признаки
	CmaMatrix &Weight,   // матрица Объекты * {ВесаОбъектов}
	CmaMatrix &Target    // матрица Объекты * {ЦелевойПризнак}
) {
	TrainFeatures = Features;
	TrainWeight = Weight;
	TrainTarget = Target;
	return 0;	
}

// Метод применения
int CKernelSmoothing::Test (
	CmaMatrix &Features, // матрица Объекты * Признаки
	CmaMatrix &Result    // матрица Объекты * {ЦелевойПризнак}
) {
	int k = Features.Size(0);  // длина контрольной выборки
	int L = TrainFeatures.Size(0);  // длина обучающей выборки
	int n = TrainFeatures.Size(1);  // число признаков
	// делаем по-простому, не паримся с просмотром только ближайших точек
	for (int i=0; i<k; i++) {
		double sumWY = 0.0;
		double sumW = 0.0;
		for (int iTrain=0; iTrain<L; iTrain++) {
			// вычисление квадрата расстояния между Test[i] и Train[iTrain]
			double RR = 0.0;
			for (int j=0; j<n; j++)
				RR += sq (TrainFeatures[iTrain][j] - Features[i][j]);
			// вычисление веса обучающей точки
			double w = TrainWeight[iTrain] * Kernel (sqrt(RR)/h);
			// вычисление числителя и знаменателя в формуле Надарая-Ватсона
			sumWY += w * TrainTarget[iTrain];
			sumW += w;
		}
		// запись результата в i-й контрольной точке
		Result[i] = double (sumW>0 ? sumWY/sumW : 0);
	}
	return 0;	
}
