/*
 * プログラミングスレまとめ in VIP
 * 初心者用課題 素数を求める
 * http://wiki.fdiary.net/vipprog/?%BD%E9%BF%B4%BC%D4%CD%D1%B2%DD%C2%EA
 */
package net.twoch.news4vip;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 * 素数一覧表示<br>
 * ユーザから指定されたn未満の素数を列挙する<br>
 */
public class PrimeNumber {

	/**
	 * Mainメソッド<br>
	 * ユーザからの指定されたnまでの間の素数を標準出力に出力する<br>
	 * 
	 * @param args	コマンドライン引数(利用しない)
	 */
	public static void main(String[] args) {
		System.out.println("1からnの間の素数を表示するプログラムです");
		
		// ユーザに入力を求める
		int n = getMax();
		// 異常が起きたときなどは負の数が返る
		if (n < 0) {
			System.out.println("異常終了します。");
			// 異常終了(-1)
			System.exit(-1);
		} else {
			// 非素数は全て-1となっているnまでの配列で返ってくる
			int[] prims = getPrimes(n);
			// 画面に表示
			printoutPrims(prims);
		}
	}


	/**
	 * 素数一覧取得<br>
	 * 2-maxの範囲の素数を表示する。<br>
	 * アルゴリズムはエラトステネスの篩を使う。<br>
	 * @param max	最大値
	 */
	private static int[] getPrimes(int max) {
		// 元の配列を作成する。
		// 2,3,4,...,max
		int[] data = createOriginalData(max);
		
		// 篩にかける 非素数には-1を設定する
		for(int i=0; i < data.length; i++) {
			// 非素数なので無視
			if (data[i] == -1) continue;
			
			// 篩にかかって残っているうちの最大値
			int survivalMax = data[i];
			for(int j=i+1; j < data.length; j++) {
				// 既に非素数と判定されておらず
				if ( data[j] != -1) {
					// 基準にしている数値で割り切れるなら素数ではない
					if ( (data[j] % data[i]) == 0 ) {
						data[j] = -1;
					} else {
						survivalMax = data[j];
					}
				}
			}
			// 生存している最大数が、基準値の平方根以下なら、
			// もう篩落とす数はいない
			if ( survivalMax < data[i] * data[i] ) break;
		}
		return data;
	}


	/**
	 * 素数表示<br>
	 * 配列中の-1以外の数を標準出力に出力する。<br>
	 * 
	 * @param data	表示データ
	 */
	private static void printoutPrims(int[] data) {
		// 素数を表示する
		int count = 0;
		for(int i =0; i < data.length; i++) {
			if (data[i] != -1) {
				count++;
				if (count == 10) {
					//横10個で改行
					System.out.println(data[i]);
					count = 0;
				} else {
					System.out.print(data[i] + " ");
				}
			}
		}
	}


	/**
	 * エラトステネスの篩用の初期配列作成<br>
	 * 
	 * @param max	最大値
	 * @return	初期配列
	 */
	private static int[] createOriginalData(int max) {
		int[] data = new int[max - 1];
		for(int i =2; i <= max; i++) {
			data[i-2] = i;
		}
		return data;
	}


	/**
	 * 最大値取得<br>
	 * ユーザのキーボード入力から最大値を取得する<br>
	 *  キー入力取得時に、IOExceptionが発生した場合、-1がかえる<br>
	 *  1以上の値が入力されるまで、再入力を求める。<br>
	 *  
	 * @return	最大値(-1  or 1 <)
	 */
	private static int getMax() {
		// 復返値
		int result = -1;
		BufferedReader bReader = null;
		try {
			// キーボード入力を受けるReaderを作成
			bReader =
				new BufferedReader(new InputStreamReader(System.in));
			// 最大値が設定されるまで繰り返す
			while( result == -1 ) {
				// プロンプトの後ろに入力を求める改行しない (println -> print)
				System.out.print("nを入力してEnterキーを押してください。\n>");
				// 改行コードまでの1行を取得 IOExceptionがthrowされる場合がある
				String line = bReader.readLine();
				// 文字列を数値に変換する
				result = string2int(line, 1);
			}
		} catch(IOException e) {
			// bReader.readLine からの異常をここで受け取る
			System.err.println(e.getMessage());
			System.out.println("キーボード入力を受けられませんでした。");
			result = -1;
		} finally {
			// 作成したReaderの後処理
			closeReader(bReader);
		}
		// ユーザが指定した値を返す
		return result;
	}

	/**
	 * 整数値変換<br>
	 * 文字列を、intの整数値に変換して返す。<br>
	 * 変換できない場合、最小値以下の場合は、-1を返す。<br>
	 * 
	 * @param line	文字列
	 * @param low 最小値
	 * @return	整数値(-1 or low<)
	 */
	private static int string2int(String line, int low) {
		int result;
		// 文字列を数値に変換する
		try {
			result = Integer.parseInt(line);
			if (result <= low) {
				result = -1;
				System.out.println("nは、" + (low + 1) 
						+ "以上に設定してください。");
			}
		} catch (NumberFormatException e) {
			// Integer.parseInt の異常をここで受け取る
			result = -1;
			System.out.println(line + "は、数値として処理できません。");
		}
		return result;
	}

	/**
	 * リーダークローズ処理<br>
	 * 指定されたリーダーがnullで無い場合、closeする。<br>
	 * close時のIOExceptionは、標準エラーにメッセージを出すだけとする。<br>
	 * 
	 * @param bReader	対象リーダー
	 */
	private static void closeReader(Reader bReader) {
		// JakartaのCommonsIO を使えば簡単になる
		if (bReader != null) {
			try {
				// 関連するストリームごとクローズ
				bReader.close();
			} catch (IOException e) {
				// このIOExceptionは復帰不能なのでエラー出力のみ
				System.err.println(e.getMessage());
			}
		}
	}
}