/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.util.diff;

import java.util.Arrays;

/**
 * @author dyoma
 */
class IntLCS {
  private final int[] myFirst;
  private final int[] mySecond;

  private final LinkedDiffPaths myPathsMatrix;
  private final int[] myPrevPathKey;
  private int[] myPrevEnds;
  private int[] myCurrentEnds;
  private final int myMaxX;
  private final int myMaxY;

  public IntLCS(int[] first, int[] second) {
    myFirst = first;
    mySecond = second;
    myMaxX = myFirst.length;
    myMaxY = mySecond.length;

    myPathsMatrix = new LinkedDiffPaths(myMaxX, myMaxY);
    myPrevPathKey = new int[myMaxX + myMaxY + 1];
    Arrays.fill(myPrevPathKey, -1);
    myPrevEnds = new int[myMaxX + myMaxY + 1];
    myCurrentEnds = new int[myMaxX + myMaxY + 1];
  }

  public int execute() throws FilesTooBigForDiffException {
    for (int d =0; d <= myMaxX + myMaxY; d++) {
      int minDiag = -calcBound(myMaxY, d);
      int maxDiag = calcBound(myMaxX, d);
      if (d != 0)
        System.arraycopy(myPrevEnds, minDiag + myMaxY, myCurrentEnds, minDiag + myMaxY, maxDiag - minDiag);
       else {
        int end = skipEquals(0, 0);
        if (end > 0) {
          int xy = (end) - 1;
          myPrevPathKey[myMaxY] = myPathsMatrix.encodeStep(xy, xy, end, false, -1);
        }
        if (myMaxX == myMaxY && end == myMaxX) return 0;
        myPrevEnds[myMaxY] = end;
        continue;
      }
      for (int k = minDiag; k <= maxDiag; k += 2) {
        int end;
        if (k == -d) {
          int prevEndV = myPrevEnds[k + 1 + myMaxY];
          int vertical = findDiagonalEnd(k + 1, prevEndV, true);
          end = encodeStep(prevEndV, vertical, k, true);
        } else if (k == d) {
          int prevEndH = myPrevEnds[k - 1 + myMaxY];
          int horisontal = findDiagonalEnd(k - 1, prevEndH, false);
          end = encodeStep(prevEndH, horisontal, k, false);
        } else {
          int prevEndH = myPrevEnds[k - 1 + myMaxY];
          int prevEndV = myPrevEnds[k + 1 + myMaxY];
          if (prevEndH+1 > prevEndV) {
            int horisontal = findDiagonalEnd(k - 1, prevEndH, false);
            end = encodeStep(prevEndH, horisontal, k, false);
          } else {
            int vertical = findDiagonalEnd(k + 1, prevEndV, true);
            end = encodeStep(prevEndV, vertical, k, true);
          }
        }
        myCurrentEnds[k + myMaxY] = end;
        if (k == myMaxX - myMaxY && end == myMaxX) return d;
      }
      int[] temps = myCurrentEnds;
      myCurrentEnds = myPrevEnds;
      myPrevEnds = temps;
    }
    throw new RuntimeException();
  }

  public LinkedDiffPaths getPaths() {
    return myPathsMatrix;
  }

  private int findDiagonalEnd(int prevDiagonal, int prevEnd, boolean isVertical) {
    int x = prevEnd;
    int y = x - prevDiagonal;
    if (isVertical) y++;
    else x++;
    return skipEquals(x, y);
  }

  private int encodeStep(int prevEnd, int diagLength, int tDiagonal, boolean afterVertical) throws FilesTooBigForDiffException {
    int end = prevEnd + diagLength;
    int prevDiagonal = tDiagonal + myMaxY;
    if (!afterVertical) end++;
    if (afterVertical) prevDiagonal++;
    else prevDiagonal--;
    int x = end - 1;
    int y = x - tDiagonal;
    if (x == -1 || y == -1 || x >= myMaxX || y >= myMaxY) return end;
    myPrevPathKey[tDiagonal + myMaxY] = myPathsMatrix.encodeStep(x, y, diagLength, afterVertical, myPrevPathKey[prevDiagonal]);
    return end;
  }

  private int calcBound(int bound, int d) {
    return (d <= bound) ? d : 2 * bound - d;
  }

  private int skipEquals(int x, int y) {
    int skipped = 0;
    while (x < myMaxX && y < myMaxY && myFirst[x] == mySecond[y]) {
      skipped += 1;
      x++;
      y++;
    }
    return skipped;
  }
}

@@ -757,16 +757,77 @@
 econd;%0A%0A
+  private final int myStart1;%0A  private final int myStart2;%0A%0A
   privat
@@ -1071,105 +1071,292 @@
     
-myFirst = first;%0A    mySecond = second;%0A    myMaxX = myFirst.length;%0A    myMaxY = mySecond.length
+this(first, second, 0, first.length, 0, second.length);%0A  %7D%0A%0A  public IntLCS(int%5B%5D first, int%5B%5D second, int start1, int count1, int start2, int count2) %7B%0A    myFirst = first;%0A    mySecond = second;%0A    myStart1 = start1;%0A    myStart2 = start2;%0A    myMaxX = count1;%0A    myMaxY = count2
 ;%0A%0A 
@@ -4469,16 +4469,27 @@
 myFirst%5B
+myStart1 + 
 x%5D == my
@@ -4495,16 +4495,27 @@
 ySecond%5B
+myStart2 + 
 y%5D) %7B%0A  

allow to count LCS of subarray