/* KFNN的消融对象  只保留K值寻优，不要标记分布增强
 * */
package ceka.KFNN;

import java.util.Arrays;
import ceka.core.Dataset;
import ceka.core.Example;
import ceka.core.Label;
import ceka.core.MultiNoisyLabelSet;
import weka.core.Utils;

public class KFNN_noLDE {

	public static final String NAME = "G";
	
	private int numExample = 0;
	private int numCategory = 0;
	
	public void doInference(Dataset dataset) throws Exception {
		// 先定义几个变量
		numCategory = dataset.getCategorySize();
		numExample = dataset.getExampleSize();
		
		double[][] probs = new double[numExample][numCategory];
		double[] count = new double[numCategory];
		for(int i=0; i<numExample; i++) {
			// 计算概率分布
			MultiNoisyLabelSet multipleNoisyLabelSet = dataset.getExampleByIndex(i).getMultipleNoisyLabelSet(0);
			int labelSize = multipleNoisyLabelSet.getLabelSetSize();
			for(int k=0; k<labelSize; k++) {
				probs[i][multipleNoisyLabelSet.getLabel(k).getValue()] += 1.0 / labelSize;
			}
			int tempIndex = Utils.maxIndex(probs[i]);
			count[tempIndex] += 1;
			Label label = new Label(null, String.valueOf(tempIndex), dataset.getExampleByIndex(i).getId(), NAME);
			dataset.getExampleByIndex(i).setIntegratedLabel(label);
		}
		
		// 稳定性设置，防止数据集太不平衡或者出现为0类别
		boolean mark = false;
		
		if(count[Utils.minIndex(count)] < 10) {
			mark = true;
		}
		
		// 划分簇
		Dataset[] subDatasets = new Dataset[numCategory];
		for(int k=0; k<numCategory; k++) {
			subDatasets[k] = new Dataset(dataset,0);
		}
		for(int i=0; i<dataset.getExampleSize(); i++) {
			int label = dataset.getExampleByIndex(i).getIntegratedLabel().getValue();
			subDatasets[label].addExample(dataset.getExampleByIndex(i));
		}
		
		// 构建马氏距离的计算方案
		Mdistance[] tempMdistances = new Mdistance[numCategory];
		for(int k=0; k<numCategory; k++) {
			tempMdistances[k] = new Mdistance(subDatasets[k]);
		}
		
		// 计算概率
		double[][] distances = new double[numExample][numExample];
		double[][] tempDis = new double[numExample][numCategory];
		double[][] probs2 = new double[numExample][numCategory];
		for(int i=0; i<numExample; i++) {
			Example e1 = dataset.getExampleByIndex(i);
			// 先求实例到各个分布的距离
			double tempMin = Double.MAX_VALUE;
			double tempMax = Double.MIN_VALUE;
			for(int k=0; k<numCategory; k++) {
				if(subDatasets[k].getExampleSize() > 0) {
					tempDis[i][k] = tempMdistances[k].calMDistance(e1);
					if(tempDis[i][k] > tempMax)
						tempMax = tempDis[i][k];
					if(tempDis[i][k] < tempMin)
						tempMin = tempDis[i][k];
				}
				else
					tempDis[i][k] = 0;
			}
			for(int k=0; k<numCategory; k++) {
				if(tempDis[i][k] > 0)
					probs2[i][k] = (tempMax - tempDis[i][k]) / (tempMax - tempMin);
				else
					tempDis[i][k] = Double.MAX_VALUE;
			}
			
			for(int j=i+1; j<numExample; j++) {
				Example e2 = dataset.getExampleByIndex(j);
				double temp = 0;
				for(int k=0; k<numCategory; k++) {
					temp += tempMdistances[k].calMDistance(e1, e2);
				}
				distances[i][j] = temp;
				distances[j][i] = temp;
			}
		}

		// 推断
		for(int i=0; i<numExample; i++) {
			Example example = dataset.getExampleByIndex(i);
			MultiNoisyLabelSet mnls = example.getMultipleNoisyLabelSet(0);
			
			int[] indexs = Utils.sort(distances[i]);
			double[] class_counts = new double[numCategory];
			double[][] maxPro = new double[numExample][numCategory];
			// 具体计算
			for(int j=0; j<numExample; j++) {
				Example neighbor = dataset.getExampleByIndex(indexs[j]);

				// 先计算权重
				double tem1 = 0;
				double tem2 = 0;
				for(int k = 0; k < mnls.getLabelSetSize(); k++){
					Label label = neighbor.getNoisyLabelByWorkerId(mnls.getLabel(k).getWorkerId());
					if(label != null){
						tem1++;
						if(label.getValue() == mnls.getLabel(k).getValue()){
							tem2 += 1.0;
						}
					}
				}
				double weight = 0.0; 
				
				//考虑当分母不为0的情况
				if(tem1 == 0){
					weight = 0;
				}else{
					weight = tem2 / tem1;
				}

				for(int k=0; k<numCategory; k++) {
					class_counts[k] += weight * probs[indexs[j]][k];
					maxPro[j][k] = class_counts[k];
				}
				if(Utils.sum(maxPro[j]) != 0)
					Utils.normalize(maxPro[j]);
			}
		    // 判断--找风险最小的地方（差值最大处）
			double q = 0.1; // process noise covariance
	        double r = 1; // measurement noise covariance
	        double p = 1; // estimation error covariance
	        double initialValue = 0; // initial estimated value
	        Kalman kalmanFilter = new Kalman(q, r, p, initialValue);
			
			double[] temp_count2 = new double[numExample];
			int[] tempSort = new int[numCategory];
			for(int j=0; j<numExample; j++) {
				tempSort = Utils.sort(maxPro[j]);
				temp_count2[j] = maxPro[j][tempSort[numCategory-1]] - maxPro[j][tempSort[numCategory-2]];
				temp_count2[j] = kalmanFilter.update(temp_count2[j], 0);
			}
			int maxIndex = Utils.maxIndex(Arrays.copyOfRange(temp_count2, 0, (int)(numExample * 0.5 / numCategory)));
			Label integratedL = new Label(null, String.valueOf(Utils.maxIndex(maxPro[maxIndex])), example.getId(), NAME);
			example.setIntegratedLabel(integratedL);
		}

		// this is important
		dataset.assignIntegeratedLabel2WekaInstanceClassValue();
	}
}
