001    package jcolibri.method.retrieve.NNretrieval.similarity.local.recommenders;
002    
003    import jcolibri.exception.NoApplicableSimilarityFunctionException;
004    import jcolibri.method.retrieve.NNretrieval.similarity.LocalSimilarityFunction;
005    
006    
007    
008    /**
009     * This function returns the similarity of two numbers (or enums) following 
010     * the INRECA - Less is Better formulae
011     * 
012     * sim(c.a,q.a)= if(c.a < q.a) then 1 else  jump * (max(a) - c.a) / (max(a) - q.a)
013     * 
014     * jump and max(a) must be defined by the designer.
015     */
016    public class InrecaLessIsBetter implements LocalSimilarityFunction {
017    
018    
019            double maxValue;
020            double jump;
021    
022            /**
023             * Constructor. max value is ignored for enum types.
024             */
025            public InrecaLessIsBetter(double maxAttributeValue, double jumpSimilarity) {
026                this.maxValue = maxAttributeValue;
027                this.jump = jumpSimilarity;
028            }
029    
030            /**
031             * Applies the similarity function.
032             * 
033             * @param caseObject is a Number
034             * @param queryObject is a Number
035             * @return result of apply the similarity function.
036             */
037            public double compute(Object caseObject, Object queryObject) throws NoApplicableSimilarityFunctionException{
038                    if ((caseObject == null) || (queryObject == null))
039                            return 0;
040                    if (! ((caseObject instanceof java.lang.Number)||(caseObject instanceof Enum)))
041                            throw new jcolibri.exception.NoApplicableSimilarityFunctionException(this.getClass(), caseObject.getClass());
042                    if (! ((queryObject instanceof java.lang.Number)||(queryObject instanceof Enum)))
043                            throw new jcolibri.exception.NoApplicableSimilarityFunctionException(this.getClass(), queryObject.getClass());
044    
045                    double caseValue;
046                    double queryValue;
047                    double max;
048                    if(caseObject instanceof Number)
049                    {
050                        Number n1  = (Number) caseObject;
051                        Number n2  = (Number) queryObject;
052                        caseValue  = n1.doubleValue();
053                        queryValue = n2.doubleValue();
054                        max = maxValue;
055                    }
056                    else
057                    {
058                        Enum enum1 = (Enum)caseObject;
059                        Enum enum2 = (Enum)queryObject;
060                        caseValue  = enum1.ordinal();
061                        queryValue = enum2.ordinal();
062                        max = caseObject.getClass().getEnumConstants().length;
063                    }
064                    
065                    if(caseValue <= queryValue)
066                        return 1;
067                    if(caseValue>=maxValue)
068                        return 0;
069                    
070                    else return jump * (max-caseValue) / (max - queryValue);
071                    
072            }
073            
074            /** Applicable to any Number subinstance */
075            public boolean isApplicable(Object o1, Object o2)
076            {
077                    if((o1==null)&&(o2==null))
078                            return true;
079                    else if(o1==null)
080                            return (o2 instanceof Number)||(o2 instanceof Enum);
081                    else if(o2==null)
082                            return (o1 instanceof Number)||(o1 instanceof Enum);
083                    else
084                            return ((o1 instanceof Number)&&(o2 instanceof Number)) ||
085                                    ((o1 instanceof Enum)&&(o2 instanceof Enum));
086            }
087    
088    }