image-a-nation challenge – using Java

Interesting challenge from google image team… here’s an attempt to create the image using java,
(more work is needed to make it more resposive while reading the image files, also reading the clues directly from the web would be better! :)

Screenshot

Screenshot

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class GoogleImageBuilder extends JComponent {

	private static final String GOOGLE_URL = "http://google.com/images?q=tbn:";
    private static BufferedImage picture = null;

    public GoogleImageBuilder() {
    	setPreferredSize(new Dimension(16 * 32, 16 * 32));
    }

    public void paintComponent(Graphics g) {
    	Map clues = HardCodedClues.getClues();
    	SortedSet keys = new TreeSet(clues.keySet());

        int x = 0;
        int y = 0;

        for (Integer key : keys) {
			try {
				URL url = new URL(GOOGLE_URL  + clues.get(key));
				System.out.println("reading: " + url.toString());
				URLConnection uc = url.openConnection();
				picture = ImageIO.read(uc.getInputStream());

			} catch (MalformedURLException e) {
				System.out.println("The following error occured!: " + e);
	            System.exit(0);

			} catch (IOException e) {
				System.out.println("The following error occured!: " + e);
	            System.exit(0);
			}

			x = (key % 16) * 32;
            y = (key / 16) * 32;

			g.drawImage(picture, x, y, 32, 32, null);
		}
    }

    private static void createAndShowGUI() {
        JFrame f = new JFrame("Google images");
        f.setLayout(new BorderLayout());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        GoogleImageBuilder test = new GoogleImageBuilder();
        f.add(test);
        f.validate();
        f.pack();
        f.setVisible(true);
    }

    public static void main(String args[]) {
        Runnable doCreateAndShowGUI = new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        };
        SwingUtilities.invokeLater(doCreateAndShowGUI);
    }
}

Here are the clues hardcoded:

import java.util.HashMap;
import java.util.Map;

public class HardCodedClues {
	private static final int RADIX_16 = 16;

    public static Map getClues() {
    	String [] clues = {
			 "28:Kx7vy-VBtpYJ", "7B:PyDg0PjbgxkJ", "F3:Rd1xXw4alLkJ", "31:FeaBmsxfey8J",

			  "20:YsNAjeTGJVEJ", "A9:leYDU1RuuwIJ", "81:jufN-4x1RnAJ", "CB:IhifTIhRpmoJ",

			  "87:8Maz8upc508J", "FA:5dh9_zqVdkIJ", "BA:xyZftsH8RFsJ", "50:L_zmPN_48bMJ",

			  "49:iLtQE1Q7CWMJ", "D6:TuKm3fTasSYJ", "FE:pls5MhpkpK8J", "60:thDCBIkyQ2EJ",

			  "85:CizK2j2yCBIJ", "23:IkmAcC6lNjIJ", "F8:kRRYwomQWhIJ", "8E:_v-YolETks4J",

			  "E1:Bf8Ltpee0v4J", "D0:0YM5CoDmx8MJ", "92:D9zUj4QVc2sJ", "0C:FsKZkZ1R7t4J",

			  "45:NVX4OCi9WXAJ", "A7:aQAFb1464l4J", "68:aej8Xg6iVhkJ", "A5:EHkO0tMtSN4J",

			  "F6:49-gxPeD0IwJ", "5E:bpLwYqr-990J", "C0:mawuaWJa1boJ", "00:NEU6wRmGVq4J",

			  "28:Kx7vy-VBtpYJ", "7B:PyDg0PjbgxkJ", "F3:Rd1xXw4alLkJ", "31:FeaBmsxfey8J",

			  "20:YsNAjeTGJVEJ", "A9:leYDU1RuuwIJ", "81:jufN-4x1RnAJ", "CB:IhifTIhRpmoJ",

			  "87:8Maz8upc508J", "FA:5dh9_zqVdkIJ", "BA:xyZftsH8RFsJ", "50:L_zmPN_48bMJ",

			  "49:iLtQE1Q7CWMJ", "D6:TuKm3fTasSYJ", "FE:pls5MhpkpK8J", "60:thDCBIkyQ2EJ",

			  "85:CizK2j2yCBIJ", "23:IkmAcC6lNjIJ", "F8:kRRYwomQWhIJ", "8E:_v-YolETks4J",

			  "E1:Bf8Ltpee0v4J", "D0:0YM5CoDmx8MJ", "92:D9zUj4QVc2sJ", "0C:FsKZkZ1R7t4J",

			  "45:NVX4OCi9WXAJ", "A7:aQAFb1464l4J", "68:aej8Xg6iVhkJ", "A5:EHkO0tMtSN4J",

			  "F6:49-gxPeD0IwJ", "5E:bpLwYqr-990J", "C0:mawuaWJa1boJ", "00:NEU6wRmGVq4J",

			  "84:h5LxuUdV03oJ", "D9:-Eup1YnE6-0J", "FC:7fjrc7LzjskJ", "03:LJouzaZ6nakJ",

			  "C3:3cyoIZdesRYJ", "EB:GumIzK6Xwv8J", "12:KYkKkVzbW-UJ", "AB:EHkO0tMtSN4J",

			  "30:vrZQ1rXJEg4J", "CE:AbXWG_eCFZYJ", "6B:EOhnwwtJMz4J", "5F:PyDg0PjbgxkJ",

			  "62:zzxVlWrc94AJ", "F2:CwpwlljQ5RUJ", "7F:9aqBJNfDaUEJ", "EE:CWVFy5sgo9MJ",

			  "4C:JDNgZADQwGkJ", "58:uJB16vUcH38J", "3D:Jij2JFUry5gJ", "C8:v7juRN1cZ4EJ",

			  "1E:16e59DDxd1QJ", "69:y1f3FuaNqhEJ", "B0:4cYEeKseVRYJ", "37:Hnm8AysFIvgJ",

			  "1D:qFpNdOAQ868J", "21:Ypml6jLyoaEJ", "34:UhJBrAWg8MAJ", "A0:xTaArVPF51IJ",

			  "EF:14kwBS4zH4kJ", "ED:eUzfBgFaYAYJ", "51:1RWzov84HGsJ", "72:WRUq1C2UF7YJ",

			  "28:Kx7vy-VBtpYJ", "7B:PyDg0PjbgxkJ", "F3:Rd1xXw4alLkJ", "31:FeaBmsxfey8J",

			  "20:YsNAjeTGJVEJ", "A9:leYDU1RuuwIJ", "81:jufN-4x1RnAJ", "CB:IhifTIhRpmoJ",

			  "87:8Maz8upc508J", "FA:5dh9_zqVdkIJ", "BA:xyZftsH8RFsJ", "50:L_zmPN_48bMJ",

			  "49:iLtQE1Q7CWMJ", "D6:TuKm3fTasSYJ", "FE:pls5MhpkpK8J", "60:thDCBIkyQ2EJ",

			  "85:CizK2j2yCBIJ", "23:IkmAcC6lNjIJ", "F8:kRRYwomQWhIJ", "8E:_v-YolETks4J",

			  "E1:Bf8Ltpee0v4J", "D0:0YM5CoDmx8MJ", "92:D9zUj4QVc2sJ", "0C:FsKZkZ1R7t4J",

			  "45:NVX4OCi9WXAJ", "A7:aQAFb1464l4J", "68:aej8Xg6iVhkJ", "A5:EHkO0tMtSN4J",

			  "F6:49-gxPeD0IwJ", "5E:bpLwYqr-990J", "C0:mawuaWJa1boJ", "00:NEU6wRmGVq4J",

			  "84:h5LxuUdV03oJ", "D9:-Eup1YnE6-0J", "FC:7fjrc7LzjskJ", "03:LJouzaZ6nakJ",

			  "C3:3cyoIZdesRYJ", "EB:GumIzK6Xwv8J", "12:KYkKkVzbW-UJ", "AB:EHkO0tMtSN4J",

			  "30:vrZQ1rXJEg4J", "CE:AbXWG_eCFZYJ", "6B:EOhnwwtJMz4J", "5F:PyDg0PjbgxkJ",

			  "62:zzxVlWrc94AJ", "F2:CwpwlljQ5RUJ", "7F:9aqBJNfDaUEJ", "EE:CWVFy5sgo9MJ",

			  "4C:JDNgZADQwGkJ", "58:uJB16vUcH38J", "3D:Jij2JFUry5gJ", "C8:v7juRN1cZ4EJ",

			  "1E:16e59DDxd1QJ", "69:y1f3FuaNqhEJ", "B0:4cYEeKseVRYJ", "37:Hnm8AysFIvgJ",

			  "1D:qFpNdOAQ868J", "21:Ypml6jLyoaEJ", "34:UhJBrAWg8MAJ", "A0:xTaArVPF51IJ",

			  "EF:14kwBS4zH4kJ", "ED:eUzfBgFaYAYJ", "51:1RWzov84HGsJ", "72:WRUq1C2UF7YJ",

			  "24:TkF3I-3AqsoJ", "6E:Cxb4nb_-6wQJ", "CC:Cfwjydcm5VUJ", "94:3V77coCFoyUJ",

			  "0E:lfqBck_AckoJ", "C7:qTuFDoAZZAIJ", "E4:X9O9H0rf7UwJ", "A1:dh_aVJQg-fQJ",

			  "1B:SSUHFP88ICIJ", "9A:8LRpia1kQyoJ", "A8:6OB8e96ajfIJ", "8D:sekIn_D0FY4J",

			  "9D:sQbLyZCjcMwJ", "9C:at2mlybzsckJ", "2D:gagHFe3EgRcJ", "7C:sbERUkuwrgkJ",

			  "DF:jIbY1S4jGOQJ", "15:sQ5gc2skXDoJ", "B5:qpQ_jThwIfwJ", "E7:bujGGLRs6f8J",

			  "B9:FC8WDc6FfQAJ", "AC:7PqlCpunKtMJ", "90:6bCXBm-ZoeMJ", "05:kUDeAWh_o54J",

			  "46:BTA_qt_HhUkJ", "3E:3fscTyozTmYJ", "14:dsppEkkJ6vIJ", "AA:q8Xw6c1aDJQJ",

			  "5C:1Yy-Elilf_wJ", "67:sYb7HA_7lCwJ", "99:kPiuzGwRUKIJ", "E2:3NCEoBX-P-gJ",

			  "28:Kx7vy-VBtpYJ", "7B:PyDg0PjbgxkJ", "F3:Rd1xXw4alLkJ", "31:FeaBmsxfey8J",

			  "20:YsNAjeTGJVEJ", "A9:leYDU1RuuwIJ", "81:jufN-4x1RnAJ", "CB:IhifTIhRpmoJ",

			  "87:8Maz8upc508J", "FA:5dh9_zqVdkIJ", "BA:xyZftsH8RFsJ", "50:L_zmPN_48bMJ",

			  "49:iLtQE1Q7CWMJ", "D6:TuKm3fTasSYJ", "FE:pls5MhpkpK8J", "60:thDCBIkyQ2EJ",

			  "85:CizK2j2yCBIJ", "23:IkmAcC6lNjIJ", "F8:kRRYwomQWhIJ", "8E:_v-YolETks4J",

			  "E1:Bf8Ltpee0v4J", "D0:0YM5CoDmx8MJ", "92:D9zUj4QVc2sJ", "0C:FsKZkZ1R7t4J",

			  "45:NVX4OCi9WXAJ", "A7:aQAFb1464l4J", "68:aej8Xg6iVhkJ", "A5:EHkO0tMtSN4J",

			  "F6:49-gxPeD0IwJ", "5E:bpLwYqr-990J", "C0:mawuaWJa1boJ", "00:NEU6wRmGVq4J",

			  "84:h5LxuUdV03oJ", "D9:-Eup1YnE6-0J", "FC:7fjrc7LzjskJ", "03:LJouzaZ6nakJ",

			  "C3:3cyoIZdesRYJ", "EB:GumIzK6Xwv8J", "12:KYkKkVzbW-UJ", "AB:EHkO0tMtSN4J",

			  "30:vrZQ1rXJEg4J", "CE:AbXWG_eCFZYJ", "6B:EOhnwwtJMz4J", "5F:PyDg0PjbgxkJ",

			  "62:zzxVlWrc94AJ", "F2:CwpwlljQ5RUJ", "7F:9aqBJNfDaUEJ", "EE:CWVFy5sgo9MJ",

			  "4C:JDNgZADQwGkJ", "58:uJB16vUcH38J", "3D:Jij2JFUry5gJ", "C8:v7juRN1cZ4EJ",

			  "1E:16e59DDxd1QJ", "69:y1f3FuaNqhEJ", "B0:4cYEeKseVRYJ", "37:Hnm8AysFIvgJ",

			  "1D:qFpNdOAQ868J", "21:Ypml6jLyoaEJ", "34:UhJBrAWg8MAJ", "A0:xTaArVPF51IJ",

			  "EF:14kwBS4zH4kJ", "ED:eUzfBgFaYAYJ", "51:1RWzov84HGsJ", "72:WRUq1C2UF7YJ",

			  "24:TkF3I-3AqsoJ", "6E:Cxb4nb_-6wQJ", "CC:Cfwjydcm5VUJ", "94:3V77coCFoyUJ",

			  "0E:lfqBck_AckoJ", "C7:qTuFDoAZZAIJ", "E4:X9O9H0rf7UwJ", "A1:dh_aVJQg-fQJ",

			  "1B:SSUHFP88ICIJ", "9A:8LRpia1kQyoJ", "A8:6OB8e96ajfIJ", "8D:sekIn_D0FY4J",

			  "9D:sQbLyZCjcMwJ", "9C:at2mlybzsckJ", "2D:gagHFe3EgRcJ", "7C:sbERUkuwrgkJ",

			  "DF:jIbY1S4jGOQJ", "15:sQ5gc2skXDoJ", "B5:qpQ_jThwIfwJ", "E7:bujGGLRs6f8J",

			  "B9:FC8WDc6FfQAJ", "AC:7PqlCpunKtMJ", "90:6bCXBm-ZoeMJ", "05:kUDeAWh_o54J",

			  "46:BTA_qt_HhUkJ", "3E:3fscTyozTmYJ", "14:dsppEkkJ6vIJ", "AA:q8Xw6c1aDJQJ",

			  "5C:1Yy-Elilf_wJ", "67:sYb7HA_7lCwJ", "99:kPiuzGwRUKIJ", "E2:3NCEoBX-P-gJ",

			  "7D:gOHngHfzX8YJ", "2C:1j66DXvpuf8J", "07:rWnu8lphvZcJ", "CA:PFnVqjCPIKIJ",

			  "D7:ydg0kWMXDeoJ", "5A:n33l1U6zbEIJ", "57:hKzAPG_v3g4J", "3F:XGwx9FgVixQJ",

			  "36:abLa4yOIyW4J", "10:sMDENsqLts0J", "D2:gJsgjLw3nSQJ", "E0:Z_K4hdZq7h8J",

			  "75:3C-XszC6kyMJ", "77:rtHRfe9A27YJ", "97:-eflxIU8g04J", "F0:SwVcyzirnnQJ",

			  "65:7-hjyVDnHjEJ", "A3:hruuOW22_L8J", "F9:lEPeKx6ket4J", "88:M3DdYOcDTWwJ",

			  "FD:Cfwjydcm5VUJ", "42:HNXS6FBy7AUJ", "4A:-n4dEXmbmyMJ", "F7:IPDuatbjjRoJ",

			  "52:2xrwdGDvyaEJ", "5B:vEMG_nMaZygJ", "27:hfidgDDREMsJ", "70:DJ1-bAcdQE8J",

			  "78:aej8Xg6iVhkJ", "61:4SHBf-6ctTQJ", "BF:oJWMF8g4n98J", "3C:wbyliY5Fw7cJ",

			  "28:Kx7vy-VBtpYJ", "7B:PyDg0PjbgxkJ", "F3:Rd1xXw4alLkJ", "31:FeaBmsxfey8J",

			  "20:YsNAjeTGJVEJ", "A9:leYDU1RuuwIJ", "81:jufN-4x1RnAJ", "CB:IhifTIhRpmoJ",

			  "87:8Maz8upc508J", "FA:5dh9_zqVdkIJ", "BA:xyZftsH8RFsJ", "50:L_zmPN_48bMJ",

			  "49:iLtQE1Q7CWMJ", "D6:TuKm3fTasSYJ", "FE:pls5MhpkpK8J", "60:thDCBIkyQ2EJ",

			  "85:CizK2j2yCBIJ", "23:IkmAcC6lNjIJ", "F8:kRRYwomQWhIJ", "8E:_v-YolETks4J",

			  "E1:Bf8Ltpee0v4J", "D0:0YM5CoDmx8MJ", "92:D9zUj4QVc2sJ", "0C:FsKZkZ1R7t4J",

			  "45:NVX4OCi9WXAJ", "A7:aQAFb1464l4J", "68:aej8Xg6iVhkJ", "A5:EHkO0tMtSN4J",

			  "F6:49-gxPeD0IwJ", "5E:bpLwYqr-990J", "C0:mawuaWJa1boJ", "00:NEU6wRmGVq4J",

			  "84:h5LxuUdV03oJ", "D9:-Eup1YnE6-0J", "FC:7fjrc7LzjskJ", "03:LJouzaZ6nakJ",

			  "C3:3cyoIZdesRYJ", "EB:GumIzK6Xwv8J", "12:KYkKkVzbW-UJ", "AB:EHkO0tMtSN4J",

			  "30:vrZQ1rXJEg4J", "CE:AbXWG_eCFZYJ", "6B:EOhnwwtJMz4J", "5F:PyDg0PjbgxkJ",

			  "62:zzxVlWrc94AJ", "F2:CwpwlljQ5RUJ", "7F:9aqBJNfDaUEJ", "EE:CWVFy5sgo9MJ",

			  "4C:JDNgZADQwGkJ", "58:uJB16vUcH38J", "3D:Jij2JFUry5gJ", "C8:v7juRN1cZ4EJ",

			  "1E:16e59DDxd1QJ", "69:y1f3FuaNqhEJ", "B0:4cYEeKseVRYJ", "37:Hnm8AysFIvgJ",

			  "1D:qFpNdOAQ868J", "21:Ypml6jLyoaEJ", "34:UhJBrAWg8MAJ", "A0:xTaArVPF51IJ",

			  "EF:14kwBS4zH4kJ", "ED:eUzfBgFaYAYJ", "51:1RWzov84HGsJ", "72:WRUq1C2UF7YJ",

			  "24:TkF3I-3AqsoJ", "6E:Cxb4nb_-6wQJ", "CC:Cfwjydcm5VUJ", "94:3V77coCFoyUJ",

			  "0E:lfqBck_AckoJ", "C7:qTuFDoAZZAIJ", "E4:X9O9H0rf7UwJ", "A1:dh_aVJQg-fQJ",

			  "1B:SSUHFP88ICIJ", "9A:8LRpia1kQyoJ", "A8:6OB8e96ajfIJ", "8D:sekIn_D0FY4J",

			  "9D:sQbLyZCjcMwJ", "9C:at2mlybzsckJ", "2D:gagHFe3EgRcJ", "7C:sbERUkuwrgkJ",

			  "DF:jIbY1S4jGOQJ", "15:sQ5gc2skXDoJ", "B5:qpQ_jThwIfwJ", "E7:bujGGLRs6f8J",

			  "B9:FC8WDc6FfQAJ", "AC:7PqlCpunKtMJ", "90:6bCXBm-ZoeMJ", "05:kUDeAWh_o54J",

			  "46:BTA_qt_HhUkJ", "3E:3fscTyozTmYJ", "14:dsppEkkJ6vIJ", "AA:q8Xw6c1aDJQJ",

			  "5C:1Yy-Elilf_wJ", "67:sYb7HA_7lCwJ", "99:kPiuzGwRUKIJ", "E2:3NCEoBX-P-gJ",

			  "7D:gOHngHfzX8YJ", "2C:1j66DXvpuf8J", "07:rWnu8lphvZcJ", "CA:PFnVqjCPIKIJ",

			  "D7:ydg0kWMXDeoJ", "5A:n33l1U6zbEIJ", "57:hKzAPG_v3g4J", "3F:XGwx9FgVixQJ",

			  "36:abLa4yOIyW4J", "10:sMDENsqLts0J", "D2:gJsgjLw3nSQJ", "E0:Z_K4hdZq7h8J",

			  "75:3C-XszC6kyMJ", "77:rtHRfe9A27YJ", "97:-eflxIU8g04J", "F0:SwVcyzirnnQJ",

			  "65:7-hjyVDnHjEJ", "A3:hruuOW22_L8J", "F9:lEPeKx6ket4J", "88:M3DdYOcDTWwJ",

			  "FD:Cfwjydcm5VUJ", "42:HNXS6FBy7AUJ", "4A:-n4dEXmbmyMJ", "F7:IPDuatbjjRoJ",

			  "52:2xrwdGDvyaEJ", "5B:vEMG_nMaZygJ", "27:hfidgDDREMsJ", "70:DJ1-bAcdQE8J",

			  "78:aej8Xg6iVhkJ", "61:4SHBf-6ctTQJ", "BF:oJWMF8g4n98J", "3C:wbyliY5Fw7cJ",

			  "BD:dyCQwPoi_QoJ", "6D:ROtjpJaaM3AJ", "D1:u77vCPi7NX4J", "DA:jr7lZOIXHE4J",

			  "80:hvDWT2JOOHIJ", "48:hfidgDDREMsJ", "8B:TMDNdWekwlEJ", "73:13SgY09fd9QJ",

			  "7E:tx3mt6GYR5oJ", "B3:JTLUoSksUFAJ", "63:DyaRtEUhtg4J", "0B:1q-lkiRNa7kJ",

			  "93:L3R7clDgu44J", "F5:o-lzUg48_5UJ", "01:6YJy7S3OGt8J", "26:vs3cbrj7Bi0J",

			  "7A:C6L9XGQS6i8J", "A4:iMWi3RNzLHUJ", "8F:2ELg7mGC5yUJ", "1A:rZEfAj2wHgYJ",

			  "A6:kP-bP5hmF9cJ", "18:KcznZAsaAk0J", "FF:kp-fx5_9CzsJ", "6C:5I-cinT_0_4J",

			  "8C:zaUGihUPAJwJ", "0F:AIt7UQkMmZkJ", "9B:FYtibg-zD20J", "B7:A2SiPb4NwWQJ",

			  "89:isoWmMm2QssJ", "C5:CibMCkpl2kAJ", "0D:g9lghtXOAFMJ", "38:gWcquZBJFPIJ",

			  "28:Kx7vy-VBtpYJ", "7B:PyDg0PjbgxkJ", "F3:Rd1xXw4alLkJ", "31:FeaBmsxfey8J",

			  "20:YsNAjeTGJVEJ", "A9:leYDU1RuuwIJ", "81:jufN-4x1RnAJ", "CB:IhifTIhRpmoJ",

			  "87:8Maz8upc508J", "FA:5dh9_zqVdkIJ", "BA:xyZftsH8RFsJ", "50:L_zmPN_48bMJ",

			  "49:iLtQE1Q7CWMJ", "D6:TuKm3fTasSYJ", "FE:pls5MhpkpK8J", "60:thDCBIkyQ2EJ",

			  "85:CizK2j2yCBIJ", "23:IkmAcC6lNjIJ", "F8:kRRYwomQWhIJ", "8E:_v-YolETks4J",

			  "E1:Bf8Ltpee0v4J", "D0:0YM5CoDmx8MJ", "92:D9zUj4QVc2sJ", "0C:FsKZkZ1R7t4J",

			  "45:NVX4OCi9WXAJ", "A7:aQAFb1464l4J", "68:aej8Xg6iVhkJ", "A5:EHkO0tMtSN4J",

			  "F6:49-gxPeD0IwJ", "5E:bpLwYqr-990J", "C0:mawuaWJa1boJ", "00:NEU6wRmGVq4J",

			  "84:h5LxuUdV03oJ", "D9:-Eup1YnE6-0J", "FC:7fjrc7LzjskJ", "03:LJouzaZ6nakJ",

			  "C3:3cyoIZdesRYJ", "EB:GumIzK6Xwv8J", "12:KYkKkVzbW-UJ", "AB:EHkO0tMtSN4J",

			  "30:vrZQ1rXJEg4J", "CE:AbXWG_eCFZYJ", "6B:EOhnwwtJMz4J", "5F:PyDg0PjbgxkJ",

			  "62:zzxVlWrc94AJ", "F2:CwpwlljQ5RUJ", "7F:9aqBJNfDaUEJ", "EE:CWVFy5sgo9MJ",

			  "4C:JDNgZADQwGkJ", "58:uJB16vUcH38J", "3D:Jij2JFUry5gJ", "C8:v7juRN1cZ4EJ",

			  "1E:16e59DDxd1QJ", "69:y1f3FuaNqhEJ", "B0:4cYEeKseVRYJ", "37:Hnm8AysFIvgJ",

			  "1D:qFpNdOAQ868J", "21:Ypml6jLyoaEJ", "34:UhJBrAWg8MAJ", "A0:xTaArVPF51IJ",

			  "EF:14kwBS4zH4kJ", "ED:eUzfBgFaYAYJ", "51:1RWzov84HGsJ", "72:WRUq1C2UF7YJ",

			  "24:TkF3I-3AqsoJ", "6E:Cxb4nb_-6wQJ", "CC:Cfwjydcm5VUJ", "94:3V77coCFoyUJ",

			  "0E:lfqBck_AckoJ", "C7:qTuFDoAZZAIJ", "E4:X9O9H0rf7UwJ", "A1:dh_aVJQg-fQJ",

			  "1B:SSUHFP88ICIJ", "9A:8LRpia1kQyoJ", "A8:6OB8e96ajfIJ", "8D:sekIn_D0FY4J",

			  "9D:sQbLyZCjcMwJ", "9C:at2mlybzsckJ", "2D:gagHFe3EgRcJ", "7C:sbERUkuwrgkJ",

			  "DF:jIbY1S4jGOQJ", "15:sQ5gc2skXDoJ", "B5:qpQ_jThwIfwJ", "E7:bujGGLRs6f8J",

			  "B9:FC8WDc6FfQAJ", "AC:7PqlCpunKtMJ", "90:6bCXBm-ZoeMJ", "05:kUDeAWh_o54J",

			  "46:BTA_qt_HhUkJ", "3E:3fscTyozTmYJ", "14:dsppEkkJ6vIJ", "AA:q8Xw6c1aDJQJ",

			  "5C:1Yy-Elilf_wJ", "67:sYb7HA_7lCwJ", "99:kPiuzGwRUKIJ", "E2:3NCEoBX-P-gJ",

			  "7D:gOHngHfzX8YJ", "2C:1j66DXvpuf8J", "07:rWnu8lphvZcJ", "CA:PFnVqjCPIKIJ",

			  "D7:ydg0kWMXDeoJ", "5A:n33l1U6zbEIJ", "57:hKzAPG_v3g4J", "3F:XGwx9FgVixQJ",

			  "36:abLa4yOIyW4J", "10:sMDENsqLts0J", "D2:gJsgjLw3nSQJ", "E0:Z_K4hdZq7h8J",

			  "75:3C-XszC6kyMJ", "77:rtHRfe9A27YJ", "97:-eflxIU8g04J", "F0:SwVcyzirnnQJ",

			  "65:7-hjyVDnHjEJ", "A3:hruuOW22_L8J", "F9:lEPeKx6ket4J", "88:M3DdYOcDTWwJ",

			  "FD:Cfwjydcm5VUJ", "42:HNXS6FBy7AUJ", "4A:-n4dEXmbmyMJ", "F7:IPDuatbjjRoJ",

			  "52:2xrwdGDvyaEJ", "5B:vEMG_nMaZygJ", "27:hfidgDDREMsJ", "70:DJ1-bAcdQE8J",

			  "78:aej8Xg6iVhkJ", "61:4SHBf-6ctTQJ", "BF:oJWMF8g4n98J", "3C:wbyliY5Fw7cJ",

			  "BD:dyCQwPoi_QoJ", "6D:ROtjpJaaM3AJ", "D1:u77vCPi7NX4J", "DA:jr7lZOIXHE4J",

			  "80:hvDWT2JOOHIJ", "48:hfidgDDREMsJ", "8B:TMDNdWekwlEJ", "73:13SgY09fd9QJ",

			  "7E:tx3mt6GYR5oJ", "B3:JTLUoSksUFAJ", "63:DyaRtEUhtg4J", "0B:1q-lkiRNa7kJ",

			  "93:L3R7clDgu44J", "F5:o-lzUg48_5UJ", "01:6YJy7S3OGt8J", "26:vs3cbrj7Bi0J",

			  "7A:C6L9XGQS6i8J", "A4:iMWi3RNzLHUJ", "8F:2ELg7mGC5yUJ", "1A:rZEfAj2wHgYJ",

			  "A6:kP-bP5hmF9cJ", "18:KcznZAsaAk0J", "FF:kp-fx5_9CzsJ", "6C:5I-cinT_0_4J",

			  "8C:zaUGihUPAJwJ", "0F:AIt7UQkMmZkJ", "9B:FYtibg-zD20J", "B7:A2SiPb4NwWQJ",

			  "89:isoWmMm2QssJ", "C5:CibMCkpl2kAJ", "0D:g9lghtXOAFMJ", "38:gWcquZBJFPIJ",

			  "11:_TotMQXuZdgJ", "6A:24tx-mIpwmwJ", "8A:-FAi_YRhbrgJ", "1F:kQEIlYRdi2QJ",

			  "41:FoWhbx2IxiUJ", "4E:XFlq06C_YeMJ", "13:vKSdbEgs9aoJ", "D5:m415gDZ2B-UJ",

			  "AD:hWBrF9YLK78J", "2B:HqEtb7AADmMJ", "DE:eCFYrqg0WsEJ", "16:QLTeOYg-yB8J",

			  "BB:jk-d_Ua8BtIJ", "C9:u0J9izcWwvAJ", "91:NM3JgAgreZQJ", "04:sYb7HA_7lCwJ",

			  "08:VwTb3zO7tL8J", "DD:kwlsYdonpIgJ", "F4:_LIV45PtumsJ", "EC:rpMyxZmO6LwJ",

			  "54:bDNO5Nsce9EJ", "2A:XFgcPHiQ6SYJ", "71:b8UD6gHfO6IJ", "9E:rwusJNoWjiQJ",

			  "FB:c-wHLtEdkf0J", "AE:brdZuMrvAMQJ", "E8:mzaxbrVLBOUJ", "4B:6ye4zJaCE_oJ",

			  "22:6fwxGNK3bJgJ", "B8:jwvALeEfsbcJ", "33:Yqx6f_8RvNgJ", "C2:sxB_6QG_Qb4J",

			  "28:Kx7vy-VBtpYJ", "7B:PyDg0PjbgxkJ", "F3:Rd1xXw4alLkJ", "31:FeaBmsxfey8J",

			  "20:YsNAjeTGJVEJ", "A9:leYDU1RuuwIJ", "81:jufN-4x1RnAJ", "CB:IhifTIhRpmoJ",

			  "87:8Maz8upc508J", "FA:5dh9_zqVdkIJ", "BA:xyZftsH8RFsJ", "50:L_zmPN_48bMJ",

			  "49:iLtQE1Q7CWMJ", "D6:TuKm3fTasSYJ", "FE:pls5MhpkpK8J", "60:thDCBIkyQ2EJ",

			  "85:CizK2j2yCBIJ", "23:IkmAcC6lNjIJ", "F8:kRRYwomQWhIJ", "8E:_v-YolETks4J",

			  "E1:Bf8Ltpee0v4J", "D0:0YM5CoDmx8MJ", "92:D9zUj4QVc2sJ", "0C:FsKZkZ1R7t4J",

			  "45:NVX4OCi9WXAJ", "A7:aQAFb1464l4J", "68:aej8Xg6iVhkJ", "A5:EHkO0tMtSN4J",

			  "F6:49-gxPeD0IwJ", "5E:bpLwYqr-990J", "C0:mawuaWJa1boJ", "00:NEU6wRmGVq4J",

			  "84:h5LxuUdV03oJ", "D9:-Eup1YnE6-0J", "FC:7fjrc7LzjskJ", "03:LJouzaZ6nakJ",

			  "C3:3cyoIZdesRYJ", "EB:GumIzK6Xwv8J", "12:KYkKkVzbW-UJ", "AB:EHkO0tMtSN4J",

			  "30:vrZQ1rXJEg4J", "CE:AbXWG_eCFZYJ", "6B:EOhnwwtJMz4J", "5F:PyDg0PjbgxkJ",

			  "62:zzxVlWrc94AJ", "F2:CwpwlljQ5RUJ", "7F:9aqBJNfDaUEJ", "EE:CWVFy5sgo9MJ",

			  "4C:JDNgZADQwGkJ", "58:uJB16vUcH38J", "3D:Jij2JFUry5gJ", "C8:v7juRN1cZ4EJ",

			  "1E:16e59DDxd1QJ", "69:y1f3FuaNqhEJ", "B0:4cYEeKseVRYJ", "37:Hnm8AysFIvgJ",

			  "1D:qFpNdOAQ868J", "21:Ypml6jLyoaEJ", "34:UhJBrAWg8MAJ", "A0:xTaArVPF51IJ",

			  "EF:14kwBS4zH4kJ", "ED:eUzfBgFaYAYJ", "51:1RWzov84HGsJ", "72:WRUq1C2UF7YJ",

			  "24:TkF3I-3AqsoJ", "6E:Cxb4nb_-6wQJ", "CC:Cfwjydcm5VUJ", "94:3V77coCFoyUJ",

			  "0E:lfqBck_AckoJ", "C7:qTuFDoAZZAIJ", "E4:X9O9H0rf7UwJ", "A1:dh_aVJQg-fQJ",

			  "1B:SSUHFP88ICIJ", "9A:8LRpia1kQyoJ", "A8:6OB8e96ajfIJ", "8D:sekIn_D0FY4J",

			  "9D:sQbLyZCjcMwJ", "9C:at2mlybzsckJ", "2D:gagHFe3EgRcJ", "7C:sbERUkuwrgkJ",

			  "DF:jIbY1S4jGOQJ", "15:sQ5gc2skXDoJ", "B5:qpQ_jThwIfwJ", "E7:bujGGLRs6f8J",

			  "B9:FC8WDc6FfQAJ", "AC:7PqlCpunKtMJ", "90:6bCXBm-ZoeMJ", "05:kUDeAWh_o54J",

			  "46:BTA_qt_HhUkJ", "3E:3fscTyozTmYJ", "14:dsppEkkJ6vIJ", "AA:q8Xw6c1aDJQJ",

			  "5C:1Yy-Elilf_wJ", "67:sYb7HA_7lCwJ", "99:kPiuzGwRUKIJ", "E2:3NCEoBX-P-gJ",

			  "7D:gOHngHfzX8YJ", "2C:1j66DXvpuf8J", "07:rWnu8lphvZcJ", "CA:PFnVqjCPIKIJ",

			  "D7:ydg0kWMXDeoJ", "5A:n33l1U6zbEIJ", "57:hKzAPG_v3g4J", "3F:XGwx9FgVixQJ",

			  "36:abLa4yOIyW4J", "10:sMDENsqLts0J", "D2:gJsgjLw3nSQJ", "E0:Z_K4hdZq7h8J",

			  "75:3C-XszC6kyMJ", "77:rtHRfe9A27YJ", "97:-eflxIU8g04J", "F0:SwVcyzirnnQJ",

			  "65:7-hjyVDnHjEJ", "A3:hruuOW22_L8J", "F9:lEPeKx6ket4J", "88:M3DdYOcDTWwJ",

			  "FD:Cfwjydcm5VUJ", "42:HNXS6FBy7AUJ", "4A:-n4dEXmbmyMJ", "F7:IPDuatbjjRoJ",

			  "52:2xrwdGDvyaEJ", "5B:vEMG_nMaZygJ", "27:hfidgDDREMsJ", "70:DJ1-bAcdQE8J",

			  "78:aej8Xg6iVhkJ", "61:4SHBf-6ctTQJ", "BF:oJWMF8g4n98J", "3C:wbyliY5Fw7cJ",

			  "BD:dyCQwPoi_QoJ", "6D:ROtjpJaaM3AJ", "D1:u77vCPi7NX4J", "DA:jr7lZOIXHE4J",

			  "80:hvDWT2JOOHIJ", "48:hfidgDDREMsJ", "8B:TMDNdWekwlEJ", "73:13SgY09fd9QJ",

			  "7E:tx3mt6GYR5oJ", "B3:JTLUoSksUFAJ", "63:DyaRtEUhtg4J", "0B:1q-lkiRNa7kJ",

			  "93:L3R7clDgu44J", "F5:o-lzUg48_5UJ", "01:6YJy7S3OGt8J", "26:vs3cbrj7Bi0J",

			  "7A:C6L9XGQS6i8J", "A4:iMWi3RNzLHUJ", "8F:2ELg7mGC5yUJ", "1A:rZEfAj2wHgYJ",

			  "A6:kP-bP5hmF9cJ", "18:KcznZAsaAk0J", "FF:kp-fx5_9CzsJ", "6C:5I-cinT_0_4J",

			  "8C:zaUGihUPAJwJ", "0F:AIt7UQkMmZkJ", "9B:FYtibg-zD20J", "B7:A2SiPb4NwWQJ",

			  "89:isoWmMm2QssJ", "C5:CibMCkpl2kAJ", "0D:g9lghtXOAFMJ", "38:gWcquZBJFPIJ",

			  "11:_TotMQXuZdgJ", "6A:24tx-mIpwmwJ", "8A:-FAi_YRhbrgJ", "1F:kQEIlYRdi2QJ",

			  "41:FoWhbx2IxiUJ", "4E:XFlq06C_YeMJ", "13:vKSdbEgs9aoJ", "D5:m415gDZ2B-UJ",

			  "AD:hWBrF9YLK78J", "2B:HqEtb7AADmMJ", "DE:eCFYrqg0WsEJ", "16:QLTeOYg-yB8J",

			  "BB:jk-d_Ua8BtIJ", "C9:u0J9izcWwvAJ", "91:NM3JgAgreZQJ", "04:sYb7HA_7lCwJ",

			  "08:VwTb3zO7tL8J", "DD:kwlsYdonpIgJ", "F4:_LIV45PtumsJ", "EC:rpMyxZmO6LwJ",

			  "54:bDNO5Nsce9EJ", "2A:XFgcPHiQ6SYJ", "71:b8UD6gHfO6IJ", "9E:rwusJNoWjiQJ",

			  "FB:c-wHLtEdkf0J", "AE:brdZuMrvAMQJ", "E8:mzaxbrVLBOUJ", "4B:6ye4zJaCE_oJ",

			  "22:6fwxGNK3bJgJ", "B8:jwvALeEfsbcJ", "33:Yqx6f_8RvNgJ", "C2:sxB_6QG_Qb4J",

			  "C1:RDCyEcRfSgYJ", "AF:crKfJjXw_5MJ", "53:0DMvhUWTKb8J", "DC:e91ckrM89cEJ",

			  "B4:WDoT2alpGIUJ", "4D:bFmzm_4qu2kJ", "C4:0vD_dZABzHMJ", "1C:sz33mz3wOUQJ",

			  "C6:E7OZk_UQ4CkJ", "98:cLfnrVphpKwJ", "06:oZjZXvq5HtcJ", "64:iVU_MpjctIIJ",

			  "9F:X-bZvp5icB4J", "95:fNZpE0lgEn8J", "A2:RxHFAXHJtyAJ", "E3:6HrAalsLflAJ",

			  "3B:KqNbjwDqqCMJ", "BE:0RUKM-BlaPsJ", "0A:1q-lkiRNa7kJ", "76:m1ROBo00yiEJ",

			  "32:SkX0iYQQY_AJ", "40:vl95ZOX_Q4IJ", "09:ep_I291MX2cJ", "2F:2H1dZ2lZ248J",

			  "D4:N-GQv-Nx1z8J", "19:f561wYNWgkEJ", "D3:UON-_Wr60O8J", "74:KQJPHtyNoj4J",

			  "3A:lzls7PYvd34J", "F1:yQFSR4MaUpwJ", "CF:oRRo9yGdXSQJ", "4F:W6-iAz1ZqaUJ",

			  "28:Kx7vy-VBtpYJ", "7B:PyDg0PjbgxkJ", "F3:Rd1xXw4alLkJ", "31:FeaBmsxfey8J",

			  "20:YsNAjeTGJVEJ", "A9:leYDU1RuuwIJ", "81:jufN-4x1RnAJ", "CB:IhifTIhRpmoJ",

			  "87:8Maz8upc508J", "FA:5dh9_zqVdkIJ", "BA:xyZftsH8RFsJ", "50:L_zmPN_48bMJ",

			  "49:iLtQE1Q7CWMJ", "D6:TuKm3fTasSYJ", "FE:pls5MhpkpK8J", "60:thDCBIkyQ2EJ",

			  "85:CizK2j2yCBIJ", "23:IkmAcC6lNjIJ", "F8:kRRYwomQWhIJ", "8E:_v-YolETks4J",

			  "E1:Bf8Ltpee0v4J", "D0:0YM5CoDmx8MJ", "92:D9zUj4QVc2sJ", "0C:FsKZkZ1R7t4J",

			  "45:NVX4OCi9WXAJ", "A7:aQAFb1464l4J", "68:aej8Xg6iVhkJ", "A5:EHkO0tMtSN4J",

			  "F6:49-gxPeD0IwJ", "5E:bpLwYqr-990J", "C0:mawuaWJa1boJ", "00:NEU6wRmGVq4J",

			  "84:h5LxuUdV03oJ", "D9:-Eup1YnE6-0J", "FC:7fjrc7LzjskJ", "03:LJouzaZ6nakJ",

			  "C3:3cyoIZdesRYJ", "EB:GumIzK6Xwv8J", "12:KYkKkVzbW-UJ", "AB:EHkO0tMtSN4J",

			  "30:vrZQ1rXJEg4J", "CE:AbXWG_eCFZYJ", "6B:EOhnwwtJMz4J", "5F:PyDg0PjbgxkJ",

			  "62:zzxVlWrc94AJ", "F2:CwpwlljQ5RUJ", "7F:9aqBJNfDaUEJ", "EE:CWVFy5sgo9MJ",

			  "4C:JDNgZADQwGkJ", "58:uJB16vUcH38J", "3D:Jij2JFUry5gJ", "C8:v7juRN1cZ4EJ",

			  "1E:16e59DDxd1QJ", "69:y1f3FuaNqhEJ", "B0:4cYEeKseVRYJ", "37:Hnm8AysFIvgJ",

			  "1D:qFpNdOAQ868J", "21:Ypml6jLyoaEJ", "34:UhJBrAWg8MAJ", "A0:xTaArVPF51IJ",

			  "EF:14kwBS4zH4kJ", "ED:eUzfBgFaYAYJ", "51:1RWzov84HGsJ", "72:WRUq1C2UF7YJ",

			  "24:TkF3I-3AqsoJ", "6E:Cxb4nb_-6wQJ", "CC:Cfwjydcm5VUJ", "94:3V77coCFoyUJ",

			  "0E:lfqBck_AckoJ", "C7:qTuFDoAZZAIJ", "E4:X9O9H0rf7UwJ", "A1:dh_aVJQg-fQJ",

			  "1B:SSUHFP88ICIJ", "9A:8LRpia1kQyoJ", "A8:6OB8e96ajfIJ", "8D:sekIn_D0FY4J",

			  "9D:sQbLyZCjcMwJ", "9C:at2mlybzsckJ", "2D:gagHFe3EgRcJ", "7C:sbERUkuwrgkJ",

			  "DF:jIbY1S4jGOQJ", "15:sQ5gc2skXDoJ", "B5:qpQ_jThwIfwJ", "E7:bujGGLRs6f8J",

			  "B9:FC8WDc6FfQAJ", "AC:7PqlCpunKtMJ", "90:6bCXBm-ZoeMJ", "05:kUDeAWh_o54J",

			  "46:BTA_qt_HhUkJ", "3E:3fscTyozTmYJ", "14:dsppEkkJ6vIJ", "AA:q8Xw6c1aDJQJ",

			  "5C:1Yy-Elilf_wJ", "67:sYb7HA_7lCwJ", "99:kPiuzGwRUKIJ", "E2:3NCEoBX-P-gJ",

			  "7D:gOHngHfzX8YJ", "2C:1j66DXvpuf8J", "07:rWnu8lphvZcJ", "CA:PFnVqjCPIKIJ",

			  "D7:ydg0kWMXDeoJ", "5A:n33l1U6zbEIJ", "57:hKzAPG_v3g4J", "3F:XGwx9FgVixQJ",

			  "36:abLa4yOIyW4J", "10:sMDENsqLts0J", "D2:gJsgjLw3nSQJ", "E0:Z_K4hdZq7h8J",

			  "75:3C-XszC6kyMJ", "77:rtHRfe9A27YJ", "97:-eflxIU8g04J", "F0:SwVcyzirnnQJ",

			  "65:7-hjyVDnHjEJ", "A3:hruuOW22_L8J", "F9:lEPeKx6ket4J", "88:M3DdYOcDTWwJ",

			  "FD:Cfwjydcm5VUJ", "42:HNXS6FBy7AUJ", "4A:-n4dEXmbmyMJ", "F7:IPDuatbjjRoJ",

			  "52:2xrwdGDvyaEJ", "5B:vEMG_nMaZygJ", "27:hfidgDDREMsJ", "70:DJ1-bAcdQE8J",

			  "78:aej8Xg6iVhkJ", "61:4SHBf-6ctTQJ", "BF:oJWMF8g4n98J", "3C:wbyliY5Fw7cJ",

			  "BD:dyCQwPoi_QoJ", "6D:ROtjpJaaM3AJ", "D1:u77vCPi7NX4J", "DA:jr7lZOIXHE4J",

			  "80:hvDWT2JOOHIJ", "48:hfidgDDREMsJ", "8B:TMDNdWekwlEJ", "73:13SgY09fd9QJ",

			  "7E:tx3mt6GYR5oJ", "B3:JTLUoSksUFAJ", "63:DyaRtEUhtg4J", "0B:1q-lkiRNa7kJ",

			  "93:L3R7clDgu44J", "F5:o-lzUg48_5UJ", "01:6YJy7S3OGt8J", "26:vs3cbrj7Bi0J",

			  "7A:C6L9XGQS6i8J", "A4:iMWi3RNzLHUJ", "8F:2ELg7mGC5yUJ", "1A:rZEfAj2wHgYJ",

			  "A6:kP-bP5hmF9cJ", "18:KcznZAsaAk0J", "FF:kp-fx5_9CzsJ", "6C:5I-cinT_0_4J",

			  "8C:zaUGihUPAJwJ", "0F:AIt7UQkMmZkJ", "9B:FYtibg-zD20J", "B7:A2SiPb4NwWQJ",

			  "89:isoWmMm2QssJ", "C5:CibMCkpl2kAJ", "0D:g9lghtXOAFMJ", "38:gWcquZBJFPIJ",

			  "11:_TotMQXuZdgJ", "6A:24tx-mIpwmwJ", "8A:-FAi_YRhbrgJ", "1F:kQEIlYRdi2QJ",

			  "41:FoWhbx2IxiUJ", "4E:XFlq06C_YeMJ", "13:vKSdbEgs9aoJ", "D5:m415gDZ2B-UJ",

			  "AD:hWBrF9YLK78J", "2B:HqEtb7AADmMJ", "DE:eCFYrqg0WsEJ", "16:QLTeOYg-yB8J",

			  "BB:jk-d_Ua8BtIJ", "C9:u0J9izcWwvAJ", "91:NM3JgAgreZQJ", "04:sYb7HA_7lCwJ",

			  "08:VwTb3zO7tL8J", "DD:kwlsYdonpIgJ", "F4:_LIV45PtumsJ", "EC:rpMyxZmO6LwJ",

			  "54:bDNO5Nsce9EJ", "2A:XFgcPHiQ6SYJ", "71:b8UD6gHfO6IJ", "9E:rwusJNoWjiQJ",

			  "FB:c-wHLtEdkf0J", "AE:brdZuMrvAMQJ", "E8:mzaxbrVLBOUJ", "4B:6ye4zJaCE_oJ",

			  "22:6fwxGNK3bJgJ", "B8:jwvALeEfsbcJ", "33:Yqx6f_8RvNgJ", "C2:sxB_6QG_Qb4J",

			  "C1:RDCyEcRfSgYJ", "AF:crKfJjXw_5MJ", "53:0DMvhUWTKb8J", "DC:e91ckrM89cEJ",

			  "B4:WDoT2alpGIUJ", "4D:bFmzm_4qu2kJ", "C4:0vD_dZABzHMJ", "1C:sz33mz3wOUQJ",

			  "C6:E7OZk_UQ4CkJ", "98:cLfnrVphpKwJ", "06:oZjZXvq5HtcJ", "64:iVU_MpjctIIJ",

			  "9F:X-bZvp5icB4J", "95:fNZpE0lgEn8J", "A2:RxHFAXHJtyAJ", "E3:6HrAalsLflAJ",

			  "3B:KqNbjwDqqCMJ", "BE:0RUKM-BlaPsJ", "0A:1q-lkiRNa7kJ", "76:m1ROBo00yiEJ",

			  "32:SkX0iYQQY_AJ", "40:vl95ZOX_Q4IJ", "09:ep_I291MX2cJ", "2F:2H1dZ2lZ248J",

			  "D4:N-GQv-Nx1z8J", "19:f561wYNWgkEJ", "D3:UON-_Wr60O8J", "74:KQJPHtyNoj4J",

			  "3A:lzls7PYvd34J", "F1:yQFSR4MaUpwJ", "CF:oRRo9yGdXSQJ", "4F:W6-iAz1ZqaUJ",

			  "29:C-dd67rIqqwJ", "66:GO4Tc1u-ugYJ", "DB:eC0ElD1t9J0J", "43:d6NIXjVJ7KUJ",

			  "82:Io93RHeLXQEJ", "CD:NXjNyJgzy4IJ", "25:_n6PawUEkHcJ", "B2:Lc1qYSv3UhUJ",

			  "BC:I-13107CCxIJ", "83:Y61PENPCqEkJ", "EA:9TqNFzh4Z_oJ", "47:NwDZxatOEc4J",

			  "B1:B-TsYBhWagAJ", "6F:fHVktF7dziUJ", "D8:xzpGFu00WYwJ", "35:9Rk1mF1piREJ",

			  "E9:JFkbdecjI8MJ", "96:CBItbsrnoXMJ", "55:noMLzjxCNboJ", "86:sKdRXX1_KzwJ",

			  "B6:VUPwfdt9GXQJ", "39:rCHm8PFbAI8J", "5D:4NwxRMobURQJ", "79:kiWLB6a7E5MJ",

			  "02:wS3Nm-eK00cJ", "E5:eRnVynYCgSsJ", "2E:Pus5p0uY2KkJ", "17:tOuRl4j_h8EJ",

			  "56:YmAwCsJOu2kJ", "59:zl6uZ0sBNPkJ", "E6:gDEADZ7sg_4J", "44:IrzQsQh4XsIJ",

			};

		Map entries = new HashMap();
		for (String clue : clues) {
			String key = clue.substring(0, clue.indexOf(':'));
			String val = clue.substring(clue.indexOf(':') + 1, clue.length());

			entries.put(Integer.parseInt(key, RADIX_16), val);
		}
		return entries;
    }
}

Enjoy!

Advertisements

Poor obfuscation implementation

POI – “Poor Obfuscation Implementation”, at least this was the initial meaning of the acronym. The POI project of Apache contains pure Java ports for the Microsoft File formats based on OLE 2 Compound Document Format.

There are several sub-projects under POI.

  • POIFS is the implementation of the OLE 2 Compound Document format, this is the basic API for all the other POI projects.
  • HSSF-XSSF is the implementation of the Excel ’97 file format and the XSSF is the implementation of the Excel 2007 OOXML (.xlsx) file format.
  • HPSF is the implementation for reading the so called property set streams, essentially this is the Document Summary that one can find in the properties of Microsoft Office files.
  • HWPF is the implementation the Microsoft Word 97(-2007) file format. This is my favorite one, with this API you can read and write MS word documents. Unfortunately this project is headless for the moment. Note that HWPF do not support docx file format (MS Word 2007).
  • HSLF is the implementation for MS PowerPoint files, provides a way to read, create or modify PowerPoint presentations.
  • HSMF is the implementation of the Outlook MSG format.
  • HDGF is the implementation of the Visio file format, provides the ability to read (only) the low level contents of visio files.
  • HPBF is the implementation for MS Publisher files. It is in an early stage and provides the ability to read parts of publisher files, writing is not supported yet.
  • The POI library can also be compiled as a Ruby extension.

The acronyms of this project are really funny:
POIFS – Poor Obfuscation Implementation File System
HSSF – Horrible SpreadSheet Format
HWPF – Horrible Word Processor Format
and so on…

As I said before the HWPF is my favorite, with the API that this project provides we can manipulate the content of MS Word documents. Either we like it or not, a huge amount of data has been written and saved in MS Word format. I am sure that there are a lot of use cases where you would want to have access to this data through java API. Using this java ports we can avoid hacks and provide elegant solutions.

Two of my favorite use cases are:
1. Translating a word document automatically using google translator (the translation is horrible, but is much better than nothing)
2. Paragraph alignment in bilingual text.

Lets see the first use case in e very simple implementation, the steps of the algorithm are:
a. extract the paragraphs from the word document.
b. translate the paragraphs using google translator. (or any proprietary translator that you may have)
c. insert the translated text in a new word document.

Here is the code for the first use case:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import org.apache.poi.hwpf.extractor.WordExtractor;
import com.google.api.translate.Language;
import com.google.api.translate.Translate;

public class Translator {
	private WordExtractor extractor;
	private String destFileName;

	public Translator(String sourceFileName, String destFileName) throws IOException {
		this.destFileName = destFileName;
		extractor = new WordExtractor(new FileInputStream(sourceFileName));
	}

	/**
	 * #a. extract the paragraphs from the word document.
	 */
	public String[] extractParagraphs() {
		return extractor.getParagraphText();
	}

	/**
	 * #b. translate the paragraphs using google translator.
	 */
	public String translate(String[] paragraphs, String sourceLanguage, String destLanguage)
			throws Exception {
		StringBuilder container = new StringBuilder();
		for (String text : paragraphs) {
			System.out.println(text);
			container.append(text);
		}
		String result = Translate.translate(container.toString(), sourceLanguage, destLanguage);
		System.out.println(result);
		return result;
	}

	/**
	 * #c. insert the translated text in a new word document.
	 */
	public void insertText(String text) throws IOException {
		FileOutputStream fs = new FileOutputStream(destFileName);
		OutputStreamWriter out = new OutputStreamWriter(fs);
		out.write(text);
		out.close();
	}

	public static void main(String[] args) throws Exception {
		if (args == null || args.length < 2) {
			System.out.println("Usage: Translator  ");
			System.exit(0);
		}
		Translator tr = new Translator(args[0], args[1]);
		// a.
		String[] paragraphs = tr.extractParagraphs();
		// b.
		String translatedText = tr.translate(paragraphs,
								Language.ENGLISH,
								Language.FRENCH);
		// c.
		tr.insertText(translatedText);

		System.out.println("\nDone!");
	}
}

The second use case is a bit more complicated but equally easy, for legal reasons I can not post the code… sorry, maybe it is a good exercise ;) for you.

You can find a lot more information about the POI project @ http://poi.apache.org/
end for the google java translation api @ http://code.google.com/p/google-api-translate-java/

To run the code just type: java Translate c:\mydoc_en.doc c:\mydoc_fr.doc

Note that in order to run the code, the following packages will be needed which you can find at the previews listed sites:
poi-3.1.jar
poi-scratchpad-3.1-FINAL-20080629.jar
google-api-translate-java-0.5.jar

Will cuil “kill” google!?


“Cuil – The World’s Biggest Search Engine… Cuil searches more pages on the Web than anyone else—three times as many as Google and ten times as many as Microsoft”.

I have no reason to doubt about this claim and so I started to use Cuil – which by the way is an old Irish word for knowledge – so the first thing I tried was of course my blog.

Here are the Cuil results:

The google results:
As you can see my blog is the first result in the google search, furthermore, google returns 44,700 results for 110j.

OK, my blog is not a criteria for evaluating a search engine but the difference is huge between them, since the cuil team claims to search three times more pages than google. Anyway, I will use a more common keyword for searching both engines. A word like computer will have different results than my blog.

Here are the results for the computer keyword:
I have the first two results in the screen shot from cuil, I do the same for google.

Cuil has 1,183,691,043 results for Computer

Take a closer look at the screen shot, there is a menu bar under the text field that classifies the results, this is very convenient menu bar and the classification is most of the time very good. There is also a side bar as a widget which also has results by category. Both bars are very helpful to find quickly what you are looking for.

The google results for the same keyword:
In google results you have always wikipedia (I don’t know for how long since they launched the knol -another word for knowledge – which is a direct competitor to wikipedia), Apple is also in the first page of google. Google has also something similar to the menu bar of cuil but it is simple text, however sometimes it is replaced by google-ads.

Google has 1,330,000,000 for computer

What is interesting is that google has again more results than cuil, from my point of view, this may have one of the following explanations:
1. Google search more pages.
2. Cuil analyze better the web and they do not list what they think it is garbage.

Few more important things about Cuil, they do NOT collect user data, they are very concerned about privacy. As they say “We believe that analyzing the Web rather than our users is a more useful approach, so we don’t collect data about you and your habits, lest we are tempted to peek. With Cuil, your search history is always private.”
Another good feature is the safe search which filter pornography or other objectionable material from your search results.

In order to be fair I have to say here that google has take some actions for the users privacy and more actions in the quality. Check the official google blog:
http://googleblog.blogspot.com/2008/07/more-transparency-in-customized-search.html
http://googleblog.blogspot.com/2008/08/search-quality-continued.html

It is your call which one to use, but personally I will give cuil a chance.