/*--------------------------------------------------------------*/
/* Licensed to the Apache Software Foundation (ASF) under one   */
/* or more contributor license agreements.  See the NOTICE file */
/* distributed with this work for additional information        */
/* regarding copyright ownership.  The ASF licenses this file   */
/* to you 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.                                           */
/*--------------------------------------------------------------*/

package org.corehunter.objectives.distance.measures;

import java.util.HashMap;
import java.util.Map;

import org.corehunter.data.CoreHunterData;
import org.corehunter.objectives.distance.DistanceMeasure;

/**
 * Caches pairwise distances upon computation.
 * 
 * @author Herman De Beukelaer
 */
public abstract class AbstractDistanceMeasure implements DistanceMeasure {
    
    private final Map<CoreHunterData, Double[][]> cache;
    private MissingValuesPolicy missingValuesPolicy;
    
    public AbstractDistanceMeasure() {
        this(MissingValuesPolicy.FLOOR);
    }
    
    public AbstractDistanceMeasure(MissingValuesPolicy policy){
        cache = new HashMap<>();
        setMissingValuesPolicy(policy);
    }
    
    /**
     * Retrieve a distance from the cache. If the distance is
     * not found in the cache it is first computed and stored.
     * 
     * @param idX id of the first item
     * @param idY id of the second item
     * @param data data from which the distance is computed
     * @return distance as computed or retrieved from the cache
     */
    @Override
    public double getDistance(int idX, int idY, CoreHunterData data){
        Double[][] distances = cache.get(data);
        if(distances != null && distances[idX][idY] != null){
            // return cached value
            return distances[idX][idY];
        } else {
            if(distances == null){
                int n = data.getSize();
                distances = new Double[n][n];
                cache.put(data, distances);
            }
            // compute, store and return
            double d = (idX != idY ? computeDistance(idX, idY, data) : 0.0);
            distances[idX][idY] = d;
            distances[idY][idX] = d;
            return d;
        }
    }
    
    /**
     * Compute distance. This method is called when a distance is not found in the cache.
     * 
     * @param idX id of the first item
     * @param idY id of the second item
     * @param data data from which the distance is computed
     * @return distance between the two items with given id
     */
    protected abstract double computeDistance(int idX, int idY, CoreHunterData data);
    
    @Override
    public final void setMissingValuesPolicy(MissingValuesPolicy policy) {
        if(missingValuesPolicy != policy){
            // update policy and clear cache
            missingValuesPolicy = policy;
            cache.clear();
        }
    }
    
    protected double missingValueContribution(double ceilValue){
        switch(missingValuesPolicy){
            case FLOOR:
                return 0.0;
            case CEIL:
                return ceilValue;
            default:
                throw new RuntimeException(
                        "This should not happen: unexpected missing values policy " + missingValuesPolicy
                );
        }
    }
    
}

@@ -1254,16 +1254,41 @@
 sures;%0A%0A
+import java.util.Arrays;%0A
 import j
@@ -1305,16 +1305,16 @@
 ashMap;%0A
-
 import j
@@ -1639,17 +1639,17 @@
 erData, 
-D
+d
 ouble%5B%5D%5B
@@ -2399,17 +2399,17 @@
         
-D
+d
 ouble%5B%5D%5B
@@ -2471,16 +2471,30 @@
 null && 
+!Double.isNaN(
 distance
@@ -2504,24 +2504,17 @@
 dX%5D%5BidY%5D
- != null
+)
 )%7B%0A     
@@ -2635,16 +2635,50 @@
  null)%7B%0A
+                // allocate cache%0A
         
@@ -2741,17 +2741,17 @@
 s = new 
-D
+d
 ouble%5Bn%5D
@@ -2755,16 +2755,220 @@
 %5Bn%5D%5Bn%5D;%0A
+                // set all values to missing (NaN)%0A                for(double%5B%5D row : distances)%7B%0A                    Arrays.fill(row, Double.NaN);%0A                %7D%0A                // link cache to data%0A
         

optimize distance cache