@*
POSIXGenerator.tmpl

Created by Graham Dennis on 2010-11-28.

Copyright (c) 2010-2012, Graham Dennis

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

*@
@extends xpdeint.ScriptElement
@from xpdeint.PrintfSafeFilter import PrintfSafeFilter
@from xpdeint.CallOnceGuards import callOnceGuard

@def seedCount
  @return len($seedArray) or 10
@end def

@def globals
  @#
uint32_t ${generatorName}_seeds[${seedCount}];
  @#
@end def

@def initialiseGlobalSeeds
  @#
  @set $featureOrdering = ['Driver']
  @#
  @if not $seedArray
${seedSystemRandomNumberGenerator}@slurp
    @#
    @set $seedGenerationDict = {'extraIndent': 0}
${insertCodeForFeatures('runtimeSeedGenerationBegin', $featureOrdering, $seedGenerationDict)}@slurp
    @silent extraIndent = seedGenerationDict['extraIndent']
${runtimeGenerateSeeds, extraIndent = extraIndent}@slurp
${insertCodeForFeaturesInReverseOrder('runtimeSeedGenerationEnd', $featureOrdering, $seedGenerationDict)}@slurp
  @else
    @for seedIdx, seed in enumerate($seedArray)
${generatorName}_seeds[$seedIdx] = $seed;
    @end for
  @end if
  @#
@end def

@# We only need to seed the system random number generator once, even if there are multiple Generator objects.
@@callOnceGuard
@def seedSystemRandomNumberGenerator
#if HAVE_SRANDOMDEV
srandomdev();
#elif HAVE_DEV_URANDOM
{
  size_t __state_size = 256;
  char __state[__state_size];
  unsigned __seed;
  FILE *__urandom_fp = fopen("/dev/urandom", "r");
  if (__urandom_fp == NULL) {
    _LOG(_ERROR_LOG_LEVEL, "Unable to seed random number generator from /dev/urandom.  Is it accessible?\n");
    // Implicit quit
  }
  size_t __bytes_read = 0;
  __bytes_read = fread(&__seed, sizeof(__seed), 1, __urandom_fp);
  if (__bytes_read != 1) {
    _LOG(_ERROR_LOG_LEVEL, "Unable to read from /dev/urandom while seeding the random number generator.\n");
    // Implicit quit
  }
  __bytes_read = fread(__state, sizeof(__state), 1, __urandom_fp);
  if (__bytes_read != 1) {
    _LOG(_ERROR_LOG_LEVEL, "Unable to read from /dev/urandom while seeding the random number generator.\n");
    // Implicit quit
  }
  fclose(__urandom_fp);
  initstate(__seed, __state, __state_size);
}
#else
#error Do not have a run-time random number source! Please supply seeds manually.
#endif
@end def

@def initialiseLocalSeeds
  @#
  @set $featureOrdering = ['Driver']
  @silent seedOffset = $insertCodeForFeatures('seedOffset', $featureOrdering)
  @#
uint32_t ${generatorName}_local_seeds[${seedCount}] = {
  ${',\n  '.join([c'${generatorName}_seeds[$i]+(0${seedOffset})*${i+1}' for i in xrange($seedCount)])}
};
  @#
@end def

@def runtimeGenerateSeeds
for (unsigned long _i0=0; _i0 < ${seedCount}; _i0++)
  ${generatorName}_seeds[_i0] = (uint32_t)random();
@end def

@def xsilOutputInfo($dict)
@*doc:
Write to the XSIL file lines naming the seeds that we generated if no seed was provided
in the script file. These are the seeds that should be provided in the script file to get
the same results.
*@
  @#
  @set $fp = dict['fp']
  @#
  @if len($seedArray)
    @return
  @end if
  @#
fprintf($fp, "\nNo seeds were provided for noise vector '${parent.parent.name}'. The seeds generated were:\n");
fprintf($fp, "    ${', '.join(['%u' for _ in xrange($seedCount)])}\n", ${', '.join([c'${generatorName}_seeds[$i]' for i in xrange($seedCount)])});
@end def
