/*
Online Java - IDE, Code Editor, Compiler

Online Java is a quick and easy tool that helps you to build, compile, test your programs online.
*/

import java.math.*;
import java.util.*;
import java.math.RoundingMode;
import java.text.DecimalFormat;

class Conversions
{
    public Conversions ()
    {
        
    }
}

class ExchangeCombinations
{
    DecimalFormat format = new DecimalFormat("##0. 00");  //format to 2dp..
    
    String currencyExchange="";
    double exchangeRate;        
    double amount=50.0; 
    double currency;
    int k; 
    String fullConversionsReverse; 
    String exchangeArbitrageFlipped; 
    String outcome="";  
    Double ArbitrageChecker;
    String fullConversions; 
    String exchangeArbitrage;
    String newCurrency;
    String initialAmount;
    long combinations;
    int count; 
    String temp;
    StringJoiner sj = new StringJoiner("=>");
    Set <String> s = new HashSet <>();
    
    int startPos; 
    int endPos;
    
    List <String> lst = new ArrayList<>(Arrays.asList("$","£","€","¥","F"));
    List <String> copy = new ArrayList<>(lst);
    
    StringBuilder sb;
    int currentSetSize;
    boolean exitCondition;
    int n;
    int r;
    int differenceObjectsAndTotalCurrenciesAvailable;
    int randomNumber;
    String currencySymbol="";
    String conversion;
    int totalCurrenciesAvailable;
    
    Random rand = new Random();
    
    public String currencyExchange(String exchangeCurrencies, String currencySymbol)
    {
        switch (exchangeCurrencies)
        {
            case "F=>¥":
            exchangeRate=174.59;
            currency = amount*exchangeRate;
            break;
            
            case "F=>£":
            exchangeRate=0.89;
            currency = amount*exchangeRate;
            break;
            
            case "F=>$":
            exchangeRate=1.16;
            currency = amount*exchangeRate;
            break;
            
            case "F=>€":
            exchangeRate=1.07;
            currency = amount*exchangeRate;
            break;
        
            case "¥=>£":
            exchangeRate=0.0051;
            currency = amount*exchangeRate;
            break;
        
            case "¥=>$":
            exchangeRate=0.0067;
            currency = amount*exchangeRate;
            break;
            
            case "¥=>€":
            exchangeRate=0.0061;
            currency = amount*exchangeRate;
            break;
            
            case "¥=>F":
            exchangeRate=0.0057;
            currency = amount*exchangeRate;
            break;
        
            case "$=>£":
            exchangeRate=0.83;
            currency = amount*exchangeRate;
            break;
            
            case "$=>$":
            exchangeRate=0.92;
            currency = amount*exchangeRate;
            break;
            
            case "$=>€":
            exchangeRate=149.97;
            currency = amount*exchangeRate;
            break;
            
            case "$=>F":
            exchangeRate=0.87;
            currency = amount*exchangeRate;
            break;
            
            case "£=>$":
            exchangeRate=1.30;
            currency = amount*exchangeRate;
            break;
            
            case "£=>€":
            exchangeRate=1.20;
            currency = amount*exchangeRate;
            break;
            
            case "£=>¥":
            exchangeRate=195.30;
            currency = amount*exchangeRate;
            break;
        
            case "£=>F":
            exchangeRate=1.12;
            currency = amount*exchangeRate;
            break;
            
            case "€=>$":
            exchangeRate=1.09;
            currency = amount*exchangeRate;
            break;
            
            case "€=>£":
            exchangeRate=0.83;
            currency = amount*exchangeRate;
            break;
            
            case "€=>¥":
            exchangeRate=162.65;
            currency = amount*exchangeRate;
            break;
            
            case "€=>F":
            exchangeRate=0.93;
            currency = amount*exchangeRate;
            break;
        }
    
        amount=currency;

        return currencySymbol+format.format(currency);  // new currency
    }
    
    public String checkSelection(int n, int r)
    {
        if (r==1 || n==1)
        {
            return "Sample size (r) or Objects (n) is too small";
        }
        
        if (r>n)
        {
            return "Sample size too big";
        }
        
        if (r<n && r==1)
        {
            return "Sample size too small";
        }

	    if (r >totalCurrenciesAvailable || n> totalCurrenciesAvailable)
        {
            return "Sample size or objects selected is larger than total currencies available";
        }

        return "";
    }
    
    public ExchangeCombinations(long combinations, int n, int r)
    {
        this.combinations=combinations;
        
        this.n=n;
        this.r=r;
        
        totalCurrenciesAvailable=lst.size();
        
        System.out.println("Combinations: " + combinations +"\n");
        System.out.println("Sample size (r): " + r);
        System.out.println("Objects: (n): " + n);
        
        if (checkSelection(n, r)!="")
        {
            System.out.println(checkSelection(n, r));
            System.exit(0);
        }
        
        do
        {
            System.out.println("\n");
            
            lst = new ArrayList<>(copy);
            
            temp="";
            
            sj=new StringJoiner("=>");
            
            do
            {
                exitCondition = !lst.isEmpty();
                
                if (lst.size()==0)
                {
                    break;
                }
                
                randomNumber = rand.nextInt(lst.size());
                
                temp=currencySymbol;
                
                currencySymbol = lst.get(randomNumber);
                
                conversion = temp + "=>" + currencySymbol;
            
                if (temp!="")
                {
                    randomTwoCurrencyChecker();
                }
                    sj.add(currencySymbol);
                    
                    lst.remove(randomNumber);
                
                    if (n>r)
                    {
                        if (n<totalCurrenciesAvailable)
                        {
                            differenceObjectsAndTotalCurrenciesAvailable = totalCurrenciesAvailable-n;
                            
                            if ((lst.size()-differenceObjectsAndTotalCurrenciesAvailable>(n-r)))   
                            {
                                exitCondition =  true;
                            }
                            else
                            {
                                break;
                            }
                        }
                        
                        if (lst.size()>n-r)
                        {
                            exitCondition =  true;
                        }
                        else
                        {
                            break;
                        }
                    }
           
                    if (n==r)
                    {
                        differenceObjectsAndTotalCurrenciesAvailable = totalCurrenciesAvailable-n;
                
                        if (lst.size()>differenceObjectsAndTotalCurrenciesAvailable)
                        {
                            exitCondition =  true;
                        }
                        else
                        {
                            break;
                        }
                    }
                }while (exitCondition);
            
                currentSetSize= s.size();
                System.out.println("\n\n************This is current set size: " + currentSetSize+"***********************************");
            
                s.add(sj.toString());
                
                System.out.println("************This is new set size: " + s.size()+"***********************************");
                System.out.println("This is saved in the set: " + sj.toString() +"\n");
                fullConversions = sj.toString();
            
                System.out.println("******Arbitrage full check - full combination of currencies:   " + fullConversions);
                
                if (s.size()==currentSetSize)
                {
                    System.out.println("*********NOT REQUIRED******************************");
                }
                else
                {
                    arbitrageCheckerFullCombinationForward();
                    arbitrageCheckerFullCombinationReverse();
                }
        
                count++;
                
            } while (s.size()<combinations);
        
        System.out.println("\nNumber cycles: " + count);
        System.out.println("Original list: " + copy.toString());
    }
    
    public void randomTwoCurrencyChecker()
    {
        System.out.println("***RANDOM TWO CURRENCY CHECKER************");
                    
        System.out.println("Current amount: " + temp + format.format(amount));
        System.out.println("This is your new currency: " + conversion + "   =>  " +  currencyExchange(conversion,currencySymbol));
        
        String reverseConversion = conversion.charAt(3) + "=>" + conversion.charAt(0);
    
        System.out.println("This is conversion back to original to check arbitrage: " + reverseConversion + "   =>" + currencyExchange(reverseConversion,temp));
        System.out.println("***********************");
    }
    
    public void arbitrageCheckerFullCombinationForward()
    {
        System.out.println("\n*****FULL FORWARD CONVERSION****");
        k=4;
        startPos=0;
        endPos = startPos+4;
        exchangeArbitrage = fullConversions.substring(0,endPos);
        amount=50;
        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<r; i++)
        {
            newCurrency = Character.toString(exchangeArbitrage.charAt(3));
            
            System.out.println("This is your new currency: " + exchangeArbitrage + "   =>  " +  currencyExchange(exchangeArbitrage, newCurrency));
            
            startPos=endPos-1;
            endPos=startPos+4;
            
            if (endPos>fullConversions.toString().length())
            {
                break;
            }
                  
            exchangeArbitrage = fullConversions.substring(startPos, endPos);
        }
        System.out.println("**************");
    }
    
    public void arbitrageCheckerFullCombinationReverse()
    {
        System.out.println("\n*****FULL REVERSE CONVERSION****");
            
        sb = new StringBuilder(fullConversions);   
        fullConversionsReverse = sb.reverse().toString();
        System.out.println("Reverse conversion****:                                        " + fullConversionsReverse);
        startPos=0;
        endPos = startPos+3;
        exchangeArbitrage = fullConversionsReverse.charAt(startPos) + "=>" + fullConversionsReverse.charAt(endPos);
            

        for (int i=0; i<r; i++) 
        {
            newCurrency = Character.toString(exchangeArbitrage.charAt(3));
            exchangeArbitrageFlipped = exchangeArbitrage.charAt(0) + "=>" +  newCurrency;
            
            outcome =  currencyExchange(exchangeArbitrageFlipped, newCurrency);
            System.out.println("This is your new currency in reverse: " + exchangeArbitrage + "   =>  " + outcome );
            
            startPos=endPos;
            endPos = startPos+3;
            
            if (endPos>fullConversionsReverse.length())
            {
                break;
            }
            
            exchangeArbitrage = fullConversionsReverse.charAt(startPos) + "=>" + fullConversionsReverse.charAt(endPos);
        }
            
        System.out.println("*********");
        checkForwardWithReverseConversion();
    }
    
    public void checkForwardWithReverseConversion()
    {
        System.out.println("\n**** FINAL CHECK - ARBITRAGE *******");
        ArbitrageChecker = Double.parseDouble(outcome.substring(1)) - Double.parseDouble(initialAmount.substring(1));
        
        if (ArbitrageChecker >0)
        {
            System.out.println("- - - Arbitrage: - - - " + outcome.charAt(0) + format.format(ArbitrageChecker) + "  (" + outcome + " - " + initialAmount+")");
        }
            
        else
        {
            System.out.println("- - - NO Arbitrage: - - - ");
        }
            
        System.out.println("***********************************************************************");
    }
}

public class Combination
{
    public static void main(String[] args) {
        System.out.println("Welcome to Online IDE!! Happy Coding :)");
        
        int originalNumber=5;
        int n=originalNumber;
        int r =3;
        Map <Integer, Long> m = new HashMap<>();
        System.out.println("***COMBINATIONS***  (WITHOUT REPLACEMENT)");
        System.out.println("C(n,r) = n! / (r!(nâˆ’r)!)");
        System.out.println("C(" + n+","+r+") = " + n+"!" + " / " + "("+r+"!"+"("+n+"-"+r+")!)");
        
        ExchangeCombinations ec = new ExchangeCombinations(Combinations (n,r,originalNumber, m), n,r);
    }
    
    public static long Combinations (int n,  int r, int originalNumber, Map factorialResults)
    {
        long result=0;
        int denominator1;
        int denominator2;
        
        if (n>=1)
        {
            result = (n* (Combinations (n-1, r,originalNumber, factorialResults)));
        
            factorialResults.put(n,result);
            
            if (n==originalNumber)
            {
                denominator1 = originalNumber-r;
                denominator2 = r; 
           
                if (factorialResults.containsKey(denominator1) && factorialResults.containsKey(denominator2))
                {
                    return result / ((long) factorialResults.get(denominator1) * (long)factorialResults.get(denominator2));
                }
           }
           
            return result;
        }
        
        return 1;
    }
}
