protected int takeStep(int i1, int i2, double alpha2, double alpha2Star, double phi2) throws Exception {
    //procedure takeStep(i1, i2)
    //
    //  if (i1 == i2) 
    //    return 0 
    if (i1 == i2) {
      return 0;
    }
    double C1 = m_C * m_data.instance(i1).weight();
    double C2 = m_C * m_data.instance(i2).weight();
    //  alpha1, alpha1' = Lagrange multipliers for i1 
    double alpha1 = m_alpha[i1];
    double alpha1Star = m_alphaStar[i1];
//  double y1 = m_target[i1];
    // TODO: verify we do not need to recompute m_error[i1] here
    // TODO: since m_error is only updated for indices in m_I0
    double phi1 = m_error[i1];
//  if ((m_iSet[i1] & I0)==0) {
//  phi1 = -SVMOutput(i1) - m_b + m_target[i1];
//  m_error[i1] = phi1;
//  }
    //  k11 = kernel(point[i1], point[i1]) 
    //  k12 = kernel(point[i1], point[i2]) 
    //  k22 = kernel(point[i2], point[i2]) 
    //  eta = -2*k12+k11+k22 
    //  gamma = alpha1-alpha1'+alpha2-alpha2'
    //
    double k11 = m_kernel.eval(i1, i1, m_data.instance(i1));
    double k12 = m_kernel.eval(i1, i2, m_data.instance(i1));
    double k22 = m_kernel.eval(i2, i2, m_data.instance(i2));
    double eta = -2 * k12 + k11 + k22;
    double gamma = alpha1 - alpha1Star + alpha2 - alpha2Star;
//  if (eta < 0) {
    // this may happen due to numeric instability
    // due to Mercer's condition, this should not happen, hence we give up
//  return 0;
//  }
    //  % We assume that eta > 0. Otherwise one has to repeat the complete 
    //  % reasoning similarly (i.e. compute objective functions at L and H 
    //  % and decide which one is largest
    //
    //  case1 = case2 = case3 = case4 = finished = 0 
    //  alpha1old = alpha1, 
    //  alpha1old' = alpha1' 
    //  alpha2old = alpha2, 
    //  alpha2old' = alpha2' 
    //  deltaphi = F1 - F2 
    //
    
    //  while !finished
    //    % This loop is passed at most three times 
    //    % Case variables needed to avoid attempting small changes twice 
    //    if (case1 == 0) &&
    //       (alpha1 > 0 || (alpha1' == 0 && deltaphi > 0)) && 
    //       (alpha2 > 0 || (alpha2' == 0 && deltaphi < 0))
    //        compute L, H (w.r.t. alpha1, alpha2) 
    //        if (L < H)
    //          a2 = alpha2 - (deltaphi / eta ) a2 = min(a2, H) a2 = max(L, a2) a1 = alpha1 - (a2 - alpha2) 
    //          update alpha1, alpha2 if change is larger than some eps 
    //        else
    //          finished = 1 
    //        endif 
    //      case1 = 1 
    //    elseif (case2 == 0) &&
    //           (alpha1 > 0 || (alpha1' == 0 && deltaphi > 2*epsilon)) && 
    //           (alpha2' > 0 || (alpha2 == 0 && deltaphi > 2*epsilon))
    //
    //        compute L, H (w.r.t. alpha1, alpha2') 
    //        if (L < H)
    //          a2 = alpha2' + ((deltaphi - 2*epsilon)/eta)) a2 = min(a2, H) a2 = max(L, a2) a1 = alpha1 + (a2-alpha2') 
    //          update alpha1, alpha2' if change is larger than some eps 
    //        else
    //          finished = 1 
    //        endif 
    //        case2 = 1 
    //    elseif (case3 == 0) &&
    //           (alpha1' > 0 || (alpha1 == 0 && deltaphi < -2*epsilon)) && 
    //           (alpha2 > 0 || (alpha2' == 0 && deltaphi < -2*epsilon))
    //         compute L, H (w.r.t. alpha1', alpha2) 
    //         if (L < H)
    //           a2 = alpha2 - ((deltaphi + 2*epsilon)/eta) a2 = min(a2, H) a2 = max(L, a2) a1 = alpha1' + (a2 - alpha2) 
    //           update alpha1', alpha2 if change is larger than some eps 
    //         else
    //           finished = 1 
    //         endif 
    //         case3 = 1 
    //    elseif (case4 == 0) &&
    //           (alpha1' > 0) || (alpha1 == 0 && deltaphi < 0)) && 
    //           (alpha2' > 0) || (alpha2 == 0 && deltaphi > 0))
    //         compute L, H (w.r.t. alpha1', alpha2') 
    //         if (L < H) 
    //           a2 = alpha2' + deltaphi/eta a2 = min(a2, H) a2 = max(L, a2) a1 = alpha1' - (a2 - alpha2') 
    //           update alpha1, alpha2' if change is larger than some eps 
    //         else
    //           finished = 1 
    //         endif 
    //         case4 = 1 
    //    else
    //      finished = 1 
    //    endif 
    //    update deltaphi 
    //  endwhile 
    
    double alpha1old = alpha1;
    double alpha1Starold = alpha1Star;
    double alpha2old = alpha2;
    double alpha2Starold = alpha2Star;
    double deltaPhi = phi1 - phi2;
    
    if (findOptimalPointOnLine(i1, alpha1, alpha1Star, C1, i2, alpha2, alpha2Star, C2, gamma, eta, deltaPhi)) {
      
      alpha1 = m_alpha[i1];
      alpha1Star = m_alphaStar[i1];
      alpha2 = m_alpha[i2];
      alpha2Star = m_alphaStar[i2];
      
      //  if changes in alpha('), alpha2(') are larger than some eps
      //    Update f-cache[i] for i in I.0 using new Lagrange multipliers 
      //    Store the changes in alpha, alpha' array 
      //    Update I.0, I.1, I.2, I.3 
      //    Compute (i.low, b.low) and (i.up, b.up) by applying the conditions mentioned above, using only i1, i2 and indices in I.0 
      //    return 1 
      //  else
      //    return 0
      //endif endprocedure
      
      //		Update error cache using new Lagrange multipliers 
      double dAlpha1 = alpha1 - alpha1old - (alpha1Star - alpha1Starold);
      double dAlpha2 = alpha2 - alpha2old - (alpha2Star - alpha2Starold);
      for (int j = m_I0.getNext(-1); j != -1; j = m_I0.getNext(j)) {
	if ((j != i1) && (j != i2)) {
	  m_error[j] -= dAlpha1 * m_kernel.eval(i1, j, m_data.instance(i1)) 
	  + dAlpha2 * m_kernel.eval(i2, j, m_data.instance(i2));
	}
      }
      m_error[i1] -= dAlpha1 * k11 + dAlpha2 * k12;
      m_error[i2] -= dAlpha1 * k12 + dAlpha2 * k22;
      
      updateIndexSetFor(i1, C1);
      updateIndexSetFor(i2, C2);
      
      //    Compute (i.low, b.low) and (i.up, b.up) by applying the conditions mentioned above, using only i1, i2 and indices in I.0 
      m_bUp = Double.MAX_VALUE; 
      m_bLow = -Double.MAX_VALUE; 
      for (int j = m_I0.getNext(-1); j != -1; j = m_I0.getNext(j)) {
	updateBoundaries(j, m_error[j]);
      }
      if (!m_I0.contains(i1)) {
	updateBoundaries(i1, m_error[i1]);
      }
      if (!m_I0.contains(i2)) {
	updateBoundaries(i2, m_error[i2]);
      }
      
      return 1;
    } 
    else {
      return 0;
    }
  }