//*** EXPERIMENTATION CODE ***
/*

//I forsee two routes to complete this:

//SOLUTION A:
//I can strip down the original string as I map the letters. I know the possibleMappingFirst and possibleMappingSecond
//I can then re-initialise them again once the conversion has been truncated...
//This will bypass lots of logic about using the randomness and also the  {1,2,2} {1,2,1,1}  for 
 encoded message such as  11416 in order to map it exactly....
 This would be suited for another class altogether...
 
 //SOLUTION B:
 I will treat it more like a military exercise. But it will take lots of compute and we can expect if
 the encoded message is too lengthy, it can be envisioned completing  {1,1,1,1,1,1,1} on  encoded message 12345678
 would have lots stress and potentially not solve the problem.
 It will work on the basis that the encoded string is NOT truncated... 
  Everything else will be on a random and explorative basis..
 My preferred choice.
 POTENTIAL ISSUES - impact on the cycle count..


 SOLUTION C
 Same as solution b,  BUT perform truncation... But let the random fill remaining of the encoding....
 Evaluate at the end...
 
SOLUTION D:
Tackle it from a complete random basis, all possibleLetters are known...
It will collapse as the encoding becomes too long.
This would be the least heavy code and could be complete in one class.

//

Online Java - IDE, Code Editor, Compiler
Online Java is a quick and easy tool that helps you to build, compile, test your programs
online.
*/
// This has been created to ensure I can utilize any random functions more efficiently.
// It is a creation of the permutations (with replacement) calculator.


// All done, see documentation
//Since there are not multiple instantiations similar to combinatins when r varied,
//I can safely reduce all variables without static

//all SET variables, cycles....these need to become Static variables.
//the rest can remain non-static
//values associated with storing the finished encoding => mappings into the final 

//in hindsight, perhaps did not need to even go through this process of waiting for first mapping??

///***CHANGES REQUIRED *******************************
//get rid of logic that it has to get L first time on sj2. This can be prepopulated
//everytime a valid mappping for secondScenarioMapping, 

//at the moment it is filing sj based on getting A=1 first and then any that appear
//cam just prep

//SEEMS LIKE A WASTE OF TIME THE CODE
//IT WILL ALREADY TAKE CARE OF THE SELECTION FROM possibleLetters!!!!!!!!!!
//ALL that was needed for preset first letter.......


import java.math.*;
import java.util.*;

class Encoding
{
    int backupFirstTemp1;
    int backupSecondTemp1;
    int temp1;
    String mapping;
    String possibleMappingFirst;
    String possibleMappingSecond;
    long permutations;
    StringJoiner sj;
    StringJoiner sj1;
    int num;
    int r;
    
    static int cycles=0;
    static int totalcycles=0;
    static int difference = 0;
    
    static List <String> backupValuesSet = new ArrayList<>();
    
    String originalString;
    Map<Integer,String> possibleLettersMap;
    Map<Integer, String> mp;
    static Set<String> st = new HashSet<>();
    Random rand = new Random();
    static String[] valuesSet; 
    static String []backupValuesSetBeforeModification;
    static int subsetEntry=1;
    
    
    public Encoding(long permutations, Map<Integer,String> mp, Map<Integer,String> possibleLettersMap, int r, String possibleMappingFirst, String possibleMappingSecond, String originalString, String[]arrangements)
    {
        this.originalString=originalString;
        this.permutations=permutations;
        this.mp=mp;
        this.possibleLettersMap=possibleLettersMap;
        //this.rMin=rMin;
        //this.rMax=rMax;
        this.possibleMappingFirst=possibleMappingFirst;
        this.possibleMappingSecond=possibleMappingSecond;
        this.r=r;
        
        sj  = new StringJoiner("");
        sj1 = new StringJoiner("");
        boolean invalidIndex=false;
        
        int currentSetSize=0;
        int newSetSize=0;
        int subsetNumber=1;
        int steps;
        String subsetIntToString="";
        int pos=0;
        int n=0;
        String secondScenarioMapping="";
        String firstScenarioMapping="";
        int q=0;
       
        System.out.println("*************INITIAL VALUE OF  CYCLES: " + cycles);
        
        do
        {
            //need to understand the boundaries here a bit better
            //this recursive method will be called for r = rMin   to  r< rMax
            //P(n,r)
            
            //When it gets to here, it needs to focus on r
            
            
            for (q=0; q<r;q++)
            {
                
            //***r runs between rMin and rMax
            if (sj.length()>=r)
            {
                System.out.println("Will attempt to add String for analysis: " + sj.toString());
                st.add(sj.toString());
                
                System.out.println("-----ADDED INTO FINAL SET: " + sj.toString() + "   Set size: " + st.size());
                
                //System.out.println("clearing stringjoiner************");
                sj = new StringJoiner ("");
                cycles++;
                totalcycles++;
                newSetSize=st.size();
                
            }
            
             //I designed this so it can also start running the second possible commencing letter
             if (sj1.length()>=r)
            {
                System.out.println("Will attempt to add String for analysis: " + sj1.toString());
                st.add(sj1.toString());
                System.out.println("-----ADDED INTO FINAL SET: " + sj1.toString());
                
                //System.out.println("clearing stringjoiner************");
                sj1 = new StringJoiner ("");
                cycles++;
                totalcycles++;
                newSetSize=st.size();
            }
                
                //this is fine, it would start again
                if (invalidIndex)
                {
                    //I do not think I need to reset StringJoiner since it would have not stored anything
                    //until it has checked q==1
                    //but no harm in performing
                    
                    //System.out.println("Clearing StringJoiner************");
                    
                    if (!firstScenarioMapping.equals(possibleMappingFirst))
                    {
                    sj=new StringJoiner("");
                    }
                    
                    if(!secondScenarioMapping.equals(possibleMappingSecond))
                    {
                    sj1=new StringJoiner("");
                    }
                    //I think q has to be 0 again since it has to find a suitable first mapping
                    
                    q=0;
                    //System.out.println("----------Q reset to 0");
                    
                    invalidIndex=false;
                }
                
                //At this point, we need need available all permutations using 1 and 2 of creating
                //the encoded message... For instance if the encoed message was 1234
                //permutations {1,1,1,1}, {2,2} {1,1,2} {1,2,1}, {2,1,1}, {1,2,1}
                //this will reduce the selection significantly
                
                temp1 = rand.nextInt(possibleLettersMap.size()) + 1;      //this should get a random number between 0 and the set size
                
                
                //System.out.println("this is random number: " + temp1);
                //System.out.println("mapping: " + )
                
                //*** q will be 0 when nothing has been added to the StringJoiner
                if (q==0)
                {
                //using random number to get value from possibleLetters of the Map
                backupFirstTemp1 = temp1;
                firstScenarioMapping  = mp.get(backupFirstTemp1);
                //System.out.println("Q is 0: " + firstScenarioMapping);
                mapping=firstScenarioMapping;
                }
                
                //this is the other possibility of the first letter in which it can be composed
                //from 2 digits wide
                if (q==1)
                {
                    backupSecondTemp1=temp1;
                    secondScenarioMapping = String.valueOf(backupFirstTemp1).concat(String.valueOf(backupSecondTemp1));
                    //System.out.println("This is second: " + secondScenarioMapping);
                    int secondScenarioMappingInteger = Integer.valueOf(secondScenarioMapping);
                    //System.out.println("int to check in the map: " + secondScenarioMappingInteger);
                    
                    //***it is now getting LETTER => NUMER MAPPING
                    secondScenarioMapping = mp.get(secondScenarioMappingInteger);
                    
                    mapping=secondScenarioMapping;
                    
                    //System.out.println("Q is 1: " + secondScenarioMapping);
                }
                
                //now at this point, we need perform several analysis
                
                if (q==1)
                {
                    // this is correct, it has received first random number, it has checked for the mapping and it
                    //has the letter in her
                    //System.out.println("first scenario mappping: " + firstScenarioMapping);
                    
                    //this is correct, it has received two random numbers, merged them and checked for mapping.....
                    //System.out.println("second second mapping: " + secondScenarioMapping);  //this is correct
                    
                    //these are from other class and these will always remain constant
                    //since it is based on the encoded message provided by end user.
                    //System.out.println("check 1: " + possibleMappingFirst);
                    //System.out.println("check 2: " + possibleMappingSecond);
                
                //if the first mapping is not
                
                
                try
                {
                    //System.out.println("**********************8INSIDE TRY:");
                    
                //this now checks that if either of the randomly generated mappings are not possible mappings,
                //it would break execution....
                //it is in a try since there can be NullPointerException, we know this would occur
                //if the random number was let's say 43 (two digits wide)
                
                //**firstScenarioMapping has the letter from map (based on single random number)
                //***secondScenarioMapping has the letter from map (based on two random numbers combined)
                //**** possibleMappingFirst is a letter from the map  of actual letter corresponding to single digit 
                //mp.get(digitToInteger);
                //**** possibleMappingSecond is a letter from the map  of actual letter corresponding to two front digits 
                //mp.get(digitToInteger);
                //if no matches, sets flag to true. This will come into effect next cycle iteration.
                
                if (!(firstScenarioMapping.equals(possibleMappingFirst)  || secondScenarioMapping.equals(possibleMappingSecond)))
                {
        
                    invalidIndex=true;
                    break;
                }
                
                //this is where it will get tricky
                //since both are viable first mappings and have been translated successfully, we need to run a StringJoiner with both as options.
                
                //12 1222        L = 12    we know that the mappings can be completed with 3 letters (min)  -  5 max
                //  1  21222     A=1       we know mappings would complete with 4 letters (minimum)  and 6 maximum
                //so how would this be handled with respect to q which is based on i (which operates between rMin and rMax)
                
                //it potentially means that if it takes the secondScenarioMapping (wider 2 digits), then it would need to perform one less operation.
                
                  
                //A=1
                //L=12
                
                //it will be here if there is NOT a null
                //in mappings 
                
                else
                {
                    //**comparing letters directly between expected and generated
                     if (firstScenarioMapping.equals(possibleMappingFirst))
                    {
                    
                    System.out.println("this is valid first scenario: " + firstScenarioMapping);
                    System.out.println("addding to SJ: " + sj.add(firstScenarioMapping));
                    }
                    
                    //if it has found a mapping of a letter, it is also viable
                    //it would need to be populated into another sj1 as discussed
                    if (secondScenarioMapping.equals(possibleMappingSecond))
                    {
                        System.out.println("this is valid second scenario: " + secondScenarioMapping);
                        sj1.add(secondScenarioMapping);
                    }
                    
                }
                
                
                }  //end try
                
                //so we know if the two digit wide number was 43, it would enter here since it would not find a mapping
                //value would be null
                //but this does not mean it will meet the criteria that it has generated mapping with either A or L in front on this instance.
                //since neither would cause NullPointerException
                //if it a catch, we just need to verify that
                //firstScenarioMapping.equals(possibleMappingFirst  == true
                //if this is the case, it would proceed and perform mapping and store in the StringJoiner
            
                catch (NullPointerException e)
                {
                    //System.out.println("INSIDE CATCH");
                    if (firstScenarioMapping.equals(possibleMappingFirst))
                    {
                        //System.out.println("1adding to SJ: " + firstScenarioMapping);
                        sj.add(firstScenarioMapping);
                        currentSetSize=st.size();
                    }
                    
                }
                
                
                 
                }  //end of if q==1
                
                
                //now this is the reality situation, when dealing with getting a random letter from the possibleLetters
                //where everything is out of control
                
                
                //we know that this String is based on having a J=10 or higher as first mapping
                //so it will continue normal operation....
                //it should have one less than sj
                
                    if (!invalidIndex && sj.length()!=0)
                 {
                     
                     
                         if (q>1)
                {
                    sj.add(mp.get(temp1));
                    //System.out.println("2Adding to sj: " + mp.get(temp1));
                }
                
                if (q>2 && sj1.length()!=0)
                {
                    
                
                //need to perform one less operation here if it has inserted a two digit wide mapping J=10 in first position onwards
                    //we would need to start from q=2 onwards here.....
                    sj1.add(mp.get(temp1));
                }
                
                     
                        //System.out.println("ADDING EXTRA LETTER TO THE MAPPING: " + );
                        //System.out.println("MAPPING SO FAR (SJ): " + sj.toString());
                        //System.out.println("MAPPING SO FAR (SJ1): " + sj1.toString());
                        
                        //System.out.println("HERE");
            
            
            
            //**************************************************
            //AT THIS POINT WE CAN ADD THE STRINGJOINERS INTO THE ST (SET)
            
            //System.out.println("******************Contents sj: " + sj.toString());
            //System.out.println("******************Contents sj1: " + sj1.toString());
            
            
                    }  //end of if (!invalidIndex)
                
                
            }   //end of for (int q=0; q<r;q++)
            
            
            
        }while (cycles<permutations);
        

        valuesSet = st.toArray(new String[st.size()]);
        backupValuesSetBeforeModification = st.toArray(new String[st.size()]);
        
        System.out.println("******************Contents of the backup set");
        System.out.println("******************Contents of the valuesSet");
        
        for (String g: valuesSet)
        {
            System.out.println("Subset " + subsetNumber+": " + g);
            subsetNumber++;
        }
        
        subsetNumber=1;
        
        for (String m: backupValuesSet)
        {
            n=0;
            do
            {
                if (m==valuesSet[n])
                {
                }
                
                n++;
                
            }while (n<valuesSet.length);
            
        }  //end for processing backupValuesSet
        
        System.out.println("*************NEW VALUE CYCLES: " + cycles);
        System.out.println("*************RUNNING TOTAL CYCLES: " + totalcycles);
        
        System.out.println("***PROCESSING SET AT INDEX: " + (difference));
        System.out.println("**ENDING AT INDEX:***** " + st.size() +"\n");
        
        backupValuesSet= new ArrayList<>(Arrays.asList(backupValuesSetBeforeModification)); 
        
        for (int entry=0; entry<valuesSet.length; entry++)
        {
            if (valuesSet[entry]!="ALREADY PROCESSED")
            {
                System.out.println(valuesSet[entry] + "    Subset: " + subsetEntry  + "  at cycle number: " + totalcycles);
                subsetEntry++;
            
                //MAIN LOGIC OF THE NEW CODE OFFICIALLY STARTS HERE.
                checkMappings(valuesSet[entry]);
            }
        }
        difference = newSetSize;
    }  //end of constructor...
    
    public void checkMappings (String storedEntry)
    {
        //can put the characters through a switch statement
        
        String conversion="";
        String num;
        
        String [][] letters = new String[][]{ {"A","1"},
                                              {"B","2"},
                                              {"C","3"},
                                              {"D","4"},
                                              {"E","5"},
                                              {"F","6"},
                                              {"G","7"},
                                              {"H","8"},
                                              {"I","9"},
                                              {"J","10"},
                                              {"K","11"},
                                              {"L","12"},
                                              {"M","13"},
                                              {"N","14"},
                                              {"O","15"},
                                              {"P","16"},
                                              {"Q","17"},
                                              {"R","18"},
                                              {"S","19"},
                                              {"T","20"},
                                              {"U","21"},
                                              {"V","22"},
                                              {"W","23"},
                                              {"X","24"},
                                              {"Y","25"},
                                              {"Z","26"}
        };
        
        for (char c: storedEntry.toCharArray())
        {
            for (int i=0; i<letters.length; i++)
            {
                if (letters[i][0].equals(Character.toString(c)))
                {
                    conversion = conversion + letters[i][1];
                    System.out.println("**************************************: " + conversion);
            }
            }
            }
        
        
        System.out.println("*******CONVERSION=> " + storedEntry + "    ORIGINAL => " + originalString  + "    Mapped =>  " + conversion);
        if (conversion.equals(originalString))
        {
            System.out.println("-------------------------------This is a valid translation: " + storedEntry + " for encoding: " + originalString+"----------------------------------\n");
        }
        else
        {
            System.out.println("INVALID\n");
        }
        
        conversion="";
        
    }
   
} //end of class.

public class Permutation
{
    public static void main(String[] args) 
    {
        //Keys k = Keys.A;
        //int m = k.map;
        
        Set<String> possibleLetters = new HashSet<>();
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        Map<Integer,String> possibleLettersMap = new HashMap<>();
        Map <Integer,String> mp = new HashMap<>();
        String possibleMappingFirst="";
        String possibleMappingSecond="";
        Encoding sc;
        String temp="";
            
        mp.put(1,"A");
        mp.put(2,"B");
        mp.put(3,"C");
        mp.put(4,"D");
        mp.put(5,"E");
        mp.put(6,"F");
        mp.put(7,"G");
        mp.put(8,"H");
        mp.put(9,"I");
        mp.put(10,"J");
        mp.put(11,"K");
        mp.put(12,"L");
        mp.put(13,"M");
        mp.put(14,"N");
        mp.put(15,"O");
        mp.put(16,"P");
        mp.put(17,"Q");
        mp.put(18,"R");
        mp.put(19,"S");
        mp.put(20,"T");
        mp.put(21,"U");
        mp.put(22,"V");
        mp.put(23,"W");
        mp.put(24,"X");
        mp.put(25,"Y");
        mp.put(26,"Z");
        
        int rMin;
        int r=0;
        String str = "2432589";   //this is encoded message I have referenced in my logic documentation
        int rMax = str.length();
        int n=str.length();
        int rLimit = 64;  //taken to be 64 from past experience.
        int digitsToInteger=0;
        Character firstDigit;
        Character secondDigit;
        int index=0;
        int lengthEncodedMessage=str.length();

	/* 
//int [] arrayNums = new int[] {1,2};
//CAN call Combination class in Amazon steps challenge....Combination as = new Combination(n, arrayNums);
//would need to move all the content out of the main method in Amazon Steps into the constructor above... It will call the Combinations method and recursively get the number of arrangements.
//The arrangements can then be passed into the staircase constructor below.... //we will be back in this class.
 //we will know the arrangements i.e if Z is selected in following configuration, it will be rejected 1,2,1//only thing to consider is as th wheter permutation is correct.......
 the permutation would do calculations such as
 
 */
 
int[] lengthLetters = new int[] {1,2};  //we know accepted will be A=1 to Z=26
Combination comb = new Combination (lengthLetters,lengthEncodedMessage);  //calling class to work out arrangements
 
//combination class will call the staircase Class
//need to get the arrangements back here... It is stored in Set<String> s; on staircase class

//I will chain references as oppose to creating a new instance of the staircase class

//comb.sc.s;

int counter=0;
String []arrangements = new String [comb.sc.arrangements().size()];

//these are all arrangements here
for (String s: comb.sc.arrangements())
{
    System.out.println("---------------------NOW ON THE PERMUTATION SIDE------: " + s);
    
     arrangements[counter]=s;
     counter++;
}



            
        for (int i=0; i<str.length();i++)
        {
            //System.out.println("Value of i: " + i);
            
            //also need to work with scenario that from i= onwards
            //it can join with the digit before and form a valid letter
            //this is how all permutations are generated
            
            if (i>=1)
            {
                String possibleEncoding = String.valueOf(str.charAt(i-1)).concat(String.valueOf(str.charAt(i)));
                digitsToInteger = Integer.valueOf(possibleEncoding);
            }
            
            
            if (mp.containsKey(digitsToInteger))
            {
                temp = mp.get(digitsToInteger);
                //tempString = Object.toString(temp);
                
                possibleLetters.add(temp);   //only need to store the String here since should not be indexing again?
                System.out.println("Following at index " + i + ":" + "("+str.charAt(i)+")"+ "  has been filled with temp: " + temp);
            }
            
            if (i==1)
            {
                possibleMappingSecond = temp;
            }
            
            //need to get the digits out, and search the map for the key  and get the value
            
            //ALSO NEED TO EXAMINE THIS AGAIN TO GET THE LETTERS
            
            int digitToInteger = Character.getNumericValue(str.charAt(i));
            
            if (mp.containsKey(digitToInteger))
            {
                temp = mp.get(digitToInteger);
                //tempString = Object.toString(temp);
                
                possibleLetters.add(temp);   //only need to store the String here since should not be indexing again?
                System.out.println("Following at index " + i + ":" + "("+str.charAt(i)+")"+ "  has been filled with temp: " + temp);
            }
            
            
            //reason for this section is that once it starts generating the 
            //selection, it will know that the first two digits (12) can be accepted
            //so that encoding starts with A=1 or  L=12
            
            if (i==0)
            {
                //temp is the letter from encoded number => LETTER
                //THIS IS CORRECT
                possibleMappingFirst = temp;
                
            }
        }
        
        //in practice we can complete same process for the last mapping
        //for instance if the String ends with   123   ,  last mapping could be  C=3  or W=23
        //but it would also mean that in random number selection it would need to process
        //last encoding ahead of the sequence...
        
        
        if (str.length()%2==1)
        {
            rMin=(str.length()+1)/2;
        }
        else
        {
            rMin=str.length()/2;
        }
        
        
        if (rMax>=rLimit)
        {
            System.out.println("rMax " + " will affect recursion");
        }
    
        System.out.println("***PERMUTATIONS***(WITH REPLACEMENT)");
        System.out.println("P^R(" + n+","+r+") = " + "Math.pow(n,r)");
        
        System.out.println("**********THE ENCODED MESSAGE TRANSLATION***********");
        System.out.println("This is encoded message: " + str);
        System.out.println("This is rMin: " + rMin);
        System.out.println("This is rMax: " + rMax);
        
        Iterator<String> iterator = possibleLetters.iterator();
        
        while (iterator.hasNext()) 
        {
        //Integer key = iterator.next();
        //System.out.println(iterator.next());
        possibleLettersMap.put(index,iterator.next());
        index++;
        }
        
        
        
        for (int i=rMin; i<=rMax; i++)
        {
             //in permutations right now,
//permutations(n,i) deals with all arrangements of the posibleletters p(n,rmin)
//this is not relevant. We need permutations for arranging 1,2 in order to make the
//length of the encoded message.
//in this constructor, need to have new AmazonClass(length encoded message, int array[] {1,2})... This will be the Amazon steps....
            
        sc = new Encoding (Permutations (n,i), mp,possibleLettersMap, i, possibleMappingFirst,possibleMappingSecond, str, arrangements);
        }
    }
        
        public static long Permutations (int n, int i)
        {
            System.out.println("\nP^R(" + n+","+i+") = " + "Math.pow("+n+","+i+")");
            
            System.out.println("Permutations: " + (long)Math.pow(n,i));
            return (long)Math.pow(n,i);
        }
}//end permutation class

