Generate and return **all** possible increasing arithmetic progressions of six primes `[a, b, c, d, e, f]` between the given limits. Note: the upper and lower limits are inclusive.

An arithmetic progression is a sequence where the difference between consecutive numbers is the same, such as: 2, 4, 6, 8.

A prime number is a number that is divisible only by itself and 1 (e.g. 2, 3, 5, 7, 11)

Your solutions should be returned as lists inside a list in ascending order of the first item (if there are multiple lists with same first item, return in ascending order for the second item etc) are the e.g: `[ [a, b, c, d, e, f], [g, h, i, j, k, l] ]` where `a < g`. If there are no solutions, return an empty list: `[]`

## Examples

Difficulty: EASY
Starter code: def primes_a_p(lower_limit, upper_limit):
	

Solutions:
is_prime = lambda n: all((n % d for d in range(3, int(n ** 0.5) + 1, 2)))

def primes_a_p(lower_limit, upper_limit):
	a_p = []
	for n in range(lower_limit | 1, upper_limit, 2):
		for gap in range(30, (upper_limit - n) // 5 + 1, 30):
			sequence = [n + i * gap for i in range(6)]
			if all(map(is_prime, sequence)):
				a_p.append(sequence)
	return a_p


limit = 10000
sieve = [0] * 2 + list(range(2, limit))
for i in range(2, limit):
	for j in range(i * i, limit, i):
		sieve[j] = 0
primes = {i for i in sieve if i}

def primes_a_p(l, u):
	(li, found) = (sorted([i for i in primes if l <= i <= u]), [])
	for (i, a) in enumerate(li):
		for b in li[i + 1:]:
			(diff, tray) = (b - a, [a, b])
			for _ in range(4):
				c = tray[-1] + diff
				if c in primes and c < u:
					tray.append(c)
					continue
				break
			if len(tray) == 6:
				found.append(tray)
	return found


from itertools import combinations, compress
import numpy as np
s = np.ones(100000)
s[:2] = s[2::2] = 0
for i in range(3, int(len(s) ** 0.5) + 1, 2):
	if s[i]:
		s[i * i::i] = 0
primes = list(compress(range(len(s)), s))

def primes_a_p(lower_limit, upper_limit):
	xs = primes[np.searchsorted(primes, lower_limit):np.searchsorted(primes, upper_limit)]
	return [list(range(a, a + 6 * (b - a), b - a)) for (a, b) in combinations(xs, 2) if all((x <= upper_limit and s[x] for x in range(b + b - a, a + 6 * (b - a), b - a)))]


from bisect import *
n = 10000
(sieve, PRIMES) = ([0] * (n + 1), [])
for i in range(2, n + 1):
	if not sieve[i]:
		PRIMES.append(i)
		for j in range(i ** 2, n + 1, i):
			sieve[j] = 1

def primes_a_p(low, high):
	subPrimes = PRIMES[bisect_left(PRIMES, low):bisect_right(PRIMES, high)]
	(ans, sP) = ([], set(subPrimes))
	for (i, p) in enumerate(subPrimes):
		for q in subPrimes[i + 1:]:
			d = q - p
			prog = [p + d * n for n in range(6)]
			if set(prog) <= sP:
				ans.append(prog)
	return ans


LIMIT = 10 ** 4
sieve = list(range(LIMIT))
sieve[1] = 0
primes = []
for n in sieve:
	if n:
		primes.append(n)
		for i in range(n * n, LIMIT, n):
			sieve[i] = 0
del sieve
primes.remove(2)

def primes_a_p(lower_limit, upper_limit, k=6):
	primes_list = [p for p in primes if lower_limit <= p <= upper_limit]
	primes_set = set(primes_list)
	max_scope = (upper_limit - lower_limit) // (k - 1)
	base_diff = 2 * 3 * 5
	solutions = []
	for n in range(1, max_scope // base_diff + 1):
		diffs = [n * base_diff * x for x in range(k)]
		scope = n * base_diff * (k - 1)
		for p in primes_list:
			if p + scope > upper_limit:
				break
			if {p + d for d in diffs} <= primes_set:
				solutions.append([p + d for d in diffs])
	return sorted(solutions)


def primes1(n):
	sieve = [True] * n
	for i in range(3, int(n ** 0.5) + 1, 2):
		if sieve[i]:
			sieve[i * i::2 * i] = [False] * ((n - i * i - 1) // (2 * i) + 1)
	return [2] + [i for i in range(3, n, 2) if sieve[i]]
lst = primes1(10000)
import bisect

def primes_a_p(lower_limit, upper_limit):
	lst0 = lst[bisect.bisect_left(lst, lower_limit):bisect.bisect_right(lst, upper_limit)]
	ans = []
	for i in range(len(lst0) - 5):
		for j in range(30, upper_limit // 5, 30):
			x = lst0[i] + j
			if x not in lst0:
				continue
			if x > lst0[-1]:
				break
			temp = [lst0[i], x]
			flag = True
			while len(temp) < 6:
				x += j
				if x in lst0:
					temp.append(x)
				else:
					flag = False
					break
			if flag == True:
				ans.append(temp)
	return ans


def sito(m, n):
	numbers = [x for x in range(0, n + 1)]
	(numbers[0], numbers[1]) = (False, False)
	primes = {}
	for (i, x) in enumerate(numbers):
		if x:
			if x >= m:
				primes[x] = x
			index = i ** 2
			while index < len(numbers):
				numbers[index] = False
				index += x
	return primes

def primes_a_p(lower_limit, upper_limit):
	primes = sito(lower_limit, upper_limit)
	longest_gap = (upper_limit - lower_limit) // 5
	ap_primes = []
	for i in list(primes.keys()):
		for gap in range(2, longest_gap, 2):
			if primes[i] + 5 * gap <= upper_limit:
				check = [primes[i] + n * gap for n in range(0, 6)]
				if any((num not in primes for num in check)):
					pass
				else:
					ap_primes.append(check)
	return ap_primes


from math import sqrt

def primes_a_p(lower_limit, upper_limit):
	primes = [p for p in prime_sieve(upper_limit + 1) if p >= lower_limit]
	res = []
	for k in range(30, upper_limit // 5, 30):
		terms = [0, k, 2 * k, 3 * k, 4 * k, 5 * k]
		for p in primes:
			pp = [t + p for t in terms]
			if all((t in primes for t in pp)):
				res.append(pp)
	return sorted(res, key=lambda v: v[0])

def prime_sieve(n):
	sieve = [True] * n
	for i in range(3, int(sqrt(n)) + 1, 2):
		if sieve[i]:
			sieve[i * i::2 * i] = [False] * ((n - i * i - 1) // (2 * i) + 1)
	return [2] + [i for i in range(3, n, 2) if sieve[i]]