#if !defined  HAVE_LFSR64_H__
#define       HAVE_LFSR64_H__
// This file is part of the FXT library.
// Copyright (C) 2010, 2012, 2014 Joerg Arndt
// License: GNU General Public License version 3 or later,
// see the file COPYING.txt in the main directory.


#include "fxttypes.h"
#include "bits/bitsperlong.h"


class lfsr64
// (binary) Linear Feedback Shift Register
// Produces a shift register sequence (SRS)
// generated by  a_k mod c where
// c defaults to the minimum weight primitive polynomial of degree 64:
//  c = x^64 + x^4 + x^3 + x + 1
// and
//  a_k = x^k (mod c)
// period of lfsr is 2^64 - 1
{
public:
    unsigned long long a_;  // internal state (polynomial modulo c)
    unsigned long long w_;  // word of the shift_register_sequence (SRS)
    unsigned long long c_;  // (mod 2) polynomial == x^64 + x^4 + x^3 + x + 1

public:
    explicit lfsr64(unsigned long long c=0)
    {
        c_ = (0==c ? 27 : c);  // + 1UL<<64  // minimum weight, low bit
//        c_ = (0==c ? 0x830f7db354117363UL : c);  // + 1UL<<64  // 'random', weight==33

        a_ = 1;  // start with a==1
        w_ = 0;
        next_w();  // avoid zero w
    }

    ~lfsr64()  { ; }

    unsigned long long next()
    {
#if  ( BITS_PER_LONG_LONG >= 64 )
        unsigned long long s = a_ >> 63;
#else
        bool s = ((signed long long)a_ < 0);  // better on 32bit machines
#endif
        a_ <<= 1;
        w_ <<= 1;
        if ( s )
        {
            a_ ^= c_;
            w_ |= 1;
        }
        return w_;
    }

    void next_w()
    // produce the next word w
    // 1 <= w <= ~0
    {
        for (ulong k=0; k<64; ++k)  next();
    }

    unsigned long long get_a()  const  { return a_; }

    unsigned long long get_w()  const  { return w_; }

    void set_w(unsigned long long  w)
    // must have w!=0
    {
        w_ = w;
        a_ = 0;
        unsigned long long c = c_;
        while ( w )
        {
            if ( w & 1 )  a_ ^= c;
            c <<= 1;
            w >>= 1;
        }
    }
};
// -------------------------


#endif  // !defined HAVE_LFSR64_H__
