public static void main(String[] args) {
        //
        final Random rnd = new Random(System.currentTimeMillis());
        //
        // network parameter.
        //
        final int input   = 2;
        final int hidden  = 8;
        //
        final int trainset_size = 5000;
        final int testset_size  = 100;
        //
        // training parameter
        //
        final int    epochs       = 4000;
        final double learningrate = 0.004;
        final double momentum     = 0.9;
        //
        // Generate a multilayer perceptron using the
        // MLPGenerator class. 
        //
        final MLPGenerator gen = new MLPGenerator();
        //
        gen.inputLayer(input);
        gen.hiddenLayer(hidden, CellType.SIGMOID, true, -1.0);
        gen.outputLayer(1, CellType.SIGMOID);
        //
        final Net mlp = gen.generate();
        mlp.initializeWeights(rnd);
        //
        // Generate samples sets.
        //
        final SampleSet trainset = generateData(trainset_size + testset_size, 0.5f, rnd);
        final SampleSet testset  = trainset.split(testset_size, rnd);
        //
        // Setup back-propagation trainer.
        //
        GradientDescent trainer = new GradientDescent();
        trainer.setEpochs(epochs);
        trainer.setLearningRate(learningrate);
        trainer.setMomentum(momentum);
        trainer.setPermute(true);
        trainer.setRnd(rnd);
        trainer.setTargetError(10E-5);
        trainer.setNet(mlp);
        trainer.setTrainingSet(trainset);
        trainer.addListener(new NetTrainerListener() {
            @Override
            public void started(NetTrainer trainer) {
            }
            @Override
            public void finished(NetTrainer trainer) {
            }
            
            @Override
            public void epoch(NetTrainer trainer) {
                //
                final int ep = trainer.getEpoch() + 1;
                //
                if ((ep) % (epochs / 16) != 0 && ep != 1) return;
                final BufferedImage img = new BufferedImage(800, 800, BufferedImage.TYPE_INT_RGB);
                
                double[] p = new double[2];
                double[] o = new double[2];
                
                for (int y = 0; y < img.getHeight(); y++) {
                    for (int x = 0; x < img.getWidth(); x++) {
                        //
                        p[0] = ((double)x) / ((double)(img.getWidth()));
                        p[1] = ((double)y) / ((double)(img.getHeight()));
                        //
                        mlp.reset();
                        mlp.input(p, 0);
                        mlp.compute();
                        mlp.output(o, 0);
                        //
                        int v = ((int)(o[0] * 255));
                        img.setRGB(x, y, ((v>>1) << 8) | v);
                    }
                }
                
                final Graphics2D g = (Graphics2D)img.getGraphics();
                g.setRenderingHint(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON
                );

                for (Sample s : trainset) {
                    int x = (int)((double)(img.getWidth() - 1) *  
                            s.getInput()[0]);
                    int y = (int)((double)(img.getHeight() - 1) * 
                            s.getInput()[1]);
                    if (s.getTarget()[0] > 0.5) {
                        g.setColor(CLASS_1);
                    } else {
                        g.setColor(CLASS_2);
                    }
                    g.fillOval(x-2, y-2, 5, 5);
                }
                
                JFrame frame = new JFrame("Geometry Learning - Epoch: " + ep);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                //
                JPanel panel = new JPanel() {
                    private static final long serialVersionUID = -4307908552010057652L;

                    @Override
                    protected void paintComponent(Graphics gfx) {
                        super.paintComponent(gfx);
                        gfx.drawImage(
                            img,  0,  0, 
                            img.getWidth(),  img.getHeight(),  null
                        );
                    }
                };
                panel.setPreferredSize(new Dimension(img.getWidth(), img.getHeight()));
                frame.add(panel);
                frame.setResizable(false);
                frame.pack();
                frame.setVisible(true);
            }
        });
        //
        // perform training.
        //
        trainer.train();
        //
        // We use the ClassificationValidator to measure the recognition
        // accuracy on the training set as well as on the test set.
        //
        System.out.println();
        ClassificationValidator val = new ClassificationValidator(mlp);
        //
        for (Sample s : trainset) {
            val.apply(s);
        }
        System.out.println(
            "recognition accuracy on trainset: " + 
            (val.ratio() * 100.0) + "%"
        );
        //
        val.reset();
        //
        for (Sample s : testset) {
            val.apply(s);
        }
        System.out.println(
            "recognition accuracy on testset: " + 
            (val.ratio() * 100.0) + "%"
        );


    }