import java.io.*;
import java.lang.Math;

public class Hopfield  {
    public static void main(String args[]) {
	int [][] x =  {
                           { -1, -1,  1, -1, -1,  // 1
        		     -1,  1,  1, -1, -1,
        		     -1, -1,  1, -1, -1,
        		     -1, -1,  1, -1, -1,
        		     -1, -1,  1, -1, -1,
        		     -1, -1,  1, -1, -1,
        		     -1, -1,  1, -1, -1,
        		     -1, -1, -1, -1, -1 },
        	           { -1,  1,  1,  1, -1,  // 2
        		      1, -1, -1, -1,  1,
        		     -1, -1, -1, -1,  1,
        		     -1, -1,  1,  1, -1,
        		     -1,  1, -1, -1, -1,
        		      1, -1, -1, -1, -1,
        		      1,  1,  1,  1,  1,
        		     -1, -1, -1, -1, -1 },
        	           {  1, -1, -1,  1, -1,  // 4
        		      1, -1, -1,  1, -1,
        		      1, -1, -1,  1, -1,
        		      1,  1,  1,  1,  1,
        		     -1, -1, -1,  1, -1,
        		     -1, -1, -1,  1, -1,
        		     -1, -1, -1,  1, -1,
        		     -1, -1, -1, -1, -1 }
 };

	Vec[] pats = new Vec[x.length];
	for (int i=0; i<pats.length; i++)
	    pats[i]=new Vec(x[i]);
	System.out.println("Network built, W[40][40]=");
	Net net = new Net(pats); net.print();

	for (int i=0; i<pats.length; i++){
	    System.out.println("pats["+i+"]; Energy="+net.energy(pats[i]));
	    pats[i].print();
	}

	int [][] y =  {
	                   { -1,  1,  1, -1, -1,  // 1ish
	                     -1, -1,  1, -1, -1,
        		     -1, -1,  1, -1, -1,
        		     -1, -1, -1,  1,  1,
        		     -1, -1,  1, -1, -1,
        		     -1, -1, -1,  1, -1,
        		     -1, -1,  1, -1, -1,
        		      1, -1, -1,  1, -1 },
        	           { -1,  1,  1,  1, -1,  // 2ish
        		     -1, -1, -1, -1,  1,
        		     -1, -1, -1, -1,  1,
        		      1,  1,  1, -1, -1,
        		     -1,  1, -1, -1, -1,
        		      1, -1, -1, -1,  1,
        		      1,  1,  1,  1, -1,
        		     -1, -1, -1,  1, -1 },
        	           {  1,  1, -1,  1, -1,  // 4ish
        		     -1,  1, -1,  1, -1,
        		      1, -1, -1,  1, -1,
        		     -1,  1,  1,  1,  1,
        		     -1, -1, -1,  1, -1,
        		      1, -1, -1,  1,  1,
        		     -1, -1, -1,  1, -1,
        		      1, -1, -1, -1, -1 },
        	           {  1,  1,  1,  1,  1,  // Horiz stripes
        		     -1, -1, -1, -1, -1,
			      1,  1,  1,  1,  1,
        		     -1, -1, -1, -1, -1,
			      1,  1,  1,  1,  1,
        		     -1, -1, -1, -1, -1,
			      1,  1,  1,  1,  1,
        		     -1, -1, -1, -1, -1 },
        	           {  1, -1,  1, -1,  1,  // Vert stripes
        		      1, -1,  1, -1,  1,
        		      1, -1,  1, -1,  1,
        		      1, -1,  1, -1,  1,
        		      1, -1,  1, -1,  1,
        		      1, -1,  1, -1,  1,
        		      1, -1,  1, -1,  1,
        		      1, -1,  1, -1,  1,
			      1,  1,  1,  1,  1 },
        	           { -1,  1, -1,  1, -1,  // Vert stripes
        		     -1,  1, -1,  1, -1,
        		     -1,  1, -1,  1, -1,
        		     -1,  1, -1,  1, -1,
        		     -1,  1, -1,  1, -1,
        		     -1,  1, -1,  1, -1,
        		     -1,  1, -1,  1, -1,
        		     -1,  1, -1,  1, -1 },
        	           { -1,  1, -1,  1, -1,  // Alternating
        		      1, -1,  1, -1,  1,
                             -1,  1, -1,  1, -1,
        		      1, -1,  1, -1,  1,
                             -1,  1, -1,  1, -1,
        		      1, -1,  1, -1,  1,
                             -1,  1, -1,  1, -1,
			      1, -1,  1, -1,  1 }
 };
	Vec[] in = new Vec[y.length];
	for (int i=0; i<in.length; i++)
	    in[i]=new Vec(y[i]);

	System.out.println();
	for (int i=0; i<in.length; i++){
	    net.setNeurons(in[i]);
	    System.out.println("Processing in["+i+"]");
	    for (int j=0; j<=50; j++){
		System.out.print("in["+i+"] Step: "+j);
		System.out.println(" Energy: "+net.energy(net.getNeurons()));
		net.getNeurons().print();
		boolean done = net.step();
		if (done) break;
	    }
	}
    }
}

class Vec {
    int[] vec;
    Vec(int n){
	vec = new int[40];
	for (int i=0; i<40; i++)
	    vec[i] = n;
    }
    Vec(Vec v){
	vec = new int[40];
	for (int i=0; i<40; i++)
	    vec[i] = v.vec[i];
    }
    Vec(int[] ii){
	vec = new int[40];
	for (int i=0; i<40; i++)
	    vec[i] = ii[i];
    }
    public boolean equals(Vec v){
	for (int i=0; i<40; i++)
	    if (vec[i] != v.vec[i]) return false;
	return true;
    }    
    public void print(){
	for (int i=0; i<8; i++){
	    for (int j=0; j<5; j++)
		System.out.print(vec[i*5 + j]==-1?" ":"*");
	    System.out.println();
	}
	System.out.println();
    }
}
class Net {
    int[][] W;    // The weights: W[i][j]
    Vec s;        // The current state of the neurons
    Net(Vec[] pats){
	W = new int[40][40];
	for (int i=0; i<40; i++)
	    for (int j=0; j<40; j++)
		if (i==j)
		    W[i][j] = 0;
		else for (int n=0; n<pats.length; n++)
		    W[i][j] += pats[n].vec[i]*pats[n].vec[j];
    }
    public int energy(Vec v){
	int E = 0;
	for (int i=0; i<40; i++)
	    for (int j=0; j<40; j++)
		E += W[i][j]*v.vec[i]*v.vec[j];
	return E;
    }
    public Vec mul(Vec v){  // return v[i] = Sum W[i][j]*v[j]
	Vec v1 = new Vec(0);
	for (int i=0; i<40; i++)
	    for (int j=0; j<40; j++)
		v1.vec[i] += W[i][j]*v.vec[j];
	return v1;
    }
    public boolean step(){  // Run net for one step. return "done" (no change)
	Vec s0 = s;
	Vec h = mul(s0);
	s = new Vec(s0);
	for (int i=0; i<40; i++)  // The sign function w/ no change for 0.
	    s.vec[i] = (h.vec[i] == 0) ? s0.vec[i] : (h.vec[i] > 0 ? +1:-1);
	return s0.equals(s);
    }
    public void setNeurons(Vec v){s = new Vec(v);}
    public Vec getNeurons(){return s;}
    public void print(){
	for (int i=0; i<40; i++){
	    for (int j=0; j<40; j++){
		String s = "" + W[i][j];
		String b = s.length()==1?" ":"";
		System.out.print(b +  s);
	    }
	    System.out.println();
	}
	System.out.println();
    }
}




