//interface and utils for modular entropy multiplier hardware

#include "entropy.h"
unsigned long rnd_last32=0; //internal for checking data
unsigned long rnd_data32=0; //used to return some bits
unsigned long lfsr_data32=LFSR_INIT;    //can't be zero or it will lock up

char rnd_state=0;           //health of data

int twoD[RND_D_TEST][RND_D_TEST];
int oneD[RND_D_TEST];
unsigned int fairnessHealth=0;
unsigned int correlationHealth=0;

char lastD=0;

void setRND(char s){
    if(s){
        //rnd
        anselbits.RND_PWR=0;    //all digital
        anselbits.RND_PHI1=0;
        anselbits.RND_PHI2=0;
        anselbits.RND_OUT=0;
        anselbits.RND_TEST=1;       //analog to avoid digital leakage
        latbits.RND_PWR=0;  //all off
        latbits.RND_PHI1=0;
        latbits.RND_PHI2=0;
        trisbits.RND_PWR=0;  //all outputs
        trisbits.RND_PHI1=0;
        trisbits.RND_PHI2=0;
        trisbits.RND_OUT=1;     //read input
        trisbits.RND_TEST=1;     //test input
        //turn on
        latbits.RND_PWR=1;
        latbits.RND_PHI1=1;
        latbits.RND_PHI2=0;        
    }else{
        latbits.RND_PHI1=0;
        latbits.RND_PHI2=0;        
        latbits.RND_PWR=0;  //all off
    }
}

char getWhiteBit(void){
    static char n=1;        //use xor to mix up correlation    
    n=n^getRNDbit();
    n=n^getRNDbit();
    n=n^getRNDbit();
    return n;
}

unsigned long get24bits(void){
    char i;
    for(i=0;i<8;i++){   //fetch another 8 bits
        rnd_data32=(rnd_data32<<1)|getWhiteBit();
    }
    return rnd_data32>>8;
}

char getRNDbit(void){
    char b;
    if(portbits.RND_PHI1){  //choose toggle
        latbits.RND_PHI1=0;
        latbits.RND_PHI2=1;                
    }else{
        latbits.RND_PHI2=0;
        latbits.RND_PHI1=1;                        
    }
    __delay_ms(1);              //new bit to settle
    b=portbits.RND_OUT;
    rnd_last32=(rnd_last32<<1)|b;    
    return b;
}

char getLFSRbit(void){
    unsigned char b;
    b=lfsr_data32>>24;
    lfsr_data32=lfsr_data32<<1;
    switch(b&144){
        case 0:
        case 144:
            break;
        case 16:
        case 128:
            lfsr_data32=lfsr_data32|2; //note goes into bit 1
            break;                
    }
    if(lfsr_data32&0x800000UL){
        return 1;
    }else{
        return 0;
    }
}

void checkState(void){  //updates rnd_state, fairnessHealth, correlationHealth
    long d;
    long s;
    s=oneD[0]+oneD[1];
    rnd_state=0;
    fairnessHealth=0;
    correlationHealth=0;
    if(s<2){return;}    //can't process no data    
    //fairness
    d=abs(oneD[0]-oneD[1]);
    d=(d*100)/s;
    if(d>100){
        fairnessHealth=0;
    }else{
        fairnessHealth=(unsigned int)(100-d);
    }    
    //correlation
    d=abs(twoD[0][0]+twoD[1][1]-twoD[0][1]-twoD[1][0]);
    d=(d*100)/s;
    if(d>100){
        correlationHealth=0;
    }else{
        correlationHealth=(unsigned int)(100-d);
    }        
}

void getEntropy(void){  //run a few times to get to a usefully random state
    unsigned int i,j;
    j=RND_BOOT_CNT+(getADC(RND_ADC_PIN)&7);
    for(i=0;i<j;i++){
        get24bits();
    }
}

char countSetBits(unsigned long n){
    char b=0;
    char i;
    for(i=0;i<32;i++){
        if(n&1){b=b+1;}
        n=n>>1;
    }
    return b;
}

unsigned long get24bitsLFSR(void){  //note that this only uses top 31 bits
    char i;    
    unsigned char b;
    if(lfsr_data32==0){lfsr_data32=LFSR_INIT;}  //avoid latchup
    for(i=0;i<8;i++){   //fetch another 8 bits
        getLFSRbit();
    }        
    return lfsr_data32>>8;
}

void runDtest(void){
    unsigned int i;
    char n;
    char x,y;
    for(i=0;i<RND_TEST_CNT;i++){
        //n=(get24bits()*RND_D_TEST)>>24;
        n=getWhiteBit();
        oneD[n]++;
        twoD[n][lastD]++;
        lastD=n;
    }
}

char getRoll(char n){   //return 1..n
    unsigned long r;    
    r=get24bits();
    return ((r*n)>>24)+1;
}

char getRollLFSR(char n){   //return 1..n
    unsigned long r;
    r=get24bitsLFSR();    
    return ((r*n)>>24)+1;
}

/*
unsigned int sqrt(unsigned long n){     //integer, does LUT approximation on hi bits, then linear search
    const unsigned int roots[]={
        0,1,1,1,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,
        5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,
        7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
    };
    unsigned char s=0;
    unsigned int r;
    unsigned long tmp=n;
    while(tmp>63){  //scale for LUT
        s++;
        tmp=tmp>>2;
    }
    r=(roots[tmp])<<s;     //find an approximation using the hi bits
    tmp=((unsigned long)r)*((unsigned long)r);
    n=n+1;
    while(n>tmp){
        tmp=tmp+r+r+1;
        r=r+1;
    }
    r=r-1;
    return r;    
}
*/