/*
 * I-BS.pl PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * Copyright (c) 2012, I-BS.pl and/or its affiliates. All rights reserved.
 */
package net.ranides.assira.math;

import java.util.Random;

/**
 * Generator liczb pseudolosowych o bardzo długiej serii bez cyklu.
 * Formalnie nie jest przeliczona długość cyklu. Empirycznie dla kilku seed'ów 
 * zostało sprawdzonych pierwsze 64 mln elementów:
 * 17, 32, 40, 197
 * @author ranides
 */
public final class LFSR extends Random {

    private static final long serialVersionUID = 2L;
    
    private static final int M = 32;
    // hard-coded for 32-bits
    private static final int[] TAPS = {1, 2, 22, 32};
    
    private boolean[] vector;

    /**
     * Tworzy nowy generator z seedem określanym na podstawie bieżącego czasu 
     * systemowego.
     */
    public LFSR() {
        this( (int)System.nanoTime() );
    }
    
    /**
     * Tworzy nowy generator z podanym seedem.
     * @param seed
     */
    public LFSR(int seed) {
        super(seed);
    }
    
    @Override
    public void setSeed(long seed) {
        synchronized(this) {
            vector = new boolean[M + 1];
            for (int i = 0; i < M; i++) {
                vector[i] = (seed & (1 << i)) != 0;
            }
            super.setSeed(0);
        }
    }

    @Override
    public int next(int bits) {
        synchronized(this) {
            // nawet jeśli dla liczby używamy tylko [bits] rejestrów
            // to w następnych krokach wykonujemy standardowe przesunięcie całej sekwencji
            int result = 0;
            for (int i = 0; i < bits; i++) {
                result |= (vector[i] ? 1 : 0) << i;
            }

            // zero jest dopuszczalne. tracimy co prawda wartość -2^31
            if (result < 0) {
                result++;
            }

            // modyfikacja ostatniego rejestru na podstawie wyróżnionych rejestrów
            vector[M] = false;
            for (int i = 0; i < TAPS.length; i++) {
                vector[M] ^= vector[M - TAPS[i]];
            }

            // przesunięcie całego wektora
            for (int i = 0; i < M; i++) {  // NOPMD: licho wie, czy arraycopy bardziej efektywne
                vector[i] = vector[i + 1];
            }

            return result;
        }
    }

}

