//*** 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... And to ensure operation starts correctly
 I will ensure it can obtain the correct possibleMappingSecond or possibleMappingFirst.
 Everything else will be on a random and explorative basis..
 My preferred choice

 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.

//

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++)
            {

                //NEED TO SORT THIS
                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();

                }

                if (sj1.length()>=r)
                {
                    //System.out.println("Will attempt to add String for analysis: " + sj1.toString());
                    st.add(sj1.toString());
                    //.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

                //-----CHATGPT--------
                // temp1 = rand.nextInt(possibleLettersMap.size()) + 1;      //this should get random number between 0 and the set size  // [COMMENTED OUT: off-by-one/truncation]
                temp1 = rand.nextInt(possibleLettersMap.size()); // 0..size-1 (possibleLettersMap keys start at 0)
                                mapping = possibleLettersMap.get(temp1); // pick eligible letter (A/B/C/... derived from input)
                //--------END CHATGPT-----------

                //System.out.println("this is random number: " + temp1);
                //System.out.println("mapping: " + )

                //this is done so that it gets random generated letter
                //and it will later check to see if it is eligible start letter

                // [ORIGINAL BLOCK COMMENTED OUT BY CHATGPT: start-route handling + mp.get(temp1) mismatch]
                //                 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;
                //                 }
                //-----CHATGPT--------
                // Equivalent of the q==0 block (but correct): start mapping comes from the actual first digit of the encoding.
                if (q==0)
                {
                    firstScenarioMapping = mp.get(Integer.valueOf(originalString.substring(0, 1)));
                    mapping = firstScenarioMapping;
                }
                //--------END CHATGPT-----------

                // 
                //                 //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);
                // 
                //                     secondScenarioMapping = mp.get(secondScenarioMappingInteger);
                //                     mapping=secondScenarioMapping;
                // 
                //                     //System.out.println("Q is 1: " + secondScenarioMapping);
                //                 }
                //-----CHATGPT--------
                // Equivalent of the q==1 (two-digit) block (but correct): mapping comes from the actual first two digits.
                if (q==1)
                {
                    // If the first two digits are > 26 (e.g. 43), mp.get(...) returns null. That's OK; validation below handles it.
                    secondScenarioMapping = mp.get(Integer.valueOf(originalString.substring(0, 2)));
                    mapping = secondScenarioMapping;
                }
                //--------END CHATGPT-----------

                // 
                //                 //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)
                // 
                //                         //***new comments
                //                         //can perhaps expand this since
                //                         //firstScenarioMapping could also equal to possibleMappingSecond
                //                         //secondScenarioMapping could equal possibleMappingFirst
                // 
                // 
                //                         if (!(firstScenarioMapping.equals(possibleMappingFirst)  || secondScenarioMapping.equals(possibleMappingSecond)))
                //                         {
                //                             //System.out.println("firstScenarioMapping.equals(possibleMappingFirst)  acceptable and none null");
                //                             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
                //                         {
                //                             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
                //-----CHATGPT--------
                // Same intent as your original q==1 try/catch block, but with the corrected mappings wired in.
                if (q==1)
                {
                    try
                    {
                        if (!(firstScenarioMapping.equals(possibleMappingFirst) || secondScenarioMapping.equals(possibleMappingSecond)))
                        {
                            invalidIndex=true;
                            break;
                        }
                        else
                        {
                            if (firstScenarioMapping.equals(possibleMappingFirst))
                            {
                                sj.add(firstScenarioMapping);
                                currentSetSize=st.size();
                            }
                            if (secondScenarioMapping.equals(possibleMappingSecond))
                            {
                                sj1.add(secondScenarioMapping);
                            }
                        }
                    }
                    catch (NullPointerException e)
                    {
                        // Usually means secondScenarioMapping was null; still allow the 1-digit start route if valid.
                        if (firstScenarioMapping!=null && firstScenarioMapping.equals(possibleMappingFirst))
                        {
                            sj.add(firstScenarioMapping);
                            currentSetSize=st.size();
                        }
                        else
                        {
                            invalidIndex=true;
                            break;
                        }
                    }
                }
                //--------END CHATGPT-----------

                // [ORIGINAL BLOCK COMMENTED OUT BY CHATGPT: end]
                //-----CHATGPT--------
                // Unified, correct start + append logic:
                // - Route 1 (sj) always starts with possibleMappingFirst (single-digit start, e.g., 'B' for leading '2')
                // - Route 2 (sj1) always starts with possibleMappingSecond (two-digit start, e.g., 'X' for leading '24')
                // - Subsequent letters are drawn from possibleLettersMap (mapping variable) and appended until length r
                if (q==0)
                {
                    if (sj.length()==0 && possibleMappingFirst!=null && !possibleMappingFirst.isEmpty())
                    {
                        sj.add(possibleMappingFirst);
                    }
                    if (sj1.length()==0 && possibleMappingSecond!=null && !possibleMappingSecond.isEmpty())
                    {
                        sj1.add(possibleMappingSecond);
                    }
                    continue; // go to next letter position
                }

                // For q>=1, append random eligible letters (mapping) until we hit length r
                if (!invalidIndex)
                {
                    if (sj.length()!=0 && sj.length()<r)
                    {
                        sj.add(mapping);
                    }
                    if (sj1.length()!=0 && sj1.length()<r)
                    {
                        sj1.add(mapping);
                    }
                }
                //--------END CHATGPT-----------

                //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)
                    {
                        //-----CHATGPT--------
                        // sj.add(mp.get(temp1));  // [COMMENTED OUT: now appended earlier via 'mapping' from possibleLettersMap]
                        //--------END CHATGPT-----------
                        //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.....
                        //-----CHATGPT--------
                        // sj1.add(mp.get(temp1)); // [COMMENTED OUT: now appended earlier via 'mapping' from possibleLettersMap]
                        //--------END CHATGPT-----------
                    }

                    //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*10);

        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");

        //-----CHATGPT--------
        // Printing the entire candidate set can explode output.
        // Enable only when you explicitly want to inspect candidate generation.
        if (Permutation.PRINT_ALL_CANDIDATES)
        //--------END CHATGPT-----------
        {
            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")
            {
                //-----CHATGPT--------
                // Optional: print each candidate before it is checked.
                if (Permutation.PRINT_ALL_CANDIDATES)
                //--------END CHATGPT-----------
                {
                    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);
                }
            }
        }

        //-----CHATGPT--------
        // By default we only print VALID translations.
        // (Invalid candidates can be extremely noisy and make log files huge.)
        boolean isValid = conversion.equals(originalString);

        if (isValid)
        {
            //-----CHATGPT--------
            // Print each valid decoding only once (random search may rediscover the same valid result).
            if (Permutation.UNIQUE_VALID_PRINTED.add(storedEntry))
            {
                System.out.println("-------------------------------This is a valid translation: "
                        + storedEntry + " for encoding: " + originalString
                        + "----------------------------------\n");
            }

            // Still record it (set will dedupe).
            Permutation.RANDOM_VALID_DECODINGS.add(storedEntry);
            //--------END CHATGPT-----------
        }
        else
        {
            if (Permutation.PRINT_INVALID)
            {
                System.out.println("*******CONVERSION=> " + storedEntry
                        + "    ORIGINAL => " + originalString
                        + "    Mapped =>  " + conversion);
                System.out.println("INVALID\n");
            }
        }
        //--------END CHATGPT-----------

        conversion="";

    }

} //end of class.

public class Permutation
{
    //-----CHATGPT--------
    // Stores all VALID decodings that your random/explorative runs discover.
    // Used to decide whether the deterministic "safety net" needs to run.
    public static final Set<String> RANDOM_VALID_DECODINGS = new LinkedHashSet<>();

    // Ensures each valid decoding is printed only once (prevents duplicates in console output).
    public static final Set<String> UNIQUE_VALID_PRINTED = new LinkedHashSet<>();

    // Output controls (keeps console/log files manageable).
    // Set PRINT_INVALID to true if you want to see rejected candidates.
    public static final boolean PRINT_INVALID = false;
    // Set PRINT_ALL_CANDIDATES to true if you want to print every candidate before checking.
    public static final boolean PRINT_ALL_CANDIDATES = false;
    //--------END CHATGPT-----------

    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 = "2432589223";   //this is encoded message I have referenced in my logic documentation
        String str = "2431221";
        System.out.println("THIS IS PRESENTED STRING: " + str);
        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);
        }


        //-----CHATGPT--------
        // Deterministic (guaranteed) decoding "safety net":
        // Your main design remains RANDOM/EXPLORATIVE (generate candidates -> verify).
        // Random search is not guaranteed to find every valid decoding in a finite number of tries,
        // so we only run the deterministic solver if randomness missed something.
        int expectedTotal = countDecodingsFast(str);
        int randomFound   = RANDOM_VALID_DECODINGS.size();

        System.out.println();
        //-----CHATGPT--------
        System.out.println();
        System.out.println("RANDOM UNIQUE VALID DECODINGS (" + randomFound + " / " + expectedTotal + "): " + RANDOM_VALID_DECODINGS);

        if (randomFound < expectedTotal)
        {
            Set<String> all = decodeAllDeterministic(str, mp);

            Set<String> missed = new LinkedHashSet<>(all);
            missed.removeAll(RANDOM_VALID_DECODINGS);

            System.out.println("MISSING DECODINGS (deterministic safety net): " + missed);

            // Optionally print each missed decoding using the same "valid translation" style.
            for (String s : missed)
            {
                if (Permutation.UNIQUE_VALID_PRINTED.add(s))
                {
                    System.out.println("-------------------------------This is a valid translation: "
                            + s + " for encoding: " + str
                            + "----------------------------------\n");
                }
            }

            System.out.println("ALL DETERMINISTIC DECODINGS (" + all.size() + "): " + all);
            System.out.println();
        }
        //--------END CHATGPT-----------

        else
        {
            System.out.println();
            System.out.println("No deterministic run needed (random matched expected total).");
            System.out.println();
        }
        //--------END CHATGPT-----------
    }

    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);
    }


    //-----CHATGPT--------
    // Fast count of how many valid decodings exist (no randomness).
    // This tells us whether the random approach found everything.
    // Rules: 1..26 are valid; leading zero chunks are invalid.
    static int countDecodingsFast(String digits)
    {
        int n = digits.length();
        if (n == 0)
        {
            return 0;
        }

        int[] dp = new int[n + 1];
        dp[n] = 1; // one way to decode an empty suffix

        for (int i = n - 1; i >= 0; i--)
        {
            char ch = digits.charAt(i);

            // A chunk cannot start with '0'
            if (ch == '0')
            {
                dp[i] = 0;
                continue;
            }

            // Take 1 digit
            dp[i] = dp[i + 1];

            // Take 2 digits if possible and <= 26
            if (i + 1 < n)
            {
                int val = (digits.charAt(i) - '0') * 10 + (digits.charAt(i + 1) - '0');
                if (val <= 26)
                {
                    dp[i] += dp[i + 2];
                }
            }
        }

        return dp[0];
    }
//--------END CHATGPT-----------

//-----CHATGPT--------
    // Guaranteed decoding using your \"Amazon steps\" idea:
    // At each position, take a step of 1 digit or 2 digits.
    // If the chunk is 1..26, map it to a letter and continue.
    // If a chunk is >26 or starts with 0, discard that route immediately.
    static Set<String> decodeAllDeterministic(String digits, Map<Integer,String> mp)
    {
        Set<String> out = new LinkedHashSet<>();
        dfsDecode(digits, 0, new StringBuilder(), mp, out);
        return out;
    }

    static void dfsDecode(String digits, int pos, StringBuilder sb,
                          Map<Integer,String> mp, Set<String> out)
    {
        if (pos == digits.length())
        {
            out.add(sb.toString());
            return;
        }

        // Step sizes (Amazon stairs): 1 or 2 digits
        for (int step = 1; step <= 2; step++)
        {
            int end = pos + step;
            if (end > digits.length())
            {
                continue;
            }

            String chunk = digits.substring(pos, end);

            // Reject leading-zero chunks like "0" or "06"
            if (chunk.charAt(0) == '0')
            {
                continue;
            }

            int val = Integer.parseInt(chunk);
            if (val < 1 || val > 26)
            {
                continue;
            }

            String letter = mp.get(val);
            if (letter == null)
            {
                continue;
            }

            int oldLen = sb.length();
            sb.append(letter);

            dfsDecode(digits, end, sb, mp, out);

            sb.setLength(oldLen);
        }
    }
    //--------END CHATGPT-----------

}//end permutation class


class Staircase
{
    int N; // this can be changed by end user, size of the staircase
    int[] X; // this is the array containing the steps
    int p; // this is random number generated
    int total; // this is running total of the steps
    int cycles=0; // this is number of cycles completed for each
    Set<String> s; // this will contain the combinations for achieving total N
    Random rand = new Random(); // object created of type Random.
    // there is no sense of X getting smaller since its with replacement....
    // need to only add the stringJoiner (converted into string) into the set if the total is equal to N
    // if total goes over, it will continue adding until all cycles for C^R (n,r) has finished
    // Combinations is total of combinations for C^R(n,r)
    // r is the sample size... This can exceed n.
    int newSetSize; // holds size of set after entry added

    public Staircase(long combinations, int[] X, int r, Set<String> s, int lengthEncodedMessage)
    {
        this .N=lengthEncodedMessage;
        this.X=X;
        this.s=s;
        System.out.println("Combinations: " + combinations);
        StringJoiner sj = new StringJoiner(","); // creating StringJoiner
        int currentSetSize=0; // holds size of set before entry added

        int count;
        int q; //used in the for loop to iterate through r
        int steps; // it will hold value of step generated randomly from r

        do
        {
            count=1;

            //sets count back to 1 when it has finished. This is visual check to ensure that r is not exceeded

            for (q=0; q<r;q++) // processing up to r only since this is the sample size
            {
                p= rand.nextInt(X.length); // if length 3, it will give index numbers 0-2... This is suitable
                steps = X[p]; // step value
                System.out.println("\nrandom step is: " + steps);
                sj.add(Integer.toString(X[p])); //adds the step into StringJoiner
                currentSetSize=s.size(); //current size of set
                total=total+steps; //keeps running total
                System.out.println("This is performing: " + (q+1) +" of " + r + "(r)");

                //reminding end user value N and running total
                System.out.println("TOTAL HERE: " + total + "    N Value is: " + N);
                //if (total==N /*&& count<=r*/)

                if (total==N) // running total equals to N (steps in staircase)
                {
                    System.out.println("This is stringjoiner right now: " + sj.toString());
                    System.out.println("Count:" + count);
                    System.out.println("current:" + currentSetSize);
                    System.out.println("TOTAL: " + total);
                    System.out.println("VALUE OF N: " + N);
                    System.out.println("COUNT: " + count);
                    System.out.println("r: " + r);
                    s.add(sj.toString()); // it will only add it if the conditions are met above (size of r and Total=N)

                    count++;
                }

                if (q==r-1) // if its on the last loop..
                {
                    sj = new StringJoiner(",");
                    total=0;

                }
            }

            if (total>N)
            {
                total=0; //resets total
                sj = new StringJoiner(","); //creates new instance, resets contents...
            }

            newSetSize = s.size(); // new set size
            System.out.println("new size: "+ newSetSize);

            if (newSetSize>currentSetSize) //if larger, i.e a new combination, it will inform end user..
            {
                System.out.println("This has been added into the set:");

            }
            cycles++; //increases cycles
            System.out.println("Number of cycles: " + cycles + "\n");
            // can not set this to a certain set size... since it is giving total combinations (with replacement) and not
            // related to placing items in certain order in which once combinations are known.
            // There are most likely distributions in statistics which might assist with the cycles...
            // This can only be placed in speculatively like throwing dice (a fixed time) and getting total....
            //the safest option for the maximum iterations is combinations x 10.
            //but in reality there is no guarantee that it would have covered all combinations at least once in that duration...

        }while (cycles<combinations*10);

        // this is now getting the String in the set...

        for (String g: s)
        {
            System.out.println(g.toString());
        }
    }  //end method

    int counter;
    String[] arrangements = new String[newSetSize];
    
    public Set<String> arrangements()
    {
        return s;
    }
}


class Combination
{
    Staircase sc; //object of type Staircase

    public Combination (int[] lengthLetters, int lengthEncodedMessage)
    {
        System.out.println("IN HERE-----------------------------------------------------------------");
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        int originalNumber=0; // for moment it is declared a 0 to keep next line content...

        int n=originalNumber;

        int r; // this does not need be in for loop. user defined

        Set <String> s = new HashSet<>(); // it will contain all combinations...

        //int [] X = new int []{5,6,7}; //user defined
        int [] X = lengthLetters;

        int minimum; // the smallest value in X. This will assist with generating r value...

        int maxRValue; //upper limit for r in the loop to process 0<r<=maxRValue+1

        //need to understand that r can be greater than n in replacement.......
        //r is taken from n objects
        // in this example, r can get larger....

        Map <Integer, Long> m; // this can stay outside loop since its not impacted by size of n and r

        n=X.length; //length of the array X of steps

        System.out.println("Staircase size is:  " + lengthEncodedMessage);
        System.out.println("Steps are: " + Arrays.toString(X));

        originalNumber=n; // this will remain constant during a full iteration..

        m= new HashMap<>(); //new instance is required to start process again...
        //r can be estimated as follows based on:
        //Maximum length of a combination is approximately (N/ minimum value in X)

        minimum=X[0];

        for (int i=0; i<X.length;i++)
        {
            if (X[i]<minimum && X[i]!=0)
            {
                minimum=X[i];
            }
        }
        maxRValue=(lengthEncodedMessage)/minimum;
        //informs end user of constraints...
        System.out.println("Maximum value of r: " + (maxRValue+ 1) + " has been set using minimum value in set: " + minimum + " , which should be in proximity to N(" + lengthEncodedMessage + ")");
        //additional 1 added due to potential rounding errors

        for (r=0; r<=maxRValue+1; r++)
        {
            System.out.println("\n***COMBINATIONS*** (WITH REPLACEMENT)");

            System.out.println("C^R(n + r) = " + "(n+r-1)! / r!(n-1)!");

            System.out.println("C^R(" + n+","+r+") = " + (n+r-1)+ "!" + " / " + r+"!"+"("+(n-1)+")!");

            //creates instance of Staircase and main execution of code...
            sc = new Staircase (Combinations (n,r,originalNumber, m), X, r,s,lengthEncodedMessage);
        }
    }

    public static long Combinations (int n, int r, int originalNumber, Map factorialResults)
    {
        long result=0;
        int denominator1; //denominator split two parts since there are two factorial calculations
        int denominator2; //denominator split two parts since there are two factorial calculations
        int Numerator=n+r-1; // Numerator
        int zero=0;
        long zeroFactorial = 1;
        // if no sample or objects, there are no outcomes...
        
        if (originalNumber==0 && r==0)
        {
            System.out.println("n and r can not both be equal to zero");
            //System.exit(0);
            return 0;
        }
        //this situation would occur if n is 0 only and r is any positive number accept 0 (if statement above)
        //for instance (C^R (n,r)) = (0,3) 0+3-1 = 2 2<3

        if (originalNumber==0 && originalNumber+r-1<r)
        {
            System.out.println("n+r-1 must be > or = to r");
            //System.exit(0);
            return 0;
        }

        if (Numerator>=1)
        {
            result = ((n+r-1)* (Combinations (n-1, r,originalNumber, factorialResults)));
            // this completes factorial for numerator
            factorialResults.put(Numerator,result); //result stored in the Map
            //factorialResults.put(n-1,result); //result stored in the Map
            //System.out.println("getting result back out numerator: " + (Numerator) + " " + factorialResults.get(n+r-1));

            if (n==originalNumber) // this will occur once
            {
                denominator1 = r;
                denominator2 = originalNumber-1;
                factorialResults.put(zero,zeroFactorial); //0! is equal to 1

                if (factorialResults.containsKey(denominator1) && factorialResults.containsKey(denominator2))
                {
                    long returnValue = result / ((long)factorialResults.get(denominator1) *(long)factorialResults.get(denominator2));
                    return returnValue;
                }
            }
            return result;
        }
        return 1; // it will reach here only when condition not met (Numerator>=1)
    }

}

