@Override
 @SuppressWarnings("unchecked")
 public <M extends Number, O extends Number> Matrix<O> outerProduct(
     Vector<M> y, Vectors.Factory<O> using) {
   if (using == Vectors.AS_INTS) {
     int[][] data2 = new int[n][y.length()];
     for (Map.Entry<Integer, Integer> e : entrySet()) {
       for (int j = 0; j < y.length(); j++) {
         data2[e.getKey()][j] = e.getValue().intValue() * y.intValue(j);
       }
     }
     return (Matrix<O>) new IntArrayMatrix(data2);
   } else if (using == Vectors.AS_REALS) {
     double[][] data2 = new double[n][y.length()];
     for (Map.Entry<Integer, Integer> e : entrySet()) {
       for (int j = 0; j < y.length(); j++) {
         data2[e.getKey()][j] = y.doubleValue(j) * e.getValue().intValue();
       }
     }
     return (Matrix<O>) new DoubleArrayMatrix(data2);
   } else {
     final SparseMatrix<O> matrix = new SparseMatrix<O>(n, y.length(), using);
     for (Map.Entry<Integer, Integer> e : entrySet()) {
       for (Map.Entry<Integer, M> e2 : y.entrySet()) {
         matrix.set(
             e.getKey(), e2.getKey(), e2.getValue().doubleValue() * e.getKey().doubleValue());
       }
     }
     return matrix;
   }
 }
 @Override
 public <M extends Number, O extends Number> Vector<O> mult(
     Vector<M> x, Vectors.Factory<O> using) {
   assert (x.length() == alpha.length);
   final Vector<O> product = using.make(alpha.length, 0.0);
   for (int i = 0; i < alpha.length; i++) {
     double value = 0.0;
     if (i > 0) {
       value += x.doubleValue(i - 1) * beta[i - 1];
     }
     value += x.doubleValue(i) * alpha[i];
     if (i < beta.length) {
       value += x.doubleValue(i + 1) * beta[i];
     }
     product.put(i, value);
   }
   return product;
 }
  /**
   * Find w, such that Mw = v
   *
   * @param v the vector
   * @return w
   */
  public Vector<Double> invMult(Vector<Double> v) {
    final int n = v.length();
    if (n != alpha.length) {
      throw new IllegalArgumentException();
    }
    double[] delta = new double[n - 1];
    double[] gamma = new double[n - 1];

    delta[0] = v.doubleValue(0) / alpha[0];
    gamma[0] = -1.0 * beta[0] / alpha[0];

    for (int i = 1; i < n - 1; i++) {
      final double bga = beta[i - 1] * gamma[i - 1] + alpha[i];
      if (bga != 0.0) {
        delta[i] = (v.doubleValue(i) - beta[i - 1] * delta[i - 1]) / bga;
        gamma[i] = -1.0 * beta[i] / bga;
      } else {
        final double bga2 = beta[i] * gamma[i] + alpha[i + 1];
        if (bga2 == 0.0) {
          // Value is 'free'
          delta[i] = delta[i + 1] = 1.0;
        } else {
          gamma[i + 1] = (v.doubleValue(i + 1) - beta[i] * delta[i]) / bga2;
          delta[i + 1] = -1.0 * beta[i + 1] / bga2;
          gamma[i] = -(alpha[i + 1] * gamma[i + 1] + beta[i + 1]) / beta[i];
          delta[i] = beta[i + 1] * delta[i + 1] / beta[i] * gamma[i + 1];
          i++;
        }
      }
    }

    double[] w = new double[n];
    final double bga3 = beta[n - 2] * gamma[n - 2] + alpha[n - 1];
    if (bga3 == 0.0) {
      w[n - 1] = 1.0; // value is 'free'
    } else {
      w[n - 1] = (v.doubleValue(n - 1) - beta[n - 2] * delta[n - 2]) / bga3;
    }
    for (int i = n - 2; i >= 0; i--) {
      w[i] = gamma[i] * w[i + 1] + delta[i];
    }
    return new RealVector(w);
  }