package net.lookin.lookin_net.algorithm;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;

/* http://look-in.net/2007/12/26/algorithm-caesar-cipher/
Algorithm: Шифр цезаря
categories: algorithm, puzzle —   edit

Как известно, Цезарь шифровал сообщения очень просто: Шифр Цезаря (wikipedia).

Пример шифрование с использованием ключа k=3:

Оригинальный текст: Съешь же ещё этих мягких французских булок, да выпей чаю.
Шифрованный текст: Фэзыю йз зьи ахмш пвёнлш чугрщцкфнлш дцосн, жг еютзм ъгб.

Интересны следующие задачи:

1) нужно найти такое русское игровое слово* и число k (ключ) что бы слово преобразовывалось опять в слово из множества слов русского языка.

2) найти такое игровое слово с максимальной длинной.

* - нарицательное существительное в именительном падеже, единственном числе. пример: слесарь, антибиотик.
*/

public class CeaserCipher {
	// storing vocabulary 
	private TreeSet<String> voc = new TreeSet<String>();
	// counter for success results
	public int count = 0;
    // alphabet of letters
	private char[] alph = new char[34];
    // map of alphabet
	private HashMap<Character, Integer> map = new HashMap<Character, Integer>(
			33);
	// result of coding
	private StringBuffer result = new StringBuffer(30);
	
	public static void main(String[] args) throws IOException {
		Date st = new Date();
		String fileName = "words.txt";
		if ((args != null) && (args.length == 1)) {
			fileName = args[0];
		}
		CeaserCipher cc = new CeaserCipher(fileName);

		cc.perform();
		Date et = new Date();
		System.out.print("time=" + (et.getTime() - st.getTime()) + " count="
				+ cc.count);
	}
    // constructor
	public CeaserCipher(String filename) throws IOException {
		this.loadVoc(filename);
		this.fillConst();
	}

	// load vocabulary
	private void loadVoc(String fileName) throws IOException {
		HashSet<String> s = new HashSet<String>(42700);
		BufferedReader reader = new BufferedReader(new FileReader(fileName));
		String word;
		while ((word = reader.readLine()) != null) {
			s.add(word);
			// System.out.print(word);
		}
		voc.addAll(s);
	}

    // perfrom iteration by words and by key
	// O(n) ~ 16*words.length*log(words.length) = n*log(n)
	public void perform() {
		Iterator i = voc.iterator();
		while (i.hasNext()) {
			String word = (String) i.next();
			// the "bad" word
			if ((word.indexOf("-") != -1) || (word.indexOf(" ") != -1)) {
				continue;
			}
			for (int k = 1; k < 17; k++) {
				
				if (voc.contains(this.code(word, k))) {
					count++;
					System.out.println(word + " " + this.code(word, k) + " " + k+" len="+word.length());
				}
			}
		}
	}
    // perform coding: word using key=k 
	private String code(String word, int k) {
		result.setLength(0);
			for (int i = 0; i < word.length(); i++) {
				result.append(alph[(map.get(word.charAt(i)) + k) % 33]);
			}
		return result.toString();
	}

	// prepare alphabet. Ё - specific
	private void fillConst() {
		int k = 0;
		for (char i = 'а'; i <= 'е'; i++) {
			alph[k] = i;
			map.put(i, k);
			k++;
		}
		alph[k] = 'ё';
		map.put('ё', 7);
		k++;
		for (char i = 'ж'; i <= 'я'; i++) {
			alph[k] = i;
			map.put(i, k);
			k++;
		}
	}
}

