#include <bits/stdc++.h>
using namespace std;

using VS = vector<string>;    using LL = long long;
using VI = vector<int>;       using VVI = vector<VI>;
using PII = pair<int, int>;   using PLL = pair<LL, LL>;
using VL = vector<LL>;        using VVL = vector<VL>;

#define ALL(a)  begin((a)),end((a))
#define RALL(a) (a).rbegin(), (a).rend()
#define SZ(a) int((a).size())
#define SORT(c) sort(ALL((c)))
#define RSORT(c) sort(RALL((c)))
#define UNIQ(c) (c).erase(unique(ALL((c))), end((c)))
#define FOR(i, s, e) for (int(i) = (s); (i) < (e); (i)++)
#define FORR(i, s, e) for (int(i) = (s); (i) > (e); (i)--)
//#pragma GCC optimize ("-O3") 
#ifdef YANG33
#include "mydebug.hpp"
#else
#define DD(x) 
#endif
const int INF = 1e9;                          const LL LINF = 1e16;
const LL MOD = 1000000007;                    const double PI = acos(-1.0);
int DX[8] = { 0, 0, 1, -1, 1, 1, -1, -1 };    int DY[8] = { 1, -1, 0, 0, 1, -1, 1, -1 };

/* -----  2018/11/12  Problem: ARC 050 D / Link: http://arc050.contest.atcoder.jp/tasks/arc050_d  ----- */
/* ------問題------
-----問題ここまで----- */
/* -----解説等-----
文字列集合をつなげて辞書順最小をつくるときは、si<sjなら、si+sj<sj+siであるようにすればよい。
大小関係を求めたいが、連結すると当然TLEする。suffixを並べるので、LCPやらrankが使えそう。
i<jとすると、連結されたものについて、区間が3つに分かれる。
それぞれ場合分けすれば解ける。
1.先頭のa.bの共通部分がbの長さ未満の時：suffix[a],suffix[b]の大小関係を見ればよく、rank[a],rank[b]を見れば良い。
2.suffix[a]のaと、suffix[b]の続きのsuffix[a]の共通部分がその長さ未満の時：suffix[a]のprefixをlen(b)だけ削ったものはsuffix(c)と考えられる。
これについて、suffix[c],suffix[a]のrankを比べれば良い。
3.ソレ以降までlcpがあるとき：一致する(一致しないならば最初の比較に失敗しなければならないため)
結局、suffix集合であることによってO(1)の比較が可能。
----解説ここまで---- */

struct SuffixArray {
	vector<int> SA;
	string s;

	void Build_SA(const string& str) {
		s = str;
		SA.resize(s.size());
		iota(begin(SA), end(SA), 0);
		sort(begin(SA), end(SA), [&](const int& a, const int& b)
		{
			if (s[a] == s[b]) return(a > b);
			return (s[a] < s[b]);
		});
		vector<int> classes(s.size()), c(s.size()), cnt(s.size());
		for (int i = 0; i < s.size(); i++) {
			c[i] = s[i];
		}
		for (int len = 1; len < s.size(); len <<= 1) {
			for (int i = 0; i < s.size(); i++) {
				if (i > 0 && c[SA[i - 1]] == c[SA[i]] && SA[i - 1] + len < s.size() && c[SA[i - 1] + len / 2] == c[SA[i] + len / 2]) {
					classes[SA[i]] = classes[SA[i - 1]];
				}
				else {
					classes[SA[i]] = i;
				}
			}
			iota(begin(cnt), end(cnt), 0);
			copy(begin(SA), end(SA), begin(c));
			for (int i = 0; i < s.size(); i++) {
				int s1 = c[i] - len;
				if (s1 >= 0) SA[cnt[classes[s1]]++] = s1;
			}
			classes.swap(c);
		}
	}

	vector<int> res_sa() {
		return SA;
	}
};

template< typename MeetSemilattice>
struct SparseTable {
	using T = typename MeetSemilattice::t;
	vector<vector<T>> st;

	SparseTable() {}

	SparseTable(const vector<T> &v) {
		int b = 0;
		while ((1 << b) <= v.size()) ++b;
		st.assign(b, vector<T>(1 << b));
		for (int i = 0; i < v.size(); i++) {
			st[0][i] = v[i];
		}
		for (int i = 1; i < b; i++) {
			for (int j = 0; j + (1 << i) <= (1 << b); j++) {
				st[i][j] = MeetSemilattice::op(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
			}
		}
	}
	inline T query(int l, int r) {
		if (l > r)swap(l, r);
		int b = 32 - __builtin_clz(r - l) - 1;
		return (MeetSemilattice::op(st[b][l], st[b][r - (1 << b)]));
	}
};

VI make_lcp(const string s, const VI &sa, const VI&rank) {
	VI LCP;
	LCP.resize(s.size());
	LCP[0] = 0;
	for (int i = 0, h = 0; i < s.size(); i++) {
		if (rank[i] + 1 < s.size()) {
			for (int j = sa[rank[i] + 1]; max(i, j) + h < s.length() && s[i + h] == s[j + h]; ++h);
			LCP[rank[i] + 1] = h;
			if (h > 0) --h;
		}
	}
	return move(LCP);
}

struct LCPs {
	struct M {
		using t = int;
		static t op(const t&l, const t&r) { return min(l, r); }
	};
	SparseTable<M> seg;
	vector<int> rank;
	LCPs(const string &s) {
		vector<int>sa; {
			SuffixArray suffixarray;
			suffixarray.Build_SA(s);
			sa.swap(suffixarray.SA);
		}
		rank = vector<int>((int)s.size());
		for (int i = 0; i < (int)s.size(); ++i) {
			rank[sa[i]] = i;
		}
		vector<int>LCP = make_lcp(s, sa, rank);
		seg = SparseTable<M>(LCP);
	}
	int lcp(int i, int j) { //s[i..] s[j..]
		if (rank[i] > rank[j]) {
			return seg.query(rank[j] + 1, rank[i] + 1);
		}
		else {
			return seg.query(rank[i] + 1, rank[j] + 1);
		}
	}
};




int main() {
	cin.tie(0);
	ios_base::sync_with_stdio(false);

	string s; cin >> s;
	// 全部一緒(N,1)
	{
		int same = 1;
		FOR(i, 0, SZ(s)) {
			same &= s.front() == s[i];
		}
		if (same) {
			cout << SZ(s) << endl;
			cout << 1 << endl;
			return 0;
		}
	}
	// 周期判定(1,1)
	{
		LCPs lcps(s);
		int periodOne = 1;
		// どのような分割も周期をもたない
		FOR(i, 0, SZ(s) - 1) {
			int prefixsz = i + 1;
			if (SZ(s) % prefixsz == 0) {

				int periodLCP = lcps.lcp(0, i + 1);
				//				int periodLCP = rh.LCP(rh, 0, SZ(s), i + 1, SZ(s));
				periodOne &= (periodLCP < SZ(s) - prefixsz);
			}
		}
		if (periodOne) {
			cout << 1 << endl;
			cout << 1 << endl;
			return 0;
		}
	}
	// その他 (2,N-1-分割しちゃダメ)
	{
		// WA
		// 最後のケースはnodiv[k]=1が存在しない
		auto f = [](const string& s) {
			LCPs lcps(s);
			VI noDivide(SZ(s), 0);
			FOR(prefixsz, 1, SZ(s)) {
				for (int k = 2 * prefixsz; k < SZ(s); k += prefixsz) {
					if (lcps.lcp(0, prefixsz) >= k - prefixsz) {
						noDivide[k - 1] = 1;
					}
					else break;
				}
			}
			return noDivide;
		};
		// 前から
		auto prefixdiv = f(s);
		// 後ろから
		string t = s;
		reverse(ALL(t));
		auto suffixdiv = f(t);
		reverse(ALL(suffixdiv));


		int ans = SZ(s) - 1;
		FOR(i, 0, SZ(s) - 1) {
			ans -= (prefixdiv[i] || suffixdiv[i + 1]);
		}
		cout << 2 << endl;
		cout << ans << endl;
	}




	return 0;
}


Measured memory_bytes: 50508