package de.lmu.ifi.dbs.elki.algorithm.result.outlier;

import de.lmu.ifi.dbs.elki.algorithm.result.AbstractResult;
import de.lmu.ifi.dbs.elki.data.RealVector;
import de.lmu.ifi.dbs.elki.database.Database;
import de.lmu.ifi.dbs.elki.distance.distancefunction.DimensionsSelectingEuclideanDistanceFunction;
import de.lmu.ifi.dbs.elki.normalization.Normalization;
import de.lmu.ifi.dbs.elki.utilities.UnableToComplyException;
import de.lmu.ifi.dbs.elki.utilities.Util;
import de.lmu.ifi.dbs.elki.utilities.optionhandling.AttributeSettings;

import java.io.PrintStream;
import java.util.BitSet;
import java.util.List;

/**
 * todo arthur comment
 *
 * @author Arthur Zimek
 * @param <O> the type of DatabaseObjects handled by this Result
 */
public class SODModel<O extends RealVector<O, Double>> extends AbstractResult<O> {

    private final DimensionsSelectingEuclideanDistanceFunction<O> DISTANCE_FUNCTION = new DimensionsSelectingEuclideanDistanceFunction<O>();

    private double[] centerValues;

    private double[] variances;

    private double expectationOfVariance;

    private BitSet weightVector;

    private double sod;

    public SODModel(Database<O> database, List<Integer> neighborhood, double alpha, O queryObject) {
        super(database);
        centerValues = new double[database.dimensionality()];
        variances = new double[centerValues.length];
        for (Integer id : neighborhood) {
            O databaseObject = database.get(id);
            for (int d = 0; d < centerValues.length; d++) {
                centerValues[d] += databaseObject.getValue(d + 1);
            }
        }
        for (int d = 0; d < centerValues.length; d++) {
            centerValues[d] /= neighborhood.size();
        }
        for (Integer id : neighborhood) {
            O databaseObject = database.get(id);
            for (int d = 0; d < centerValues.length; d++) {
                // distance
                double distance = centerValues[d] - databaseObject.getValue(d + 1);
                // variance
                variances[d] += distance * distance;
            }
        }
        expectationOfVariance = 0;
        for (int d = 0; d < variances.length; d++) {
            variances[d] /= neighborhood.size();
            expectationOfVariance += variances[d];
        }
        expectationOfVariance /= variances.length;
        weightVector = new BitSet(variances.length);
        for (int d = 0; d < variances.length; d++) {
            if (variances[d] < alpha * expectationOfVariance) {
                weightVector.set(d, true);
            }
        }
        DISTANCE_FUNCTION.setSelectedDimensions(weightVector);
        sod = subspaceOutlierDegree(queryObject);
    }

    public double subspaceOutlierDegree(O queryObject) {
        O center = queryObject.newInstance(centerValues);
        double distance = DISTANCE_FUNCTION.distance(queryObject, center).getValue();
        distance /= weightVector.cardinality();
        return distance;
    }

    public void output(PrintStream outStream, Normalization<O> normalization, List<AttributeSettings> settings) throws UnableToComplyException {
        outStream.println("### " + this.getClass().getSimpleName() + ":");
        outStream.println("### relevant attributes (counting starts with 0): " + this.weightVector.toString());
        outStream.println("### center of neighborhood: " + Util.format(centerValues));
        outStream.println("### subspace outlier degree: " + this.sod);
        outStream.println("################################################################################");
        outStream.flush();
    }

    public double getSod() {
        return this.sod;
    }

}

@@ -293,24 +293,94 @@
 ceFunction;%0A
+import de.lmu.ifi.dbs.elki.normalization.NonNumericFeaturesException;%0A
 import de.lm
@@ -489,51 +489,8 @@
 on;%0A
-import de.lmu.ifi.dbs.elki.utilities.Util;%0A
 impo
@@ -1015,24 +1015,47 @@
 terValues;%0A%0A
+    private O center;%0A%0A
     private 
@@ -1176,16 +1176,20 @@
 le sod;%0A
+    
 %0A    pub
@@ -1773,32 +1773,40 @@
 ize();%0A        %7D
+        
 %0A        for (In
@@ -2707,16 +2707,72 @@
 ector);%0A
+        center = queryObject.newInstance(centerValues);%0A
         
@@ -2810,16 +2810,24 @@
 ryObject
+, center
 );%0A    %7D
@@ -2885,69 +2885,21 @@
 ject
-) %7B%0A        O center = queryObject.newInstance(centerValues);
+, O center) %7B
 %0A   
@@ -3389,24 +3389,40 @@
 oString());%0A
+        try %7B%0A  
         outS
@@ -3472,35 +3472,148 @@
 %22 + 
-Util.format(centerValues));
+normalization.restore(center).toString());%0A        %7D%0A        catch(NonNumericFeaturesException e) %7B%0A          e.printStackTrace();%0A        %7D
 %0A   

Add normalization to center vector reporting