import java.io.*;
/**
 * This class implements the CYK algorithm
 
 * @Shaun Ross
 * @March 10, 2004
 */
public class CYK
{
    // instance variables
    public String [][] VijTable;    //table for the CYK check
    public FileReader FR;           //instance of the File Reader class
    public String outputFilename;   //instance of the output file name
    public FileWriter writer;       //a file writer for html output
    public PrintWriter out;         //a way to use print and println with my file writer
    public boolean startCheck;      //check to see if the start symbol is in the last place VijTable
    
    /**
     * Constructor for objects of class CYK
     * Initializes things needed for the CYK algorithm, FileReader, VijTable
     * Also calls upon the algorithm
     */
    public CYK(String fileName)
    {
        outputFilename = new String("");
        outputFilename = fileName + ".html";
        try
        {
            writer = new FileWriter(outputFilename);
            out = new PrintWriter(writer);
        }
        catch Exception e )
        {
            e.printStackTrace();
        }
        
        
        FR = new FileReader(fileName);
        VijTable = new String[FR.stringLength+1][FR.stringLength+1];
        VijTable = initialize2DArray(VijTable, FR.stringLength+1, FR.stringLength+1);
        algorithm();
        startCheck = false;
        finalCheck(FR.stringLength);
        htmlOutput();
        try
        {
            writer.close();
        }
        catch Exception e )
        {
            e.printStackTrace();
        }
    }
    
    /**
     * Final check to see if a string is in the grammar
     * Side effect............... prints out if the grammar is in the language
     
     @param      rows ---- the number of rows in the VijTable
     @return     nothing
     */
    private void finalCheck(int rows)
    {
        
        char startSymbol = FR.ruleArray[1][0];
        
        //for loop goes through the length of the string in the last row of VijTable
        for(int i= 0; i< VijTable[rows][0].length(); i++)
        {
            //check to see if the character at the position i is the startSymbol
            if VijTable[rows][0].charAt(i== startSymbol)
            {
                startCheck = true;
            }
        
        
        if(startCheck)
        {
            System.out.println();
            System.out.println("<<<<<<<<<<<<<<  THE STRING IS IN THE GRAMMAR  >>>>>>>>>>>>>>");
            System.out.println();
        }
    }//end finalCheck(int rows)
   
   
   /**
     * this is the actual algorithm of CYK
     *    begin
     *    {
     *        //Step 1
     *        for i:=1 to n do
     *          {
     *              Vi1:= {A|A_. a is a production and the ith symbol of x is a};
     *          }
     *        //Step 2
     *        for j:=2 to n do
     *        {
     *            //Step 3
     *            for i:= 1 to n-j+1 do
     *            {
     *                //Step 6
     *                Vij := nil;
     *                //Step 4
     *                for k:= 1 to j-1 do
     *                {
     *                    //Step 5 --done in multiple parts because of 
     *                               the different combinations possible of BC
     *                    Vij = Vij UNION {A|A -> BC is a production, B is in Vik and C
     *                                     is in Vi+k,j-k}
     *                }
     *            }
     *        }
     *    }
     *      
     
     @param      none
     @return     nothing
     */
    private void algorithm()
    {
        String productions = new String("");        //a string of productions
        String VijValue = new String("");           //the value of the Vij Position in the Table
        String Vik = new String("");                //the Vik value
        String Vother = new String("");             //the Vi+k,j-k value
        boolean VikMult = false;                    //if there are multiple Vik values
        boolean VotherMult = false;                 //if there are multiple Vi+k,j-k values
        char VikSub, VotherSub;                     //Sub parts of Vik and Vi+k,j-k in chars
        String VikSubstring = new String("");       //Vik substring constructed by the VikSub chars
        String VotherSubstring = new String("");    //Vi+k,j-k substring constructed by the VotherSub chars
        
        //put the string at the top of the VijTable
        //Step 1
        for(int i=0; i < FR.stringLength; i++)
        {
            VijValue = "";
            VijValue = VijValue + FR.ruleArray[0][i];
            VijTable[0][i= VijValue;
            productions = findProduction(VijValue);
            VijTable[1][i= productions;
        }
        
        //the rest of the algorithm
        int i, k;
        VijValue = "";
        
        //Step 2
        forint j = 2; j <= FR.stringLength; j++)
        {
            //Step 3
            for (i=0; i < (FR.stringLength-j+1); i++)
            {
                //Step 4
                for(k = 1; k<=j-1; k++)
                {
                    Vik = VijTable[k][i];
                    //check for multiples in Vik
                    if(Vik.length() 1)
                    {
                        VikMult = true;
                    }
                    
                    Vother = VijTable[j-k][i+k];
                    //check for multiples in Vother
                    if(Vother.length() 1)
                    {
                        VotherMult = true;
                    }
                    
                    //don't have multiples in Vik or Vother
                    if!VikMult && !VotherMult )
                    {
                        VijValue = "";
                        VijValue = Vik + Vother;
                        productions = findProduction(VijValue);
                        //will check if the production rule is already there and 
                        //insert it into the table
                        //Step 5
                        checkInsertProduction(productions, j, i);
                        
                    }//end no multiples
                    
                    
                    
                    //have multiples in Vik
                    else if(VikMult && !VotherMult)
                    {
                        //need to get all combonations from multiple Vik's and the Vother
                        for(int p=0; p < Vik.length(); p++)
                        {
                            VikSub = Vik.charAt(p);
                            if(VikSub == ',')
                            {
                                //do nothing it is a , not a grammar terminal or non terminal
                            }
                            //check to see if the combination has a rule that will work
                            else 
                            {
                                VikSubstring = "";
                                VijValue = "";
                                VikSubstring = VikSubstring + VikSub;
                                VijValue = VikSubstring + Vother;
                                productions = findProduction(VijValue);
                                //will check if the production rule is already there and 
                                //insert it into the table
                                //Step 5
                                checkInsertProduction(productions, j, i);
                             }
                        }
                    }//end multiple Vik
                    
                    
                    //have multiples in Vother
                    else if(!VikMult && VotherMult)
                    {
                        //need to get all combonations from multiple Vother's and the Vik
                        for(int l=0; l < Vother.length(); l++)
                        {
                            VotherSub = Vother.charAt(l);
                            if(VotherSub == ',')
                            {
                                //do nothing it is a , not a grammar terminal or non terminal
                            }
                            //check to see if the combination has a rule that will work
                            else
                            {
                                VijValue = "";
                                VotherSubstring = "";
                                VotherSubstring = VotherSubstring + VotherSub;
                                VijValue = Vik + VotherSubstring;
                                productions = findProduction(VijValue);
                                //will check if the production rule is already there and 
                                //insert it into the table
                                //Step 5
                                checkInsertProduction(productions, j, i);
                             }
                        }
                    }//end multiple Vother
                    
                    
                    
                    //have multiples in Vik and Vother
                    else if(VikMult && VotherMult)
                    {
                        int p;
                        for(int o = 0; o < Vik.length(); o++)
                        {
                            VikSubstring = "";
                            VikSub = Vik.charAt(o);
                            VikSubstring = VikSubstring + VikSub;
                            
                            //need to get all combonations from multiple Vother's and the one Vik value
                            for(p=0; p < Vother.length(); p++)
                            {
                                VotherSub = Vother.charAt(p);
                                if(VotherSub == ',')
                                {
                                    //do nothing it is a , not a grammar terminal or non terminal
                                }
                                //check to see if the combination has a rule that will work
                                else
                                {
                                    VotherSubstring = "";
                                    VijValue = "";
                                    VotherSubstring = VotherSubstring + VotherSub;
                                    VijValue = VikSubstring + VotherSubstring;
                                    productions = findProduction(VijValue);
                                    //will check if the production rule is already there and 
                                    //insert it into the table
                                    //Step 5
                                    checkInsertProduction(productions, j, i);
                                 }
                            }//end for loop for Vother length
                        }//end for loop for Vik length
                    }//end multiple Vik and Vother
                    
                    //reset the multiple booleans for Vik and Vother
                    VikMult = VotherMult = false;
                }//end //Step 4 for(k = 1; k<=j-1; k++)
                
                //Step 6 -- put nil if there are no productions that can make that substring
                if (VijTable[j][i].length() 1)
                {
                    VijTable[j][i"nil";
                }
            }//end //Step 3 for (i=0; i < (FR.stringLength-j+1); i++)
        }//end //Step 2 for( int j = 2; j <= FR.stringLength; j++)
    }//end algorithm
    
    
    /**
     * findProduction finds if there is a rule that has the right hand side of the 
     * string To Match
     
     @param      stringToMatch  --the string needed to match a rule for
     @return     String of the rule that matches the string
     */
    private String findProduction(String stringToMatch)
    {
        String matchString = new String("");
        String rulesThatApply = new String("");
        char currentPosition;
        int j = 3;

        //start at 1 because it is the first production rule
        for(int i = 0; i < FR.lineNumber-1; i++)
        {
            matchString = "";
            //start at 3 for the start of right hand side
            for(j = 3; j< FR.column+1; j++)
            {
                currentPosition = FR.ruleArray[i][j];
                //read the entire production, if current possition is a | or a % we have
                //read the entire right hand side of a rule
                if (currentPosition == '|' || currentPosition == '%')
                {
                    //check to see if we match what we are looking for
                    if(matchString.equals(stringToMatch))
                    {
                        //get the rule for this RHS match
                        if(rulesThatApply.length() 0)
                        {
                            rulesThatApply = rulesThatApply + ',' + FR.ruleArray[i][0];
                        }
                        else
                        {
                            rulesThatApply = rulesThatApply+FR.ruleArray[i][0];
                        }
                    }
                    matchString = "";
                }//end if (currentPosition == '|' || currentPosition == '%')
                else
                {
                    //add the character to the matchString
                    matchString = matchString + FR.ruleArray[i][j];
                }
            }//end for(j = 3; j< FR.column+1; j++)
          }//end for(int i = 0; i < FR.lineNumber-1; i++)
         return rulesThatApply;
    }//end findProduction(String stringToMatch)
    
    
    
    /**
     * Check Insert Production, checks to see if there are already rules
     * that are in the position of insertion of a new rule
     
     @param      productions -- the string of the rule that applies
     @param      j           -- the row position in the VijTable
     @param      i           -- the column position in the VijTable
     @return     nothing
     */
    private void checkInsertProduction(String productions, int j, int i)
    {
        if(productions.length() 1)
        {
            //didnt' find any rules
        }
        else ifVijTable[j][i].length() 1)
        {
            //found rules and there aren't any rules that apply yet
            VijTable[j][i]= productions;
        }
        else ifVijTable[j][i].length() >= )
        {
            //found rules and there are already rules
           
           //check for duplicates before we put them in the VijTable
           productions = checkDuplicates(productions, j, i);
           ifproductions.length() )
           {
               VijTable[j][i= VijTable[j][i"," + productions;
           }
        }
     }//end checkInsertProduction(String productions, int j, int i)
    
    
    /**
     * Check Duplicates, checks to see if there are any duplicates in the 
     * productions string or the in the position of the VijTable
     
     @param      productions -- the string of the rules that applie
     @param      j           -- the row position in the VijTable
     @param      i           -- the column position in the VijTable
     @return     Sting of the production rules that aren't duplicates
     */
    private String checkDuplicatesString productions , int j, int i)
    {
        String checkString = new String("");
        String noDupsProductions = new String("");
        char checkChar;
        boolean duplicate = false;
        
        //go through each production to see if it is already in VijTable
        for(int k = 0; k< productions.length(); k++)
        {
            duplicate = false;
            checkChar = productions.charAt(k);
            //if it isn't a , then proceed
            if(checkChar != ',')
            {
                
                //go over each element of VijTable to make sure not duplicated
                for(int l = 0; l < VijTable[j][i].length(); l++)
                {
                    ifVijTable[j][i].charAt(l== checkChar)
                    {
                        duplicate = true;
                    }
                }
            
            //if it isn't a duplicate add it to the productions
            if(!duplicate && noDupsProductions.length() >= 1)
            {
                checkString = "";
                checkString = checkString + checkChar;
                noDupsProductions = noDupsProductions + "," + checkString;
            }
            //if it isn't a duplicate and it is the first one to add to the new productions
            else if(!duplicate && noDupsProductions.length() 1)
            {
                checkString = "";
                checkString = checkString + checkChar;
                noDupsProductions = noDupsProductions +  checkString;
            }
          }//end if(checkChar != ',')
        }//end for(int k = 0; k< productions.length(); k++)
        return noDupsProductions;
    }//end private String checkDuplicates( String productions , int j, int i)
    
    
    /**
    * Initializes a 2D string array with "", nothing
    
    @param      arrayToInitialize -- the 2D string array to initialize
    @param      rows           -- the # of rows
    @param      columns        -- the # of columns
    @return     2D String array
    */
    private String [][] initialize2DArray(String [][] arrayToInitialize, int rows, int columns)
    {
        int j;
        for(int i= 0; i<rows; i++)
        {
            for(j= 0; j<columns; j++)
            {
                arrayToInitialize[i][jnew String("");
            }
        }
        return arrayToInitialize;
    }//end String [][] initialize2DArray(String [][] arrayToInitialize, int rows, int columns)
    
    
    /**
    * html Output, to create output in HTML format to put on the web
    
    @param      none
    @return     nothing
    */
    public void htmlOutput()
    {
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Output for CYK algorithm</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<p>");
        htmlPrintGrammar();
        out.println("</p>");
        out.println("<p>");
        htmlPrintTestString();
        out.println("</p>");
        out.println("<p>");
        htmlPrintVmatrix();
        out.println("</p>");
        out.println("<p>");
        htmlPrintTruth();
        out.println("</p>");
        out.println("</body>");
        out.println("</html>");
        out.println();
    }//end htmlOutput()
    
    /**
    * html print grammar, to create grammar output for web
    
    @param      none
    @return     nothing
    */
    public void htmlPrintGrammar()
    {
        String rule = new String("");
        
        out.println("<h3>Grammar:</h3>");
        int j;
        for(int i = 1; i < FR.ruleArray.length; i++)
        {
            for(j = 0; j < FR.ruleArray[i].length; j++)
            {
                if(FR.ruleArray[i][j!= '%')
                {
                    rule = rule + FR.ruleArray[i][j];
                }
            }
            out.println(rule + "<br />");
            rule = "";
        }
    }//end htmlPrintGrammar()
    
    /**
    * html print test string, to create test string output for web
    
    @param      none
    @return     nothing
    */
    public void htmlPrintTestString()
    {
        String testString = new String("");
        for(int i=0; i < FR.stringLength; i++)
        {
            testString = testString + FR.ruleArray[0][i];
        }
        out.print("<h3>Input String: </h3>");
        out.println(testString);
    }//end htmlPrintTestString()
    
    /**
    * html print V matrix, to create V matrix output for web
    
    @param      none
    @return     nothing
    */
    public void  htmlPrintVmatrix()
    {
        out.println("<h3>V Matrix</h3>");
        out.println("<table>");
        int j;
        for(int i = 1; i < VijTable.length; i++)
        {
            out.println("<tr align = \"center\">");
            for(j = 0; j < VijTable[i].length-i; j++)
            {
                if(startCheck)
                {
                    out.print("<td bgcolor=\"#66FF33\">" + VijTable[i][j"</td>");
                }
                else
                    out.print("<td bgcolor=\"#FF0000\">" + VijTable[i][j"</td>");
                
            }
            out.println("</tr>");
            //out.println("<br />");
        }
        out.println("</table>");
    }//end htmlPrintVmatrix()
    
    /**
    * html print truth, to print out if the grammar is in language output for web
    
    @param      none
    @return     nothing
    */
    public void  htmlPrintTruth()
    {
        String testString = new String("");
        
        for(int i=0; i < FR.stringLength; i++)
        {
            testString = testString + FR.ruleArray[0][i];
        }
        
        if(startCheck)
        {
            out.print(testString);
            out.println(" is in L(G)");
        }
        else
        {
            out.print(testString);
            out.println(" is <b>NOT</b> in L(G)");
        }
    }//end htmlPrintTruth()
    
}//end public class CYK