public void learn(int lastWord, int word) {

        if (word==-1) return;
        int bptt = conf.bptt;
        int bpttBlock = conf.bpttBlock;

        beta2=beta*alpha;

        // Compute error vectors
        loadWordVector(word);
        CommonOps.sub(wordVector, outputLayer.neurons, outputLayer.errors);

        // Flush error
        hiddenLayer.errors.zero();
        // TODO: class compression

        // TODO: learn direct connections between words

        // TODO: learn direct connections to classes

        // TODO: back propagate to compression

        CommonOps.multTransA(hiddenSynapse.weights, outputLayer.errors, hiddenLayer.errors);

        // Learn hidden weights
        hiddenSynapse.learnAll(hiddenLayer.neurons, outputLayer.errors, alpha, beta);

        // BPTT

        // Copy from HL to BL_0
        bpttHiddenLayers[0].copyFrom(hiddenLayer);

        int wordOfStep;
        int maxSteps = bptt + bpttBlock;

        if (((counter % conf.bpttBlock)==0) || (conf.independent && (word==0))) {

            for (int step = 0; step < maxSteps - 2; step++) {

                wordOfStep = bpttHistory[step];

                // Error derivation at hidden layer
                hiddenLayer.errorDerivation();

                // IBS[col of word_step][x] += alpha * HL.er[x] * 1
                bpttInputSynapse.learnOneColumn(wordOfStep, hiddenLayer.errors, alpha);

                // RBS[x] += alpha * HL.er[row of x] * RL.ac[col of x]
                bpttRecurrentSynapse.learnAll(recurrentLayer.neurons, hiddenLayer.errors, alpha, Double.NEGATIVE_INFINITY);

                // Clean RL.er
                recurrentLayer.errors.zero();

                // Propagate HL.er -> (RS) -> RL.er
                CommonOps.multTransA(recurrentSynapse.weights, hiddenLayer.errors, recurrentLayer.errors);

                // HL.er = RL.er + BL_s+1.er
                CommonOps.add(bpttHiddenLayers[step + 1].errors, recurrentLayer.errors, hiddenLayer.errors);

                if (step < maxSteps - 3) {
                    hiddenLayer.neurons = bpttHiddenLayers[step + 1].neurons.copy();
                    recurrentLayer.neurons = bpttHiddenLayers[step + 2].neurons.copy();
                }
            }

            // Clean BL_x.er
            for (Layer bpttLayer : bpttHiddenLayers) {
                bpttLayer.errors.zero();
            }

            // HL.ac = BL_0.ac
            hiddenLayer.neurons = bpttHiddenLayers[0].neurons.copy();

            // RS += RBS - regularization (self * beta2)
            recurrentSynapse.accumulate(bpttRecurrentSynapse.weights, (counter%10)==0 ? beta2 : 0);

            // Clean RBS
            bpttRecurrentSynapse.zero();

            // IS += IBS - regularization (self * beta2)
            inputSynapse.accumulate(bpttInputSynapse.weights, (counter%10)==0 ? beta2 : 0);

            // Clean IBS
            bpttInputSynapse.zero();
        }
    }