//マップをつくる
//ふるって素数マップ作る
//dp[深さ][左端からの距離]で素数洞窟に入った回数をDP、素数かどうかはO(1)で判定
//ある意味良問。誤読とか範囲指定ミスとか嘘解法とか、出しまくった。
#include<iostream>
#include<queue>
#include<algorithm>
#include<stdio.h>
using namespace std;

class Solver{
public:
	int isprime[1100000];	//素数なら1、そうでないなら0を入れる
	int data[2001][2001];	//マップデータ。
	int dp[1201][1201];
	int dp2[1201][1201];
	
	void SetPrime( int m ){
		int i,j;
		for( i = 0; i <= m; i++ ) isprime[i] = 1;
		isprime[0] = 0; isprime[1] = 0;
		for( i = 2; i <= m; i++ ){
			if( isprime[i] ) {
				//cout << i << endl;
				for( j = i*2; j <= m; j += i )
					isprime[j] = 0;
			}
		}
	}
	
	void uzumaki( int m ){
		int i,j;
		int ci=600,cj=600;
		int cnt=1;
		for( i = 1; cnt <= m; i++ ){
			//→
			for( j = 0; j <= i; j++ ){
				if( cnt > m )
					break;
				data[i+ci][j+cj] = cnt;
				cnt++;
			}
			//↑
			for( j = 1; j <= 2*i-1; j++ ){
				if( cnt > m )
					break;
				data[i-j+ci][i+cj] = cnt;
				cnt++;
			}
			//←
			for( j = 1; j <= 2*i; j++ ){
				if( cnt > m )
					break;
				data[-i+1+ci][i-j+cj] = cnt;
				cnt++;
			}
			//↓
			for( j = 1; j <= 2*i; j++ ){
				if( cnt > m )
					break;
				data[-i+1+j+ci][-i+cj] = cnt;
				cnt++;
			}
			//→
			for( j = 1; j < i; j++ ){
				if( cnt > m )
					break;
				data[i+1+ci][-i+j+cj] = cnt;
				cnt++;
			}
		}
		//デバッグ
		/*for( i = -17; i <= 17; i++ ){
			for( j = -17; j <= 17; j++ ){
				if ( isprime[data[i+ci][j+cj]] )
					printf("%4dp",data[i+ci][j+cj]);
				else
					printf("%5d",data[i+ci][j+cj]);
			}
			cout << endl;
		}*/
	}
	
	pair<int, int> search_ij(int m, int n) {
		int i,j;
		int ci=600,cj=600;	//uzumakiと同じにしておくこと！
		int cnt=1;
		for( i = 1; cnt <= m; i++ ){
			if ( data[i+1+ci][cj] < n ) {
				cnt = data[i+1+ci][cj];
				continue;
			}
			//→
			for( j = 0; j <= i; j++ ){
				if( cnt == n )
					return pair<int, int>(i+ci, j+cj);
				cnt++;
			}
			//↑
			for( j = 1; j <= 2*i-1; j++ ){
				if( cnt == n )
					return pair<int, int>(i-j+ci, i+cj);
				cnt++;
			}
			//←
			for( j = 1; j <= 2*i; j++ ){
				if( cnt == n )
					return pair<int, int>(-i+1+ci, i-j+cj);
				cnt++;
			}
			//↓
			for( j = 1; j <= 2*i; j++ ){
				if( cnt == n )
					return pair<int, int>(-i+1+j+ci, -i+cj);
				cnt++;
			}
			//→
			for( j = 1; j < i; j++ ){
				if( cnt == n )
					return pair<int, int>(i+1+ci, -i+j+cj);
				cnt++;
			}
		}
		return pair<int, int>(-1, -1);
	}
	
	void DP( int m, int n ){
		int i,j;
		pair<int, int> ij = search_ij(m, n);
		i = ij.first; if ( i == -1 ) return;
		for( ; i < 1200; i++ ) {
			for( j = 0; j < 1200; j++ ) {
				dp[i][j] = -114514;
				dp2[i][j] = 0;
			}
		}
		i = ij.first;
		j = ij.second;
		dp[i][j] = 0;
		
		for( ; i < 1200; i++ ){
			for( j = 0; j < 1200; j++ ){
				//今いる場所がゴールなら何もしない
				if ( data[i][j] > m || dp[i][j] < 0 )
					continue;
				
				//今いる場所が素数か
				int c = isprime[ data[i][j] ];
				dp[i+1][j-1] = max( dp[i][j]+c, dp[i+1][j-1] );
				dp[i+1][j] = max( dp[i][j]+c, dp[i+1][j] );
				dp[i+1][j+1] = max( dp[i][j]+c, dp[i+1][j+1] );
			}
		}
		
		int ans = 0;
		for( i = ij.first; i < 1200; i++ ){
			for( j = 0; j < 1200; j++ ) {
				ans = max(ans, dp[i][j]);
			}
		}
		
		//経路復元とついでにans2の更新。
		//ちなみに、ゴールからたどっていけば、たどった場所すべてがスタートからありつけるようにできるので、
		//ans2を直接更新することができる。(経路の候補が消える！ということがない)→嘘解法らしい。
		//したがって、ans2はdp2を全部埋めてから求めることにしよう。
		//…最後に通過した素数洞穴の番号＞＜＞＜！(↑は嘘解法ではなかったようです。誤読！！最大値じゃない！！誤読！！)
		//誤飲も怖いけど誤読も恐ろforces…
		int ans2 = 0;
		if ( ans > 0 ) {
			//
			for( i = 1199; i > ij.first; i-- ) {
				for( j = 0; j < 1200; j++ ) {
					if ( (dp[i][j] != ans && dp2[i][j] == 0) || (dp2[i][j] == 252521) )
						continue;
					
					if ( data[i-1][j-1] <= m && dp[i-1][j-1] + isprime[data[i-1][j-1]] == dp[i][j]) {
						dp2[i-1][j-1] = 1;
						if ( isprime[data[i-1][j-1]] ) {
							dp2[i-1][j-1] = 252521;
						}
					}
					if ( data[i-1][j] <= m && dp[i-1][j] + isprime[data[i-1][j]] == dp[i][j] ) {
						dp2[i-1][j] = 1;
						if ( isprime[data[i-1][j]] )
							dp2[i-1][j] = 252521;
					}
					if ( data[i-1][j+1] <= m && dp[i-1][j+1] + isprime[data[i-1][j+1]] == dp[i][j] ) {
						dp2[i-1][j+1] = 1;
						if ( isprime[data[i-1][j+1]] )
							dp2[i-1][j+1] = 252521;
					}
				}
			}
			//
			for( i = 1199; i >= ij.first; i-- ) {
				for( j = 0; j < 1200; j++ ) {
					if ( dp2[i][j] == 252521 && isprime[data[i][j]] && data[i][j] <= m ) {
						ans2 = max(ans2, data[i][j]);
					}
				}
			}
		}
		cout << ans << " " << ans2 << endl;
	}
};

Solver test;

int main(){
	int m,n;
	
	test.SetPrime(1090000);
	test.uzumaki(1090000);
	while( cin >> m >> n ){
		if(!m)return 0;
		test.DP(m, n);
	}
	return 0;
}

Measured memory_bytes: 28268