Little Tanya decided to present her dad a postcard on his Birthday. She has already created a message — string *s* of length *n*, consisting of uppercase and lowercase English letters. Tanya can't write yet, so she found a newspaper and decided to cut out the letters and glue them into the postcard to achieve string *s*. The newspaper contains string *t*, consisting of uppercase and lowercase English letters. We know that the length of string *t* greater or equal to the length of the string *s*.

The newspaper may possibly have too few of some letters needed to make the text and too many of some other letters. That's why Tanya wants to cut some *n* letters out of the newspaper and make a message of length exactly *n*, so that it looked as much as possible like *s*. If the letter in some position has correct value and correct letter case (in the string *s* and in the string that Tanya will make), then she shouts joyfully "YAY!", and if the letter in the given position has only the correct value but it is in the wrong case, then the girl says "WHOOPS".

Tanya wants to make such message that lets her shout "YAY!" as much as possible. If there are multiple ways to do this, then her second priority is to maximize the number of times she says "WHOOPS". Your task is to help Tanya make the message.
The first line contains line *s* (1 ≤ |*s*| ≤ 2·10<sup class="upper-index">5</sup>), consisting of uppercase and lowercase English letters — the text of Tanya's message.

The second line contains line *t* (|*s*| ≤ |*t*| ≤ 2·10<sup class="upper-index">5</sup>), consisting of uppercase and lowercase English letters — the text written in the newspaper.

Here |*a*| means the length of the string *a*.
# count = 0
#
# n = int(input())
# nLst = [int(x) for x in input().split()]
# nLst = list(map(int,input.split()))
#
#
# for i in nLst:
#     if i == 0:
#         count += 1
#         if count >= 2:
#             break
#
#
# if len(nLst) < 1:
#     print("NO")
#
# if len(nLst) > 1:
#     if count == 1:
#         print("YES")
#     else:
#         print("NO")
# if len(nLst) == 1:
#     if nLst[0]:
#         print("YES")
#     else:
#         print("NO")

# n = int(input())
# lst = []
# tempLst = []
# for i in range(n):
#     lst.append(list(map(int, input().split())))
# if n < 1:
#     print(0)
# else:
#     for i in lst:
#         for j in i:
#             if j not in tempLst:
#                 tempLst.append(j)
#     tempLst.sort()
#     index = -1
#     tempLst2 = [tempLst[0], tempLst[len(tempLst) - 1]]
#     for i in range(len(lst)):
#         if lst[i] == tempLst2:
#             index = i + 1
#             break
#     print(index)


# class Point:
#     def __init__(self, px, py):
#         self.x = px
#         self.y = py
#
#     def __lt__(self, other):
#         if self.x < other.x or (self.x == other.x and self.y < other.y):
#             return True
#         return False
#
#
# def process(n, lst):
#     i = 0
#     k = 0
#     res = []
#     while i < n:
#         while len(res) > 0 and res[-1] == k + 1:
#             k += 1
#             res.pop()
#         if lst[i] != k + 1:
#             res.append(lst[i])
#         else:
#             k += 1
#         i += 1
#     while len(res) > 0 and res[-1] == k + 1:
#         k += 1
#         res.pop()
#
#     if k == n:
#         print("yes")
#     else:
#         print("no")

# from queue import Queue
# from os import linesep as endl
# MAX = 1001


# def bfs(s, f, graph, visited, V, E):
#     for i in range(V):
#         visited[i] = [False for x in range(E)]
#         # path[i] = -1
#     q = Queue()
#     visited[s][f] = True
#     q.put([s, f])
#     while not q.empty():
#         u = q.get()
#         x = u[0]
#         y = u[1]
#         v = []
#         v.append([x + 1, y])
#         v.append([x, y + 1])
#         v.append([x - 1, y])
#         v.append([x, y - 1])
#         for p in v:
#             xx = p[0]
#             yy = p[1]
#             if xx >= 1 and xx <= V and yy >= 1 and yy <= E:
#                 if not visited[xx][yy] and graph[xx][yy] == '.':
#                     visited[xx][yy] = True
#                     q.put([xx, yy]
#             if not visited[v]:
#                 visited[v] = True
#                 q.put(v)
#                 # path[v] = u
#     if visited[f]:
#         return True
#     else:
#         return False


# def print_path(s, f):
#     b = []
#     if f == s:
#         print(s)
#         return
#     if path[f] == -1:
#         print(-1)
#         return
#     while True:
#         b.append(f)
#         f = path[f]
#         if f == s:
#             b.append(s)
#             break
#     sum = 0
#     for i in range(len(b)-1, -1, -1):
#         # print(b[i], end=' ')
#         sum += 6
#     print(sum, end=' ')
#     return


# def isOnMap(x, y, W, H):
#     return x >= 0 and y >= 0 and x < H and y < W
#
#
# def bfs1(sx, sy, graph, visited, W, H):
#     sheep = 0
#     wolf = 0
#     q = Queue()
#     q.put([sx, sy])
#     if graph[sx][sy] == 'v':
#         wolf += 1
#     if graph[sx][sy] == 'k':
#         sheep += 1
#     visited[sx][sy] = True
#     # visited[sx][sy] = True
#     while not q.empty():
#         u = q.get()
#         x = u[0] - 1
#         y = u[1]
#         if isOnMap(x, y, W, H):
#             if graph[x][y] == 'v' and not visited[x][y]:
#                 visited[x][y] = True
#                 wolf += 1
#                 q.put([x, y])
#             elif graph[x][y] == 'k' and not visited[x][y]:
#                 visited[x][y] = True
#                 sheep += 1
#                 q.put([x, y])
#             elif graph[x][y] == '.' and not visited[x][y]:
#                 visited[x][y] = True
#                 q.put([x, y])
#         x = u[0] + 1
#         y = u[1]
#         if isOnMap(x, y, W, H):
#             if graph[x][y] == 'v' and not visited[x][y]:
#                 visited[x][y] = True
#                 wolf += 1
#                 q.put([x, y])
#             elif graph[x][y] == 'k' and not visited[x][y]:
#                 visited[x][y] = True
#                 sheep += 1
#                 q.put([x, y])
#             elif graph[x][y] == '.' and not visited[x][y]:
#                 visited[x][y] = True
#                 q.put([x, y])
#         x = u[0]
#         y = u[1] - 1
#         if isOnMap(x, y, W, H):
#             if graph[x][y] == 'v' and not visited[x][y]:
#                 visited[x][y] = True
#                 wolf += 1
#                 q.put([x, y])
#             elif graph[x][y] == 'k' and not visited[x][y]:
#                 visited[x][y] = True
#                 sheep += 1
#                 q.put([x, y])
#             elif graph[x][y] == '.' and not visited[x][y]:
#                 visited[x][y] = True
#                 q.put([x, y])
#         x = u[0]
#         y = u[1] + 1
#         if isOnMap(x, y, W, H):
#             if graph[x][y] == 'v' and not visited[x][y]:
#                 visited[x][y] = True
#                 wolf += 1
#                 q.put([x, y])
#             elif graph[x][y] == 'k' and not visited[x][y]:
#                 visited[x][y] = True
#                 sheep += 1
#                 q.put([x, y])
#             elif graph[x][y] == '.' and not visited[x][y]:
#                 visited[x][y] = True
#                 q.put([x, y])
#     # if visited[fx][fy]:
#     #     return True
#     return sheep, wolf
#
#
# def bfs(s, lst, visited, graph, dist, m):
#     q = Queue()
#     visited[s] = True
#     q.put(s)
#     dist[s] = lst[s - 1]
#     count = 0
#     while not q.empty():
#         u = q.get()
#         # if len(graph[u]) == 1 and u != 1:
#         #     count += 1
#         # u = q.get()
#         for v in graph[u]:
#             if not visited[v]:
#                 visited[v] = True
#                 dist[v] = lst[v - 1]
#                 if len(graph[v]) == 1:
#                         if dist[u] <= m:
#                             count += 1
#                             dist[v] += dist[u]
#                 else:
#                     dist[v] += dist[u]
#                 q.put(v)
#     return count


# def isOnMap(r, c, W, H):
#     return r >= 0 and c >= 0 and r < H and c < W
#
#
# def dfsRecurtion(s, visited, graph, path):
#     visited[s] = True
#     for i in graph[s]:
#         if not visited[i]:
#             path[i] = s
#             dfsRecurtion(i, visited, graph, path)
#
#
# def dfs(src, graph, visited, dis):
#     s = []
#     visited[src] = True
#     dis[src] = 1
#     s.append(src)
#     while len(s) > 0:
#         u = s.pop()
#         for v in graph[u]:
#             if not visited[v]:
#                 visited[v] = True
#                 s.append(v)
#                 dis[src] += 1
#
# # def dfs(s, graph, visited, dis):
# #     visited[s] = True
# #     for i in graph[s]:
# #         if not visited[i]:
# #             dis[i] = dis[s] + 1
# #             dfs(i, graph, visited, dis)
#
# checkMap = [[1, 0], [-1, 0], [0, 1], [0, -1]]
#
#
# def dfs1(x, y, n, m, visited, graph, path, lakelth, countLake):
#     s = []
#     visited[x][y] = True
#     s.append([x, y])
#     temp1 = x
#     temp2 = y
#     lakelth[temp1][temp2] = 1
#     flag = True
#     # count = 1
#     if x == 0 or x == n - 1 or y == 0 or y == m - 1:
#         lakelth[temp1][temp2] = -1
#         flag = False
#     while len(s) > 0:
#         u = s.pop()
#         for i in range(len(checkMap)):
#             x = u[0] + checkMap[i][0]
#             y = u[1] + checkMap[i][1]
#             if isOnMap(x, y, m, n):
#                 if not visited[x][y]:
#                     if x == 0 or x == n - 1 or y == 0 or y == m - 1:
#                         lakelth[temp1][temp2] = -1
#                         flag = False
#                     visited[x][y] = True
#                     s.append([x, y])
#                     path[x][y] = [u[0], u[1]]
#                     if flag:
#                         # temp1 = x
#                         # temp2 = y
#                         # lakelth[temp1][temp2] += 1
#                         lakelth[x][y] = lakelth[temp1][temp2] + 1
#                         # count += 1
#                         lakelth[temp1][temp2] = -1
#                         temp1 = x
#                         temp2 = y
#                     else:
#                         lakelth[temp1][temp2] = -1
#     # print(lakelth[temp1][temp2])
#     if lakelth[temp1][temp2] > 0:
#         countLake += 1
#     return countLake
#
#
# # Letter = list("ABCDEFGHỊKLMNOPQRSTUVWXYZ")
# lstRow = [-1, -1, 0, 1, 1, 1, 0, -1]
# lstCol = [0, 1, 1, 1, 0, -1, -1, -1]
#
#
# def isOnMap1(r, c, W, H):
#     return r >= 0 and c >= 0 and r < H and c < W
#
#
# def dfs2(row, col, graph, visited, dis, w, h, maxCase):
#     for i in range(8):
#         r = row + lstRow[i]
#         c = col + lstCol[i]
#
#         if isOnMap1(r, c, w, h) and not visited[r][c] and ord(graph[r][c]) == ord(graph[row][col]) + 1:
#             visited[r][c] = True
#             dfs2(r, c, graph, visited, dis + 1, w, h, maxCase)
#     if dis > maxCase[0]:
#         maxCase[0] = dis
#
#
# def dfs3(src, graph, visited, length, maxDis, check_index):
#     if maxDis[0] < length:
#         maxDis[0] = length
#         check_index[0] = src
#     visited[src] = True
#     for node in graph[src]:
#         if not visited[node[0]]:
#             # visited[node[0]] = True
#             dfs3(node[0], graph, visited, length + node[1], maxDis, check_index)
#             # visited[node[0]] = False
#     visited[src] = False
#     # print(src, length)
#     # if maxDis[0] < length:
#     #     maxDis[0] = length


# def dfs3(src, graph, visited, roadDis, length, maxDis, n):
#     s = []
#     visited[src] = True
#     s.append(src)
#     distance = [0 for i in range(n)]
#     distance[src] = length
#     while len(s) > 0:
#         u = s.pop()
#         for v in graph[u]:
#             if not visited[v]:
#                 visited[v] = True
#                 s.append(v)
#                 print(f"Khoảng cách từ {src + 1} đến {v + 1} là {roadDis[v][u]}")
#                 distance[v] = distance[u] + roadDis[v][u]
#     maxNum = max(distance)
#     if maxDis[0] < maxNum:
#         maxDis[0] = maxNum

# import heapq


# class PQEntry:
#     def __init__(self, value):
#         self.value = value
#
#     def __lt__(self, other):
#         return self.value > other.value
#
#
# class Job:
#     def __init__(self, a, b, d):
#         self.a = a
#         self.b = b
#         self.d = d
#
#     def __lt__(self, other):
#         return self.d < other.d
#
#
# class JobProcess:
#     def __init__(self, a, b, d):
#         self.a = a
#         self.b = b
#         self.d = d
#
#     def __lt__(self, other):
#         return self.d > other.d


# import heapq
# from queue import Queue
# INF = int(1e9)
#
#
# class City:
#     def __init__(self, name, id, dist):
#         self.name = name
#         self.id = id
#         self.dist = dist
#
#     def __lt__(self, other):
#         return self.dist < other.dist
#
#
# def dijkstra_tuple(s, graph, dist):
#     pq = []
#     heapq.heappush(pq, (0, s))
#     dist[s] = 0
#     while len(pq) > 0:
#         top = heapq.heappop(pq)
#         u = top[1]
#         w = top[0]
#         if dist[u] != w:
#             continue
#         for neighbor in graph[u]:
#             if w + neighbor[0] < dist[neighbor[1]]:
#                 dist[neighbor[1]] = w + neighbor[0]
#                 heapq.heappush(pq, (dist[neighbor[1]], neighbor[1]))

'''IN 6 CHỮ SỐ SAU PHẦN THẬP PHÂN'''
'''print("{:.6f}".format(number) + ' percent')'''

# MAX = 1000
# INF = int(1e9)


# def bellman_ford(s, n, m, graph, dist):
#     dist[s] = 0
#     for i in range(n):
#         for j in range(m):
#             x = graph[j][0]
#             y = graph[j][1]
#             t = graph[j][2]
#             if (dist[x] != INF) and (dist[x] + t > dist[y]):
#                 dist[y] = dist[x] + t
#     for i in range(n):
#         for j in range(m):
#             x = graph[j][0]
#             y = graph[j][1]
#             t = graph[j][2]
#             if (dist[x] != INF) and (dist[x] + t > dist[y]):
#                 return False
#     return True
#         # x = graph[i][0]
#         # y = graph[i][1]
#         # t = graph[i][2]
#         # if dist[x] != INF and dist[x] + t < dist[y]:
#         #     dist[y] = -INF
#             # return False
#     # return True
#
#
# def bellman_ford1(s, n, graph, dist, negativeCycle):
#     dist[s][s] = 0
#     for i in range(n):
#         for j in range(len(graph)):
#             x = graph[j][0]
#             y = graph[j][1]
#             t = graph[j][2]
#             if (dist[s][x] != INF) and (dist[s][x] + t < dist[s][y]):
#                 dist[s][y] = dist[s][x] + t
#                 if i == n - 1:
#                     negativeCycle[s][y] = True
#     for i in range(n):
#         for j in range(len(graph)):
#             x = graph[j][0]
#             y = graph[j][1]
#             t = graph[j][2]
#             if (dist[s][x] != INF) and (dist[s][x] + t < dist[s][y]):
#                 negativeCycle[s][y] = True
#
# import math
# MAX_GRAPH = 507
# INF = 2**31 * 100
# CHAR_ORD = 65
#
#
# def floyd_warshall(graph):
#     for k in range(MAX_GRAPH):
#         for i in range(MAX_GRAPH):
#             for j in range(MAX_GRAPH):
#                 if i <= k <= j:
#                     graph[i][j] = max(graph[i][j], graph[i][k] + graph[k][j])
#     ans = 0
#     for i in range(len(graph)):
#         lst = graph[i]
#         for j in lst:
#             ans = max(ans, j)
#     return ans
#
#
# def floyd_warshall1(graph):
#     for k in range(MAX_GRAPH):
#         for i in range(MAX_GRAPH):
#             for j in range(MAX_GRAPH):
#                 if graph[i][j] == 0 or graph[i][j] > graph[i][k] + graph[k][j]:
#                     if graph[i][k] and graph[k][j]:
#                         graph[i][j] = graph[i][k] + graph[k][j]
#
#
# from bisect import bisect_left
# from bisect import bisect_right
#
#
# def check(a, n, jump):
#     for i in range(n):
#         if a[i] > jump:
#             return False
#         if a[i] == jump:
#             jump -= 1
#     return True
#
#
# import math
#
#
# INF = float(1e-7)
#
#
# def f(x, p, q, r, s, t, u):
#     return p * math.exp(-x) + q * math.sin(x) + r * math.cos(x) + s * math.tan(x) + t * (x**2) + u
#
#
# def bis(p, q, r, s, t, u):
#     print(INF)
#     left, right = 0, 1
#     while left + INF < right:
#         x = left + (right - left) / 2
#         if f(left, p, q, r, s, t, u) * f(x, p, q, r, s, t, u) <= 0.0:
#             right = x
#         else:
#             left = x
#     return left + (right - left) / 2

#BST

import string


def print_tree(d, count_tree):
    od = {k: v for k, v in sorted(d.items())}
    for k, v in od.items():
        print(k, "{:.4f}".format((v / count_tree) * 100))
    d = dict()
    count_tree = 0
    return d, count_tree


def main():
    s = input()
    t = input()
    yay = 0
    whoops = 0
    setWord = dict()
    for i in t:
        if setWord.get(i) == None:
            setWord[i] = 1
        else:
            setWord[i] = setWord.get(i) + 1
    for i in s:
        if setWord.get(i) != None:
            if setWord[i] > 0:
                setWord[i] = setWord.get(i) - 1
                yay += 1
            else:
                whoops += 1
        else:
            whoops += 1
    print(yay, whoops)


if __name__ == '__main__':
    main()

# if __name__ == '__main__':
#     test_case = int(input())
#     for i in range(test_case):
#         n, m, k, s, t = map(int, input().split())
#         graph = [[] for j in range(n + 1)]
#         dist_s = [INF for j in range(n + 1)]
#         dist_t = [INF for j in range(n + 1)]
#         for j in range(m):
#             d, c, l = map(int, input().split())
#             graph[d].append((l, c))
#             graph[c].append((l, d))
#         dijkstra_tuple(s, graph, dist_s)
#         dijkstra_tuple(t, graph, dist_t)
#         min_road = dist_s[t]
#         print(min_road)
#         for j in range(k):
#             u, v, q = map(int, input().split())
#             # print(dist_s[u], dist_t[v])
#             # print(dist_s[v], dist_t[u])
#             temp = min(dist_s[u] + q + dist_t[v], dist_s[v] + q + dist_t[u])
#             if temp < min_road:
#                 min_road = temp
#
#             # distance = dijkstra_tuple(s, t, graph, n)
#             # if min_road > distance:
#             #     min_road = distance
#             # graph[u].pop()
#             # graph[v].pop()
#
#         if min_road == INF:
#             print(-1)
#         else:
#             print(min_road)

    # DFS
    # N = int(input())
    # graph = [[] for i in range(N + 1)]
    # visited = [False for i in range(N + 1)]
    # path = [-1 for i in range(N + 1)]
    # for i in range(N - 1):
    #     u, v = map(int, input().split())
    #     graph[u].append(v)
    #     graph[v].append(u)
    # s = 1
    # dfs(s, visited, graph, path)
    # M = int(input())
    # lstHarem = [False for i in range(N + 1)]
    # min_id = 10**9
    # min_num = 10**9
    # # print(path)
    # while M > 0:
    #     id = int(input())
    #     if path[id] < min_num or (path[id] == min_num and id < min_id):
    #         min_id = id
    #         min_num = path[id]
    #     M -= 1
    # print(min_id)'''
    #
    # '''BFS'''
    # '''N, M = map(int, input().split())
    # graph = [[] for i in range(N + 1)]
    # visited = [[] for i in range(N + 1)]
    # for i in range(len(visited)):
    #     visited[i] = [False for i in range(M + 1)]
    # for i in range(N):
    #     graph[i] = list(input())
    #     for j in range(M):
    #         if graph[i][j] == '#':
    #             visited[i][j] = True
    # sheepNum = 0
    # wolfNum = 0
    # for i in range(N):
    #     for j in range(M):
    #         if graph[i][j] != '#' and not visited[i][j]:
    #             sheep, wolf = bfs1(i, j, graph, visited, M, N)
    #             if i != 0 and i != N - 1 and j != 0 and j != M - 1:
    #                 if sheep > wolf:
    #                     wolf = 0
    #                 else:
    #                     sheep = 0
    #             # print(sheep, wolf)
    #             sheepNum += sheep
    #             wolfNum += wolf
    # print(sheepNum, wolfNum)




