/* random.c,v 1.1.1.1 1995/02/27 07:38:26 explorer Exp */

/*
 * Copyright (c) 1993 Martin Birgmeier
 * All rights reserved.
 *
 * You may redistribute unmodified or modified versions of this source
 * code provided that the above copyright notice and this and the
 * following conditions are retained.
 *
 * This software is provided ``as is'', and comes with no warranties
 * of any kind. I shall in no event be liable for anything that happens
 * to anyone/anything when using this software.
 */

/*
 * This was modified to fit into Rayshade with minimal fuss.  While running
 * images on different machine types, system-defined random functions were
 * found to be quite different.  Hopefully, this will generate the same
 * output regardless of machine arch.
 */

#include <math.h>
#include <stdlib.h>

#include "common.h"

#define RAND48_SEED_0   (0x330e)
#define RAND48_SEED_1   (0xabcd)
#define RAND48_SEED_2   (0x1234)
#define RAND48_MULT_0   (0xe66d)
#define RAND48_MULT_1   (0xdeec)
#define RAND48_MULT_2   (0x0005)
#define RAND48_ADD      (0x000b)

static uint16 __rand48_seed[3] = {
  RAND48_SEED_0,
  RAND48_SEED_1,
  RAND48_SEED_2
};

static uint16 __rand48_mult[3] = {
  RAND48_MULT_0,
  RAND48_MULT_1,
  RAND48_MULT_2
};

static uint16 __rand48_add = RAND48_ADD;

static void __dorand48 _PROTO((uint16[3]));

static void 
__dorand48(xseed)
     uint16 xseed[3];
{
  uint32 accu;
  uint16 temp[2];
  
  accu = ((uint32) __rand48_mult[0] * (uint32) xseed[0] +
	  (uint32) __rand48_add);
  temp[0] = (uint32) accu;	/* lower 16 bits */
  accu >>= sizeof(uint16) * 8;
  accu += ((uint32) __rand48_mult[0] * (uint32) xseed[1] +
	   (uint32) __rand48_mult[1] * (uint32) xseed[0]);
  temp[1] = (uint16) accu;	/* middle 16 bits */
  accu >>= sizeof(uint16) * 8;
  accu +=  (__rand48_mult[0] * xseed[2] +
	    __rand48_mult[1] * xseed[1] +
	    __rand48_mult[2] * xseed[0]);
  xseed[0] = temp[0];
  xseed[1] = temp[1];
  xseed[2] = (uint16) accu;
}

Float
nrand()
{
  __dorand48(__rand48_seed);

  return (ldexp((Float) __rand48_seed[0], -48) +
	  ldexp((Float) __rand48_seed[1], -32) +
	  ldexp((Float) __rand48_seed[2], -16));
}

void
seednrand(seed)
     uint32 seed;
{
  __rand48_seed[0] = RAND48_SEED_0;
  __rand48_seed[1] = (uint16) seed;
  __rand48_seed[2] = (uint16) (seed >> 16);
  __rand48_mult[0] = RAND48_MULT_0;
  __rand48_mult[1] = RAND48_MULT_1;
  __rand48_mult[2] = RAND48_MULT_2;
  __rand48_add = RAND48_ADD;
}

#if TEST_RANDOM

/*
 * This is a test function, which is known to generate the correct values on
 * i386 (NetBSD),  DECstation (Ultrix),  DEC Alpha (OSF/1)
 *
 * If it does not generate the correct output for your machine, please try to
 * fix it, and mail me the patch.  If you can't do that, at least mail me that
 * it didn't work, what machine type you are using, and other info
 *
 * The correct output:
 * 0.940647        0.670256        0.631741        0.206735        0.055375
 * 0.288436        0.222196        0.989920        0.546271        0.962893
 * 0.272670        0.377903        0.403888        0.029244        0.060559
 * 0.312907        0.659284        0.170635        0.658811        0.942682
 * 0.994639        0.494733        0.953667        0.940781        0.278211
 */ 

main()
{
  register int i;

  seednrand(123456);

  for (i = 0 ; i < 5 ; i++)
    printf("%f\t", nrand());
  printf("\n");

  for (i = 0 ; i < 5 ; i++)
    printf("%f\t", nrand());
  printf("\n");

  for (i = 0 ; i < 5 ; i++)
    printf("%f\t", nrand());
  printf("\n");

  for (i = 0 ; i < 5 ; i++)
    printf("%f\t", nrand());
  printf("\n");

  for (i = 0 ; i < 5 ; i++)
    printf("%f\t", nrand());
  printf("\n");

  exit(0);
}
#endif /* TEST_RANDOM */
