package CDS::Noise;
use Exporter;
@ISA = ('Exporter');

#//     \page Noise Noise.pm
#//     Documentation for Noise.pm
#//     This Noise part generates uniform random noise in the 
#//     (0, 1) range. This block utilizes the pseudo-random number 
#//     generator from page 342  (section 7.1) of the third edition 
#//     of Numerical recipies. 
#//
#// \n


$printed = 0;
$init_code_printed = 0;
1;

sub partType {
    return Noise;
}

# Print Epics communication structure into a header file
# Current part number is passed as first argument
sub printHeaderStruct {
    my ($i) = @_;
}

# Print Epics variable definitions
# Current part number is passed as first argument
sub printEpics {
    my ($i) = @_;
}


# Print variable declarations int front-end file
# Current part number is passed as first argument
sub printFrontEndVars  {
    my ($i) = @_;
    print ::OUT "static double \L$::xpartName[$i];\n";
    if ($printed) { return; }

    print ::OUT << "END";
static unsigned long noise_seed = 0;
static unsigned long noise_u = 0, noise_v = 0, noise_w = 0;
inline unsigned long noise_int64( void ) {
   noise_u = noise_u * 2862933555777941757LL + 7046029254386353087LL;
   noise_v ^= noise_v >> 17; noise_v ^= noise_v << 31; noise_v ^= noise_v >> 8;
   noise_w = 4294957665U * (noise_w & 0xffffffff) + (noise_w >> 32);
   unsigned long noise_x = noise_u ^ (noise_u << 21);
   noise_x ^= noise_x >> 35; noise_x ^= noise_x <<4;
   return (noise_x + noise_v) ^ noise_w;
}
inline double noise_doub( void ) { return 5.42101086242752217E-20 * noise_int64(); }
inline void noise_set_seed(unsigned long seed) {
   noise_v = 4101842887655102017LL;
   noise_w = 1;
   noise_u = seed ^ noise_v; noise_int64();
   noise_v = noise_u; noise_int64();
   noise_w = noise_v; noise_int64();
}
END
$printed = 1;
}

# Check inputs are connected
sub checkInputConnect {
    my ($i) = @_;
    return "";
}

# Figure out part input code
# Argument 1 is the part number
# Argument 2 is the input number
# Returns calculated input code
sub fromExp {
    my ($i, $j) = @_;
    my $from = $::partInNum[$i][$j];
    return "\L$::xpartName[$from]";
}

# Return front end initialization code
# Argument 1 is the part number
# Returns calculated code string
sub frontEndInitCode {
    my ($i) = @_;
    if ($init_code_printed) { return ""; }
    my $calcExp  =  "\L$::xpartName[$i] = 0;\n";

    #If we don't have an explicit seed set, get one from the tsc register
    if ( $::noiseGeneratorSeed eq "none" ) {
        $calcExp    .=  "rdtscl(noise_seed);\n";
        $calcExp    .=  "rdtscl(noise_w);\n";
        $calcExp    .=  "noise_seed |= (noise_w << 32UL);\n";
    }
    else {
        $calcExp    .=  "noise_seed = " . $::noiseGeneratorSeed . "UL;\n";
    }
    $calcExp    .=  "noise_set_seed(noise_seed);\n";

    $init_code_printed = 1;
    return $calcExp;
}


# Return front end code
# Argument 1 is the part number
# Returns calculated code string
sub frontEndCode {
    my ($i) = @_;
    my $calcExp = "// Noise\n";
    $calcExp .= "\L$::xpartName[$i] = noise_doub();\n";
}