docs: public static double correlation(Vec a, Vec b, boolean bothNonZero)
      {
          final double aMean;
          final double bMean;
          if(bothNonZero)
          {
              aMean = a.sum()/a.nnz();
              bMean = b.sum()/b.nnz();
          }
          else
          {
              aMean = a.mean();
              bMean = b.mean();
          }
  
          double r = 0;
          double aSqrd = 0, bSqrd = 0;
  
          if (a.isSparse() || b.isSparse())
          {
              Iterator<IndexValue> aIter = a.getNonZeroIterator();
              Iterator<IndexValue> bIter = b.getNonZeroIterator();
  
              //if one is empty, then a zero forms on the denomrinator
              if (!aIter.hasNext() && !bIter.hasNext())
                  return 1;
              if (!aIter.hasNext() || !bIter.hasNext())
                  return Double.MAX_VALUE;
  
              IndexValue aCur = null;
              IndexValue bCur = null;
  
              boolean newA = true, newB = true;
              int lastObservedIndex = -1;
              do
              {
  
                  if (newA)
                  {
                      if (!aIter.hasNext())
                          break;
                      aCur = aIter.next();
                      newA = false;
                  }
                  if (newB)
                  {
                      if (!bIter.hasNext())
                          break;
                      bCur = bIter.next();
                      newB = false;
                  }
  
                  if (aCur.getIndex() == bCur.getIndex())
                  {
                      //accumulate skipped positions where both are zero
                      if(!bothNonZero)
                          r += aMean * bMean * (aCur.getIndex()-lastObservedIndex - 1);
                      lastObservedIndex = aCur.getIndex();
  
                      double aVal = aCur.getValue() - aMean;
                      double bVal = bCur.getValue() - bMean;
                      r += aVal * bVal;
  
                      aSqrd += aVal * aVal;
                      bSqrd += bVal * bVal;
  
                      newA = newB = true;
                  }
                  else if (aCur.getIndex() > bCur.getIndex())
                  {
                      if (!bothNonZero)
                      {
                          //accumulate skipped positions where both are zero
                          r += aMean * bMean * (bCur.getIndex()-lastObservedIndex - 1);
                          lastObservedIndex = bCur.getIndex();
  
                          double bVal = bCur.getValue() - bMean;
                          r += -aMean * bVal;
                          bSqrd += bVal * bVal;
                      }
                      newB = true;
                  }
                  else if (aCur.getIndex() < bCur.getIndex())
                  {
                      if (!bothNonZero)
                      {
                          //accumulate skipped positions where both are zero
                          r += aMean * bMean * (aCur.getIndex()-lastObservedIndex - 1);
                          lastObservedIndex = aCur.getIndex();
                      
                          double aVal = aCur.getValue() - aMean;
                          r += aVal * -bMean;
                          aSqrd += aVal * aVal;
                      }
                      newA = true;
                  }
              }
              while (true);
  
              if (!bothNonZero)
              {
                  //only one of the loops bellow will execute
                  while (!newA || (newA && aIter.hasNext()))
                  {
                      if(newA)
                          aCur = aIter.next();
                      //accumulate skipped positions where both are zero
                      r += aMean * bMean * (aCur.getIndex()-lastObservedIndex - 1);
                      lastObservedIndex = aCur.getIndex();
  
                      double aVal = aCur.getValue() - aMean;
                      r += aVal * -bMean;
                      aSqrd += aVal * aVal;
                      newA = true;
                  }
  
                  while (!newB || (newB && bIter.hasNext()))
                  {
                      if(newB)
                          bCur = bIter.next();
                      //accumulate skipped positions where both are zero
                      r += aMean * bMean * (bCur.getIndex()-lastObservedIndex - 1);
                      lastObservedIndex = bCur.getIndex();
  
                      double bVal = bCur.getValue() - bMean;
                      r += -aMean * bVal;
                      bSqrd += bVal * bVal;
                      newB = true;
                  }
  
                  r += aMean * bMean * (a.length()-lastObservedIndex - 1);
                  aSqrd += aMean * aMean * (a.length()-a.nnz());
                  bSqrd += bMean * bMean * (b.length()-b.nnz());
              }
          }
          else//dense!
          {
              for(int i = 0; i < a.length(); i++)
              {
                  double aTmp = a.get(i);
                  double bTmp = b.get(i);
                  if(bothNonZero && (aTmp == 0 || bTmp == 0))
                      continue;
                  double aVal = aTmp-aMean;
                  double bVal = bTmp-bMean;
                  r += aVal*bVal;
                  aSqrd += aVal*aVal;
                  bSqrd += bVal*bVal;
              }
          }
          
          if(bSqrd == 0 && aSqrd == 0)
              return 0;
          else if(bSqrd == 0 || aSqrd == 0)
              return r/Math.sqrt((aSqrd+1e-10)*(bSqrd+1e-10));
  
          return r/Math.sqrt(aSqrd*bSqrd);
      }