/*
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 NcR    combinations 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.*;
import java.math.RoundingMode;  
import java.text.DecimalFormat;  

class conversions
{
    public conversions (String currency, Double amount)
    {
        
    }
}


class largestNumber
{
    DecimalFormat format = new DecimalFormat("##. 00");
    
    String currencyExchange="";
    double exchange;
    double amount=50.0;
    double currency;
    String temp1;
    int k;
    String fullConversionsReverse;
    String exchangeArbitrageFlipped;
    String outcome="";
    Double ArbitrageChecker;
    String fullConversions;
    String exchangeArbitrage;
    String newCurrency;
    String initialAmount;
            
    
    public String currencyExchange(String exchangeCurrencies, String numberArray)
    {
        
        
         switch (exchangeCurrencies)
    {
        case "$=>£":
            exchange=0.83;
            currency = amount*exchange;
            break;
        case "$=>€":
            exchange=0.92;
            currency = amount*exchange;
            break;
        case "£=>$":
            exchange=1.30;
            currency = amount*exchange;
            break;
        case "£=>€":
            exchange=1.20;
            currency = amount*exchange;
            break;
        case "€=>$":
            exchange=1.09;
            currency = amount*exchange;
            break;
        case "€=>£":
            exchange=0.83;
            currency = amount*exchange;
            break;
    }
    
    amount=currency;
    temp1=numberArray;
    return numberArray+format.format(currency);
    
    
    
   //System.out.println("This is your new currency: " +numberArray+ currency);
   
    }
    
    enum currencies
    {
        Dollar("$"),
        Pound("£"),
        Euro("$");
        
        final String currency;
        
        currencies (String currency)
        {
            this.currency=currency;
            
        }
    }
    
    
    long combinations;
    int count;
    String temp;
    StringJoiner sj = new StringJoiner("=>");
    
    Set <String> s = new HashSet <>();   // this will store the  combinations 
    
    List <String> lst = new ArrayList<>(Arrays.asList("$","£","€"));  // this is list of numbers. In future, it will be chosen to use other data
    
    List <String> copy = new ArrayList<>(lst);  //keeps a copy    // this list keeps a copy since during program execution the top list is modified
    
    //need to keep copy of all currency symbol and amount
    // might be god idea to create a new instance
    List <conversions> ce = new ArrayList<>();
    
    public largestNumber(long combinations)
    {
        
        this.combinations=combinations;
        int randomNumber;    // random  number generated
        String numberArray="";     // value at index of randomNumber in the set
        String conversion;
        
        
        Random rand = new Random();  // generates random number
        
        System.out.println("Combinations: " + combinations +"\n");   //number combinations without replacement
       
        
        do
        {
            
            lst = new ArrayList<>(copy);     // each time it starts again, it has to get the original ArrayList back
            
            temp="";   // this is used concatenation of value before it is stored in the set....
            
            sj=new StringJoiner("=>");
            
            do
            {
                System.out.println("Size of list: " + lst.size());         //size list
                
                randomNumber = rand.nextInt(lst.size());                  //random number between  0 - (size list-1)
                System.out.println("random number: " + (randomNumber));
                
                temp=numberArray;   // this is needed so that the correct  currency1=>currency2  can be 
                // examined in switch statement
                
                numberArray = lst.get(randomNumber);   //gets number from the list
                
                conversion = temp + "=>" + numberArray;
                
                System.out.println("Conversion check:" + conversion);
                System.out.println("Value of temp: " + temp);
                
                
                if (temp!="")  // if currencies are not the same, it will start exchange rates
                {
                    System.out.println("***************");
                    
                    System.out.println("Current amount: " + temp + format.format(amount));
                    
                    System.out.println("This is your new currency: " + conversion + "   =>  " +  currencyExchange(conversion,numberArray));
                    
                    String reverseConversion = conversion.charAt(3) + "=>" + conversion.charAt(0);
                    System.out.println("This is reverse76: " + reverseConversion);
                    System.out.println("temp: " + temp);
                    System.out.println("This is conversion back to original to check arbitrage: " + reverseConversion + "   =>" + currencyExchange(reverseConversion,temp));
                    
                    
                }
                
                System.out.println("This is currency in list: " + numberArray);    //corresponding value in list
                
                sj.add(numberArray);
                //temp =  temp + Integer.toString(numberArray);    // concatenating the values
                
                
                
                
                lst.remove(randomNumber);     // this is important step.. it removes value at this index from list... enforce combination without replacment
                
                //System.out.println("value of i: " + i + "\n");
                
            } while (!lst.isEmpty());            // it will keep processing while this is true....   while list is not empty
            
            
            s.add(sj.toString());    // adding the value to the set.
            System.out.println("set size: " + s.size() +"\n");   //once this has incremented, the set has grown...
            System.out.println("This is saved in the set: " + sj.toString() +"\n");
            
            // Arbitrage check here
            
            
            
            
            fullConversions = sj.toString();
            
            
            System.out.println("******Arbitrage full check - full combination of currencies:   " + fullConversions);
            
            
            
            arbitrageCheckerTwoCurrencies();
            
            arbitrageCheckerFullCombination();
            
            
            
        count++;   // this is indication of a cycle.. It is aiming to be close to combinations...
        
        } while (s.size()<combinations);    // this is 1 less due to 0 indexing of the set. 
        
        
        System.out.println("Number cycles: " + count);
        
        System.out.println("Original list: " + copy.toString());  //original list outputted to the screen
        //System.out.println("highest is: " + checkMaximum());     // function call to check for maximum
        
    }
    
    public void arbitrageCheckerTwoCurrencies()
    {
        
            System.out.println("Number times conversion to check is: " + (copy.size()+1));
            
            k=4;
            exchangeArbitrage = fullConversions.substring(0,k);
            
            initialAmount =  exchangeArbitrage.charAt(0) + format.format(amount);
            
            System.out.println("Current amount (AMOUNT SHOULD RETURN HERE IF NO ARBITRAGE): " + initialAmount); 
             
            for (int i=0; i<copy.size()-1; i++)   // if three currencies, it will have to process conversion twice in forward direction
            {  
                
            newCurrency = Character.toString(exchangeArbitrage.charAt(3));
               
            //System.out.println("segment: " + exchangeArbitrage);    
                
                System.out.println("This is your new currency: " + exchangeArbitrage + "   =>  " +  currencyExchange(exchangeArbitrage, newCurrency));
                
            exchangeArbitrage = fullConversions.substring(k-1);
            
            }
        
    }
    
    public void arbitrageCheckerFullCombination()
    {
        // This now has to go back to original state
            // issue is creating the substring in reverse and directions of => will be flipped
            //easiest starting point is just reversing the string with StringBuilder
            
            
            StringBuilder sb = new StringBuilder(fullConversions);
            fullConversionsReverse = sb.reverse().toString();
            System.out.println("Reverse conversion****: " + fullConversionsReverse);
            
            k=4;
            exchangeArbitrage = fullConversionsReverse.substring(0,k);  
            
            for (int i=0; i<copy.size()-1; i++)   // if three currencies, it will have to process conversion twice in forward direction
            {  
                
            newCurrency = Character.toString(exchangeArbitrage.charAt(3));  // this is correct
            exchangeArbitrageFlipped = exchangeArbitrage.charAt(0) + "=>" +  newCurrency;
            
            //System.out.println("segment'678 : " + fullConversionsReverse.charAt(0)+"=>"+fullConversionsReverse.charAt(3));    
             System.out.println("segment'678 : " + exchangeArbitrageFlipped);
             
            outcome =  currencyExchange(exchangeArbitrageFlipped, newCurrency);
            System.out.println("This is your new currency in reverse: " + exchangeArbitrageFlipped + "   =>  " + outcome );
                
            exchangeArbitrage = fullConversionsReverse.substring(k-1);
            
            //System.out.println("Revised string!!!!!: " + exchangeArbitrage);
            }
            
            ArbitrageChecker = Double.parseDouble(outcome.substring(1)) - Double.parseDouble(initialAmount.substring(1));
            
            if (ArbitrageChecker >0)
            {
                System.out.println("- - - Arbitrage: - - - " + outcome.charAt(0) + format.format(ArbitrageChecker));
            }
            
        
    }
    
    
    
    public int checkMaximum()
    {
        int highest=0;
        
        for (String m: s)    // checks each string in the set
        {
            if (Integer.valueOf(m)>highest)   // greater than initial 0.. Strings are converted back to //integer
            {
                highest=Integer.valueOf(m);      // it will store value
            }
            
        }
        
        return highest;
    }
}


public class Combination
{
    public static void main(String[] args) {
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        int originalNumber=3;
        int n=originalNumber;
        int r =3;
        Map <Integer, Long> m = new HashMap<>();
        System.out.println("***COMBINATIONS***  (WITHOUT REPLACEMENT)");
        System.out.println("P(" + n+","+r+") = " + n+"!" + " / " + "("+r+"!"+"("+n+"-"+r+")!)");
        
        largestNumber ln = new largestNumber(Combinations (n,r,originalNumber, m));
        
    }
    
    public static long Combinations (int n,  int r, int originalNumber, Map factorialResults)
    {
        
        // n are objects
        // r is sample
       
       /*
       ***CALCULATION***
        P(n,r) =   n! /  (r!(n−r)!)
        
        */
        
        long result=0;
        int denominator1;
        int denominator2;
        
        if (n>=1)
        {
            // EXAMPLE
            
            // P (5,6)   =   6* 5* 4 * 3 * 2 * 1   /   6! (6-5)!     =    720 / (5! * 1!) =  120 / 5*4*3*2*1  * 1  = 720 / 120 = 6
        
        result = (n* (Combinations (n-1, r,originalNumber, factorialResults)));   // this completes factorial for numerator
        
        factorialResults.put(n,result);   //result stored in the Map
        //System.out.println("getting result back out: " + factorialResults.get(n));
        
        
        if (n==originalNumber)   // this will occur once
        {
            
            denominator1 = originalNumber-r;        // originalNumber required since n has reduced as part of the recursive calls
           
            denominator2 = r;                       // r sample size has not changed
           
            
        // this is using the Java Memoization technique to ensure the factorial outcome is not calculated again, to save program cycles.
        // since the returns are done in reverse order.... n = 1 is processed first and n=6 last... Hence in practice
        // there will be entry in Map for all factorials, ready for the denominator..
        
        
            if (factorialResults.containsKey(denominator1) && factorialResults.containsKey(denominator2))
            
            {
                //System.out.println("here");
                //System.out.println("This is exact value of factorial  " + (denominator1) + " : " +  factorialResults.get(denominator1));
                //System.out.println("This is exact value of factorial  " + (denominator2) + " : " +  factorialResults.get(denominator2));
               
                return result / ((long) factorialResults.get(denominator1) * (long)factorialResults.get(denominator2));
            }
           
        }
           
            return result;
            
        }
          return 1;    // it should not reach here  
        }
        
    }
