/*
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 combinations (with replacement) calculator.
// It has used techniques I learnt including recursion and also memoization to speed up execution.
// I will incorporate this into Java applications I created previously..


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

class Staircase 
{
    int N = 45;     // this can be changed by end user....
    
    Set <Integer> s = new HashSet<>();
    int[] X;
    int p;
    int total;
    
     Random rand = new Random();
    
      
    // need to process whole thing until the set.size < combinations
    // 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
    //need
    
    
    public Staircase(long combinations, int[] X)
    {
        this.X=X;
         
        System.out.println("Combinations: " + combinations);
        
        StringJoiner sj = new StringJoiner(",");
        
        int currentSetSize;
        int newSetSize;
        
        
        do
        {
            total=0;
            sj = new StringJoiner();
            
        do
        {
           p= rand.nextInt(X.length);  // if length 3, it will give numbers 0-2...
           sj.add(X[p].toString());
           total=total+p;
           currentSetSize=set.size();
           
           if (total=N)
           {
           set.add(sj.toString());
           }
           
           newSetSize = set.size();
        
           if (newSize>currentSize)
           {
               System.out.println("This has been added into the set: " + sj.toString());
           }
           
        }while (total<N);  
        
        
        } while (set.size()<combinations);
        
          
           
        
        /*
        do
        {
            do{
                
            }
            
        }
        */
        
    }
}


public class Combination
{
    public static void main(String[] args) 
    {
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        // this is going to be slight challenge...
        int originalNumber=0;   // for moment it is declared a 0 to keep next line content... This variable is based on the size of n in outer for loop...
        int n=originalNumber;   // this can stay here.... 
        int r;   // this does not need be in for loop
        
        int [] X = new int []{1,2,3};  //just to keep this loop flowing, I will complete exercise inline with 3 values in k..

        // Require a nested loop as follows and execution has to run separately

        //These are all scenarios
        //n=1 to  n< X.length()
        //r=1 to r<=n

        //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
        
        for (n=0; n<=X.length; n++)    // this should go from 1 to 3
        {
            System.out.println("VALUE N: " + n);
            
            originalNumber=n;  // this will remain constant during a full iteration..
            
             m= new HashMap<>();  //new instance is required to start process again...
             
             for (r=0; r<=n+5; r++)  // for moment r is set 5 more than objects available.. This bit can be experimented later..
             {
                 System.out.println("***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)+")!");
                 
                 
                 Staircase sc = new Staircase (Combinations (n,r,originalNumber, m), X);
                 //System.out.println(Combinations (n,r,originalNumber, m));
             }
        }
        
    }

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