/******************************************************************************
  * Name: COS 126 Staff
  * NetID: cos126
  * Precept: P00
  * 
  * Fall 15 Programming Exam 2, part 1
  * Description: Object class to process finite formal languages,
  * supporting union, concatenation and n-closure.
  
*****************************************************************************/
public class Language
{
    private final SET<String> language; // Set of strings in the language
    
    // Create a new empty Language.
    public Language()
    {
        language = new SET<String>();  
    }
    
    // Create a new Language consisting of the single String s.
    public Language(String s)
    {
        language = new SET<String>();
        language.add(s);
    }
    
    // Add s to this language.
    public void add(String s)
    {
        language.add(s);
    }
    
    // Return the union of this Language and that Language.
    public Language union(Language that)
    {
        Language result = new Language();
        
        for (String s : this.language)
            result.add(s);
        for (String s : that.language)
            result.add(s);
        
        return result;
    }
    
    // Return the concatenation of this Language and that Language.
    public Language concatenate(Language that)
    {
        // Set up a new empty language for the result
        Language result = new Language();
        
        // What if one of the language sets is empty?
        // Just copy the other language (use union of other and the empty set)
        if (this.language.isEmpty()) {
            result = that.union(result);
            return result;
        }
        if (that.language.isEmpty()) {
            result = this.union(result);
            return result;
        }
        
        // concatenate each string in this set with each string in that set
        for (String s1 : this.language) {
            for (String s2 : that.language) {
                result.add(s1 + s2);
            }
        }
        return result;
    }
    
    // Return the n-closure of this Language.
    public Language closure(int n)
    {
        // Set up a new empty language for the result
        Language result = new Language();
        
        // concatenate n copies of language (empty set if n is zero)
        for (int i = 0; i < n; i++) {
            result = result.concatenate(this);
        }
        return result;
    }
    
    public String toString()
    {
        String result = " ";
        for (String s : language)
            result = result + s + " ";
        return result;
    }
    
    public static void main(String[] args)
    { 
        Language t1 = new Language("a");
        Language t2 = new Language("abc");
        Language t3 = t1.union(t2);
        StdOut.println(t3);
        
        Language t4 = new Language("b");
        Language t5 = new Language("bc");
        Language t6 = new Language("bcd");
        Language t7 = t4.union(t5);
        Language t8 = t7.union(t6);
        Language t9 = new Language("cd");
        Language t10 = new Language("d");
        Language t11 = t9.union(t10);
        Language t12 = t8.concatenate(t11);
        StdOut.println(t12);
        
        Language t13 = new Language("e");
        Language t14 = new Language("ef");
        Language t15 = t13.union(t14);
        Language t16 = t15.closure(2);
        StdOut.println(t16);
    }
}