import java.util.regex.*;

public class Main {
    private static Pattern combineRe(Pattern p1, Pattern p2){
        return Pattern.compile(combineRE(p1.pattern(), p2.pattern()));
    }
    private static String combineRE(String p1, String p2){
        int groups1 = 0, groups2=0;
        StringBuilder newP = new StringBuilder("(?=");
        newP.append(p1);
        newP.append("$)(?=");
        
        Pattern capturingGroup = Pattern.compile("(?<!\\\\)(\\\\\\\\)*\\((?!\\?)");
        Matcher m = capturingGroup.matcher(p1);
        while(m.find()) groups1 ++;
        
        m = capturingGroup.matcher(p2);
        
        while(m.find()) groups2 ++;
        String new2 = p2;
        
        for(int i=1; i<=groups2; i++)
            new2 = new2.replaceAll("(?<!\\\\)\\\\"+i, "\\\\" + (i+groups1));
                
        newP.append(new2);
        newP.append("$).*");
        return newP.toString();
    }
    public static void main(String[] args){
        reComboTestCase[] cases = {
            new reComboTestCase(
                "(?=.*)([ab])\\1"
                , "([c]*)\\1bb"
                , new String[] { "aa" }
                , new String[] { "ccccbb" }
                , new String[] { "bb" }
            )
            , new reComboTestCase(
                "a."
                , ".b"
                , new String[] { "aa", "ac" }
                , new String[] { "bb", "cb"}
                , new String[] { "ab" }
            )
        };
        
        for (int i=0; i<cases.length; i++)
            cases[i].test(i);
    }
    private static class reComboTestCase{
        String p1;
        String p2;
        String[] left; //Strings that should match p1 but not p2
        String[] right; //Strings that should match p2 but not p1
        String[] intersect; //Strings that should match both
        public reComboTestCase(String p1, String p2, String[] left, String[] right, String[] intersect) {
            this.p1 = p1;
            this.p2 = p2;
            this.left = left;
            this.right = right;
            this.intersect = intersect;
        }
        public void test(int n){
            System.out.printf("TEST %d:\n", n);
            System.out.printf("\t   P1: %s\n", p1);
            System.out.printf("\t   P2: %s\n", p2);
            String combo = Main.combineRE(p1,p2);
            System.out.printf("\tCombo: %s\n", combo);
            Pattern one = Pattern.compile(p1);
            Pattern two = Pattern.compile(p2);
            Pattern three = Pattern.compile(combo);
            System.out.printf("\t%100s: %5s %5s %5s\n", "LEFT", "ONE", "TWO", "COMBO");
            for(int i=0; i<left.length; i++)
                System.out.printf("\t%100s: %5s %5s %5s\n"
                    , left[i]
                    , one.matcher(left[i]).matches()
                    , two.matcher(left[i]).matches()
                    , three.matcher(left[i]).matches()
                );
            System.out.printf("\t%100s: %5s %5s %5s\n", "RIGHT", "ONE", "TWO", "COMBO");
            for(int i=0; i<right.length; i++)
                System.out.printf("\t%100s: %5s %5s %5s\n"
                    , left[i]
                    , one.matcher(right[i]).matches()
                    , two.matcher(right[i]).matches()
                    , three.matcher(right[i]).matches()
                );
            System.out.printf("\t%100s: %5s %5s %5s\n", "INTERSECT", "ONE", "TWO", "COMBO");
            for(int i=0; i<intersect.length; i++)
                System.out.printf("\t%100s: %5s %5s %5s\n"
                    , left[i]
                    , one.matcher(intersect[i]).matches()
                    , two.matcher(intersect[i]).matches()
                    , three.matcher(intersect[i]).matches()
                );
            
        }
    }
}