001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2008-2011 SonarSource
004     * mailto:contact AT sonarsource DOT com
005     *
006     * Sonar is free software; you can redistribute it and/or
007     * modify it under the terms of the GNU Lesser General Public
008     * License as published by the Free Software Foundation; either
009     * version 3 of the License, or (at your option) any later version.
010     *
011     * Sonar is distributed in the hope that it will be useful,
012     * but WITHOUT ANY WARRANTY; without even the implied warranty of
013     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014     * Lesser General Public License for more details.
015     *
016     * You should have received a copy of the GNU Lesser General Public
017     * License along with Sonar; if not, write to the Free Software
018     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019     */
020    package org.sonar.plugins.core.timemachine;
021    
022    import java.util.List;
023    
024    public class TendencyAnalyser {
025    
026      public final static Integer TENDENCY_BIG_UP = 2;
027      public final static Integer TENDENCY_UP = 1;
028      public final static Integer TENDENCY_NEUTRAL = 0;
029      public final static Integer TENDENCY_DOWN = -1;
030      public final static Integer TENDENCY_BIG_DOWN = -2;
031    
032      public Integer analyseLevel(List<Double> values) {
033        TendencyAnalyser.SlopeData slopeData = analyse(values);
034        if (slopeData != null) {
035          return slopeData.getLevel();
036        }
037        return null;
038      }
039    
040      public SlopeData analyse(List<Double> values) {
041        double sumY = 0.0;
042        double sumX = 0.0;
043        double sumYPower2 = 0.0;
044        double sumXY = 0.0;
045        double sumXPower2 = 0.0;
046        int nbrPoints = 0;
047        boolean nullValuesYList = true;
048        int i = 0;
049        for (Double p : values) {
050          if (p != null) {
051            nullValuesYList = false;
052            //SumY calculation
053            sumY += p;
054            // sumYPower2 calculation
055            sumYPower2 += p * p;
056            //sumXY calculation
057            sumXY += p * (i + 1);
058            //SumX calculation
059            sumX += (i + 1);
060            //sumXPower2 calculation
061            sumXPower2 += (i + 1) * (i + 1);
062            //Point number calculation
063            nbrPoints++;
064          }
065          i++;
066        }
067        // no tendency if null values or only 1 value
068        if (nullValuesYList || nbrPoints == 1) {
069          return null;
070        }
071        double n0 = (((nbrPoints) * (sumXY)) - ((sumX) * (sumY)));
072        double d = (((nbrPoints) * (sumXPower2)) - ((sumX) * (sumX)));
073        double n1 = (((sumY) * (sumXPower2)) - ((sumX) * (sumXY)));
074    
075        SlopeData result = new SlopeData();
076    
077        //yIntercept Calculation the value when X equals zero
078        result.setYIntercept(n1 / d);
079        // Slope Calculation
080        if (n0 == 0d && d == 0d) {
081          result.setSlope(0.0);
082        } else {
083          Double slope = n0 / d;
084          if (Double.isNaN(slope) || Double.isInfinite(slope)) {
085            result.setSlope(null);
086          } else {
087            result.setSlope(slope);
088          }
089        }
090        result.setSumXPower2(sumXPower2);
091        result.setSumXY(sumXY);
092        result.setSumYPower2(sumYPower2);
093    
094        if (sumXPower2 == 0 || sumYPower2 == 0) {
095          result.setCorrelationRate(0.0);
096        } else {
097          result.setCorrelationRate((sumXY) / (Math.sqrt(sumXPower2 * sumYPower2)));
098        }
099    
100        return result;
101      }
102    
103    
104      static class SlopeData {
105        private double sumXPower2;
106        private double sumYPower2;
107        private double sumXY;
108        private double yIntercept; // not used today
109        private Double slope;
110        private Double correlationRate;
111    
112        public double getSumXPower2() {
113          return sumXPower2;
114        }
115    
116        public void setSumXPower2(double sumXPower2) {
117          this.sumXPower2 = sumXPower2;
118        }
119    
120        public double getSumYPower2() {
121          return sumYPower2;
122        }
123    
124        public void setSumYPower2(double sumYPower2) {
125          this.sumYPower2 = sumYPower2;
126        }
127    
128        public double getSumXY() {
129          return sumXY;
130        }
131    
132        public void setSumXY(double sumXY) {
133          this.sumXY = sumXY;
134        }
135    
136        public double getYIntercept() {
137          return yIntercept;
138        }
139    
140        public void setYIntercept(double yIntercept) {
141          this.yIntercept = yIntercept;
142        }
143    
144        public Double getSlope() {
145          return slope;
146        }
147    
148        public void setSlope(Double slope) {
149          this.slope = slope;
150        }
151    
152        public Double getCorrelationRate() {
153          return correlationRate;
154        }
155    
156        public void setCorrelationRate(Double correlationRate) {
157          this.correlationRate = correlationRate;
158        }
159    
160        public Integer getLevel() {
161          double hSlope = 0.8;
162          double nSlope = 0.2;
163    
164          double vHighCorcoef = 1.0;
165          double modCorcoef = 0.69;
166          Double correlationCoeff = getCorrelationRate();
167          boolean vHCorCoefPos = (correlationCoeff > modCorcoef) && (correlationCoeff <= vHighCorcoef);
168          boolean vHCorCoefNeg = (correlationCoeff < -modCorcoef) && (correlationCoeff >= -vHighCorcoef);
169    
170          if ((vHCorCoefPos || vHCorCoefNeg) && (slope >= hSlope)) {
171            return TENDENCY_BIG_UP;
172    
173          } else if ((vHCorCoefPos || vHCorCoefNeg) && (slope <= -hSlope)) {
174            return TENDENCY_BIG_DOWN;
175    
176          } else if ((vHCorCoefPos || vHCorCoefNeg) && ((slope >= nSlope) && (slope < hSlope))) {
177            return TENDENCY_UP;
178    
179          } else if ((vHCorCoefPos || vHCorCoefNeg) && ((slope <= -nSlope) && (slope > -hSlope))) {
180            return TENDENCY_DOWN;
181    
182          } else if ((vHCorCoefPos || vHCorCoefNeg) && ((slope < nSlope) || (slope > -nSlope))) {
183            return TENDENCY_NEUTRAL;
184    
185          } else if (correlationCoeff == 0 && slope == 0 && !vHCorCoefPos && !vHCorCoefNeg) {
186            return TENDENCY_NEUTRAL;
187          }
188          return null;
189        }
190      }
191    }