using System;
using System.Collections.Generic;
public class Test
{
const long MOD = 1000000007;
abstract class RegNode { }
class SymbolNode : RegNode
{
public char Symbol;
public SymbolNode(char c) => Symbol = c;
}
class ConcatNode : RegNode
{
public RegNode Left, Right;
public ConcatNode(RegNode l, RegNode r) { Left = l; Right = r; }
}
class UnionNode : RegNode
{
public RegNode Left, Right;
public UnionNode(RegNode l, RegNode r) { Left = l; Right = r; }
}
class StarNode : RegNode
{
public RegNode Inner;
public StarNode(RegNode node) => Inner = node;
}
class RegexParser
{
string s;
int pos;
public RegNode Parse(string input)
{
s = input;
pos = 0;
return ParseExpression();
}
RegNode ParseExpression()
{
SkipWhite();
if (pos >= s.Length) throw new Exception("Błąd w wyrażeniu");
if (s[pos] == 'a' || s[pos] == 'b')
{
var node = new SymbolNode(s[pos]);
pos++;
SkipWhite();
return node;
}
if (s[pos] == '(')
{
pos++;
SkipWhite();
var left = ParseExpression();
SkipWhite();
if (pos >= s.Length) throw new Exception("Brak zamknięcia nawiasu");
if (s[pos] == '*')
{
pos++;
SkipWhite();
if (pos >= s.Length || s[pos] != ')')
throw new Exception("Brak ) po *");
pos++;
SkipWhite();
return new StarNode(left);
}
if (s[pos] == '|')
{
pos++;
SkipWhite();
var right = ParseExpression();
SkipWhite();
if (pos >= s.Length || s[pos] != ')')
throw new Exception("Brak ) w alternatywie");
pos++;
SkipWhite();
return new UnionNode(left, right);
}
// (R1R2)
var rightConcat = ParseExpression();
SkipWhite();
if (pos >= s.Length || s[pos] != ')')
throw new Exception("Brak ) w konkatenacji");
pos++;
SkipWhite();
return new ConcatNode(left, rightConcat);
}
throw new Exception("Niepoprawny symbol w wyrażeniu");
}
void SkipWhite()
{
while (pos < s.Length && char.IsWhiteSpace(s[pos]))
pos++;
}
}
class NFA
{
public List<(char symbol, int to)>[] transitions;
public List<int>[] epsilons;
public int Start;
public int End;
public int StateCount;
public NFA(int capacity)
{
transitions = new List<(char symbol, int to)>[capacity];
epsilons = new List<int>[capacity];
for (int i = 0; i < capacity; i++)
{
transitions[i] = new List<(char symbol, int to)>();
epsilons[i] = new List<int>();
}
}
}
class NfaBuilder
{
int idx = 0;
public NFA Build(RegNode root)
{
int cap = CountNodes(root)*2 + 2;
var nfa = new NFA(cap);
var (s, e) = BuildRec(root, nfa);
nfa.Start = s;
nfa.End = e;
nfa.StateCount = idx;
return nfa;
}
(int start, int end) BuildRec(RegNode node, NFA nfa)
{
if (node is SymbolNode sym)
{
int s = idx++;
int e = idx++;
nfa.transitions[s].Add((sym.Symbol, e));
return (s, e);
}
if (node is ConcatNode c)
{
var left = BuildRec(c.Left, nfa);
var right = BuildRec(c.Right, nfa);
nfa.epsilons[left.end].Add(right.start);
return (left.start, right.end);
}
if (node is UnionNode u)
{
int s = idx++;
int e = idx++;
var left = BuildRec(u.Left, nfa);
var right = BuildRec(u.Right, nfa);
nfa.epsilons[s].Add(left.start);
nfa.epsilons[s].Add(right.start);
nfa.epsilons[left.end].Add(e);
nfa.epsilons[right.end].Add(e);
return (s, e);
}
if (node is StarNode st)
{
int s = idx++;
int e = idx++;
var inner = BuildRec(st.Inner, nfa);
nfa.epsilons[s].Add(inner.start);
nfa.epsilons[s].Add(e);
nfa.epsilons[inner.end].Add(e);
nfa.epsilons[inner.end].Add(inner.start);
return (s, e);
}
throw new Exception("Nieznany typ węzła");
}
int CountNodes(RegNode node)
{
if (node is SymbolNode) return 1;
if (node is ConcatNode c) return 1 + CountNodes(c.Left) + CountNodes(c.Right);
if (node is UnionNode u) return 1 + CountNodes(u.Left) + CountNodes(u.Right);
if (node is StarNode s) return 1 + CountNodes(s.Inner);
return 0;
}
}
static List<int>[] EClosureAll(NFA nfa)
{
var closures = new List<int>[nfa.StateCount];
for (int i = 0; i < nfa.StateCount; i++)
closures[i] = EClosure(i, nfa);
return closures;
}
static List<int> EClosure(int start, NFA nfa)
{
var stack = new Stack<int>();
var visited = new bool[nfa.StateCount];
var list = new List<int>();
stack.Push(start);
visited[start] = true;
while (stack.Count > 0)
{
int s = stack.Pop();
list.Add(s);
foreach (int nxt in nfa.epsilons[s])
{
if (!visited[nxt])
{
visited[nxt] = true;
stack.Push(nxt);
}
}
}
return list;
}
static long[] BuildTransitionMatrix(NFA nfa)
{
int N = nfa.StateCount;
var closures = EClosureAll(nfa);
long[] M = new long[N*N];
for (int i = 0; i < N; i++)
{
foreach (int c in closures[i])
{
foreach (var (symbol, to) in nfa.transitions[c])
{
foreach (int t in closures[to])
{
M[i*N + t] = (M[i*N + t] + 1) % MOD;
}
}
}
}
return M;
}
static void MatrixMul(long[] A, long[] B, long[] C, int N)
{
Array.Clear(C, 0, N*N);
for (int i = 0; i < N; i++)
{
int iN = i*N;
for (int k = 0; k < N; k++)
{
long val = A[iN + k];
if (val == 0) continue;
int kN = k*N;
for (int j = 0; j < N; j++)
{
long sum = C[iN + j] + val * B[kN + j];
if (sum >= 8000000000000000000L) sum %= MOD;
C[iN + j] = sum;
}
}
// mod po każdej linii
for (int j = 0; j < N; j++)
C[iN + j] %= MOD;
}
}
static long[] MatrixPow
(long[] M
, long exp, int N
) {
long[] res = new long[N*N];
for (int i = 0; i < N; i++)
res[i*N + i] = 1;
long[] baseMat = new long[N*N];
Array.Copy(M, baseMat, N*N);
long[] tmp = new long[N*N];
{
{
MatrixMul(res, baseMat, tmp, N);
// swap
var swp = res;
res = tmp;
tmp = swp;
}
MatrixMul(baseMat, baseMat, tmp, N);
{
var swp = baseMat;
baseMat = tmp;
tmp = swp;
}
}
return res;
}
static long CountStrings(RegNode root, long L)
{
var nfaBuilder = new NfaBuilder();
var nfa = nfaBuilder.Build(root);
int N = nfa.StateCount;
long[] M = BuildTransitionMatrix(nfa);
var Mpow = MatrixPow(M, L, N);
long result = Mpow[nfa.Start*N + nfa.End] % MOD;
if (L == 0)
{
var startClosure = EClosure(nfa.Start, nfa);
if (startClosure.Contains(nfa.End))
result = (result + 1) % MOD;
}
return result;
}
public static void Main()
{
// Staram się wczytywać T bezpiecznie
string tLine = Console.ReadLine();
if (string.IsNullOrWhiteSpace(tLine))
{
// brak danych: np. brak testów
return; // albo throw
}
int T = int.Parse(tLine.Trim());
var parser = new RegexParser();
for (int _t = 0; _t < T; _t++)
{
string line = Console.ReadLine();
// zabezpieczenie przed nullem / pustym wierszem
if (string.IsNullOrWhiteSpace(line))
{
// Możemy przerwać albo wczytać jeszcze raz itp.
// Dla pewności mogę przerwać pętlę testów
// break;
// lub rzucić nowy wyjątek:
throw new Exception("Błąd: pusty wiersz testu!");
}
line = line.Trim();
int lastSpace = line.LastIndexOf(' ');
if (lastSpace == -1)
{
throw new Exception("Brak spacji rozdzielającej wyrażenie i L w wierszu: " + line);
}
string reg = line.Substring(0, lastSpace).Trim();
string lStr = line.Substring(lastSpace + 1).Trim();
if (!long.TryParse(lStr, out long L))
{
throw new Exception("Nieprawidłowa liczba L: " + lStr);
}
// Parsuję wyrażenie
var root = parser.Parse(reg);
// Liczę
long ans = CountStrings(root, L);
Console.WriteLine(ans % MOD);
}
}
}