#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include<limits.h>
#include<algorithm>
#include<queue>
#include<set>
#include<math.h>
#include<random>
#include<set>
#include<cctype>

#define lli long long int
#define MOD 1000000007

using namespace std;

long long int modpow(long long int a, long long int n) {
	if (n == 0)return 1;
	if (n == 1)return (a % MOD);

	long long int hanbun = n / 2;
	long long int amari = n % 2;

	long long int ans = (a * a) % MOD;
	ans = modpow(ans, hanbun);

	if (amari == 1)ans *= a;

	ans %= MOD;

	return ans;

}

long long int gcd(long long int a, long long int b) {
	if (a < b) {
		long long int tmp = a;
		a = b;
		b = tmp;
	}

	long long int amari = a % b;

	if (amari == 0)return b;

	return gcd(b, amari);
}

long long int lcm(long long int a, long long int b) {
	long long int ans = (a * b) % MOD;
	long long int wari = modpow(gcd(a, b), MOD - 2);
	ans = (ans * wari) % MOD;
	return ans;
}

long long int factElement[10000] = { 0 };

long long int factrial(int n) {

	if (factElement[n] != 0)return factElement[n];

	if (n == 0) {
		factElement[0] = 1;
		return factElement[n];
	}

	long long int ans = n * factrial(n - 1);
	factElement[n] = ans;
	return factElement[n];

}

struct SegmentTree {
private:
	int n;
	vector<long long int> node;
	int TANNI;

	long long int Func(long long int a, long long int b) {
		return min(a,b);
	}

public:
	//元配列ｖをセグメント木で表現する。
	SegmentTree(vector<long long int> v, long long int tanni) {
		//最下段のノード数は元配列のサイズ以上になる最小の2冪→これをｎとおく。
		//セグメント木全体で必要なノード数は2n-1個である。
		int sz = v.size();
		n = 1;
		while (n < sz)n *= 2;

		TANNI = tanni;
		node.resize(2 * n - 1, TANNI);

//最下段に値を入れた後に、下の段から順番に値を入れる。
//値を入れるには、自分の子の２値を参照すればよい。
for (int i = 0; i < sz; i++)node[i + n - 1] = v[i];
for (int i = n - 2; i >= 0; i--)node[i] = Func(node[2 * i + 1], node[2 * i + 2]);
	}

	void update(long long int x, long long int val) {
		//最下段のノードにアクセスする。
		x += (n - 1);

		//最下段のノードを更新したら、あとは親に上って更新していく。
		node[x] = val;

		while (x > 0) {
			x = (x - 1) / 2;
			node[x] = Func(node[2 * x + 1], node[2 * x + 2]);
		}
	}

	long long int getVal(int x) {
		x += (n - 1);
		return node[x];
	}

	long long int getAns(int a, int b, int k = 0, int l = 0, int r = -1) {
		//最初に呼び出された時の区間は[0,n)
		if (r < 0)r = n;

		//要求区間と対象区間が交わらない→単位元を返す。
		if (r <= a || b <= l) return TANNI;

		//要求区間が対象区間を完全に被覆する→対象区間を答えの計算に使う。
		if (a <= l && r <= b) return node[k];

		//要求区間が対象区間の一部を被覆→子をよびだす。
		//左の子をvl、右の子をvrとしている。
		//新しい対象区間は現在の対象区間を半分に割ったもの。
		int vl = getAns(a, b, 2 * k + 1, l, (l + r) / 2);
		int vr = getAns(a, b, 2 * k + 2, (l + r) / 2, r);
		return Func(vl, vr);


	}



};




int getsuuji(string str1, string str2, string str3, string str4, string str5) {

	if (str1 == ".###") {
		//0,2,3,5,6,7,8,9
		if (str2 == ".#.#") {
			//0,8,9
			if (str3 == ".#.#") {
				//0
				return 0;

			}
			else {
				//8,9

				if (str4 == ".#.#")return 8;
				return 9;
			}
		}
		else if (str2 == "...#") {
			//2,3,7
			if (str3 == "...#") {
				//7
				return 7;
			}
			else {
				//2,3

				if (str4 == ".#..") {
					//2
					return 2;
				}
				else {
					//3
					return 3;
				}


			}

		}
		else {
			//5,6
			if (str4 == "...#") {
				//5
				return 5;
			}
			else {
				return 6;
			}



		}






	}
	else if (str1 == "..#.") {
		//1
		return 1;


	}
	else if (str1 == ".#.#") {
		//4

		return 4;


	}





}



int main()
{
	
	int n;
	cin >> n;

	vector<int> ans(n,0);

	for (int x = 1; x * x <= n; x++) {
		for (int y = 1; y * y <= n; y++) {
			for (int z = 1; z * z <= n; z++) {
				int wk;
				wk = x * x + y * y + z * z + x * y + y * z + z * x;

				if (1 <= wk && wk <= n) {
					ans[wk - 1]++;


				}




			}

		}


	}



	for (int i = 0; i < ans.size(); i++) {
		cout << ans[i] << endl;


	}





	return 0;

}



Measured memory_bytes: 3580