Problem:
Score : 1100 points

Problem Statement

Let f(A, B), where A and B are positive integers, be the string satisfying the following conditions:

f(A, B) has length A + B;

f(A, B) contains exactly A letters A and exactly B letters B;

The length of the longest substring of f(A, B) consisting of equal letters (ex., AAAAA or BBBB) is as small as possible under the conditions above;

f(A, B) is the lexicographically smallest string satisfying the conditions above.

For example, f(2, 3) = BABAB, and f(6, 4) = AABAABAABB.

Answer Q queries: find the substring of f(A_i, B_i) from position C_i to position D_i (1-based).

Constraints

1 \leq Q \leq 10^3

1 \leq A_i, B_i \leq 5 \times 10^8

1 \leq C_i \leq D_i \leq A_i + B_i

D_i - C_i + 1 \leq 100

All input values are integers.

Partial Score

500 points will be awarded for passing the testset satisfying 1 \leq A_i, B_i \leq 10^3.

Input

Input is given from Standard Input in the following format:

Q
A_1 B_1 C_1 D_1
A_2 B_2 C_2 D_2
:
A_Q B_Q C_Q D_Q

Output

For each query i in order of input, print a line containing the substring of f(A_i, B_i) from position C_i to position D_i (1-based).

Sample Input 1

5
2 3 1 5
6 4 1 10
2 3 4 4
6 4 3 7
8 10 5 8

Sample Output 1

BABAB
AABAABAABB
A
BAABA
ABAB

Solution in C# (Mono 4.6.2.0) (judge verdict: Runtime Error)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Globalization;
using System.Diagnostics;



class Myon
{
    public Myon() { }
    public static int Main()
    {
        new Myon().calc();
        return 0;
    }


    Scanner cin;

    void calc()
    {
        cin = new Scanner();
        int Q = cin.nextInt();
        for (int i = 0; i < Q; i++)
        {
            int A = cin.nextInt();
            int B = cin.nextInt();
            int C = cin.nextInt();
            int D = cin.nextInt();
            C--;
            D -= C;
            Console.WriteLine(calc2(A, B, C, D));
        }
    }

    string calc2(int A, int B, int C, int D)
    {
        int size = getSize(A, B);
        List<Tuple<int, int>> AL = new List<Tuple<int, int>>();
        List<Tuple<int, int>> BL = new List<Tuple<int, int>>();
        if ((long)A * size < B)
        {
            //typeBABAB
            AL.Add(Tuple.Create(1, 0));
            AL.Add(Tuple.Create(A, 1));
            if (B % size != 0) BL.Add(Tuple.Create(1, B % size));
            BL.Add(Tuple.Create(B / size, size));

            //Console.WriteLine("A");
        }
        else if (size == 1)
        {
            if (A < B) AL.Add(Tuple.Create(1, 0));
            AL.Add(Tuple.Create(A, 1));
            BL.Add(Tuple.Create(B, 1));
            BL.Add(Tuple.Create(100, 1));
            //Console.WriteLine("B");

        }
        else {
            int ng = 0;
            int ok = B;
            while (ok - ng > 1)
            {
                //Console.WriteLine(ok + " " + ng);
                int mid = (ng + ok) / 2;
                int okturn = (A - mid) / (size - 1);
                int ngturn = mid - ((B - mid + (size - 2)) / (size - 1));
                if (okturn <= ngturn + 1) ok = mid;
                else ng = mid;
            }
            

            int tmpA = A;
            tmpA -= ok;
            int fullA = tmpA / (size - 1);
            AL.Add(Tuple.Create(fullA, size));
            tmpA -= fullA * (size - 1);
            if (tmpA > 0)
            {
                if (fullA != ok)
                {
                    AL.Add(Tuple.Create(1, 1 + tmpA));
                    AL.Add(Tuple.Create(ok - fullA - 1, 1));
                }
                else
                {
                    AL.Add(Tuple.Create(1, A));
                }
            }
            else
            {
                AL.Add(Tuple.Create(ok - fullA, 1));
            }
            //AL.Reverse();

            int tmpB = B;
            tmpB -= ok;
            int fullB = tmpB / (size - 1);
            BL.Add(Tuple.Create(fullB, size));
            tmpB -= fullB * (size - 1);
            if (tmpB > 0)
            {
                if (fullB != ok)
                {
                    BL.Add(Tuple.Create(1, 1 + tmpB));
                    BL.Add(Tuple.Create(ok - fullB - 1, 1));
                }
                else
                {
                    BL.Add(Tuple.Create(1, tmpB));
                }
            }
            else
            {
                BL.Add(Tuple.Create(ok - fullB, 1));
            }
            BL.Reverse();
        }
        return getString(C, D, AL, BL);
    }

    string getString(long C, int D, List<Tuple<int, int>> AL, List<Tuple<int, int>> BL)
    {
        StringBuilder ans = new StringBuilder();
        long Aturn = 0;
        long Anum = 0;
        int Apos = 0;
        long Bturn = 0;
        long Bnum = 0;
        int Bpos = 0;
        while (ans.Length < D)
        {
            while (Aturn == 0 && AL.Count > Apos)
            {
                Aturn = AL[Apos].Item1;
                Anum = AL[Apos].Item2;
                Apos++;
            }

            while(Bturn == 0 && BL.Count > Bpos)
            {
                Bturn = BL[Bpos].Item1;
                Bnum = BL[Bpos].Item2;
                Bpos++;
            }

            if(C >= Anum + Bnum)
            {
                long need = (C / Math.Max(Anum + Bnum, 1));
                need = Math.Min(need, Math.Min(Aturn, Bturn));
                C -= need * (Anum + Bnum);
                Aturn -= need;
                Bturn -= need;
                continue;
            }

            long tempNum, dec;

            Aturn--;
            tempNum = Anum;
            dec = Math.Min(Anum, C);
            tempNum -= dec;
            C -= dec;

            ans.Append(new string('A', (int)tempNum));

            Bturn--;
            tempNum = Bnum;
            dec = Math.Min(Bnum, C);
            tempNum -= dec;
            C -= dec;

            ans.Append(new string('B', (int)tempNum));

        }



        return ans.ToString().Substring(0, D);
    }
    
    int getSize(int A, int B)
    {
        int MAX = Math.Max(A, B);
        int MIN = Math.Min(A, B);

        int size = (MAX + MIN) / (MIN + 1);
        return size;
    }
}


class Scanner
{
    string[] s;
    int i;

    char[] cs = new char[] { ' ' };

    public Scanner()
    {
        s = new string[0];
        i = 0;
    }

    public string next()
    {
        if (i < s.Length) return s[i++];
        string st = Console.ReadLine();
        while (st == "") st = Console.ReadLine();
        s = st.Split(cs, StringSplitOptions.RemoveEmptyEntries);
        if (s.Length == 0) return next();
        i = 0;
        return s[i++];
    }

    public int nextInt()
    {
        return int.Parse(next());
    }
    public int[] ArrayInt(int N, int add = 0)
    {
        int[] Array = new int[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = nextInt() + add;
        }
        return Array;
    }

    public long nextLong()
    {
        return long.Parse(next());
    }

    public long[] ArrayLong(int N, long add = 0)
    {
        long[] Array = new long[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = nextLong() + add;
        }
        return Array;
    }

    public double nextDouble()
    {
        return double.Parse(next());
    }


    public double[] ArrayDouble(int N, double add = 0)
    {
        double[] Array = new double[N];
        for (int i = 0; i < N; i++)
        {
            Array[i] = nextDouble() + add;
        }
        return Array;
    }
}