diff src/DLD-FUNCTIONS/rand.cc @ 2928:295f037b4b3e

[project @ 1997-05-05 05:32:33 by jwe]
author jwe
date Mon, 05 May 1997 05:33:54 +0000
parents
children aa9d0c0e0458
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/DLD-FUNCTIONS/rand.cc	Mon May 05 05:33:54 1997 +0000
@@ -0,0 +1,410 @@
+/*
+
+Copyright (C) 1996, 1997 John W. Eaton
+
+This file is part of Octave.
+
+Octave 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, or (at your option) any
+later version.
+
+Octave 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 Octave; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctime>
+
+#include <string>
+
+#include "f77-fcn.h"
+#include "lo-mappers.h"
+
+#include "defun-dld.h"
+#include "error.h"
+#include "gripes.h"
+#include "help.h"
+#include "oct-obj.h"
+#include "unwind-prot.h"
+#include "utils.h"
+
+// Possible distributions of random numbers.  This was handled with an
+// enum, but unwind_protecting that doesn't work so well.
+#define uniform_dist 1
+#define normal_dist 2
+
+// Current distribution of random numbers.
+static int current_distribution = uniform_dist;
+
+// Has the seed been set yet?
+static int initialized = 0;
+
+extern "C"
+{
+  int *F77_FCN (dgennor, DGENNOR) (const double&, const double&,
+				   double&);
+
+  int *F77_FCN (dgenunf, DGENUNF) (const double&, const double&,
+				   double&);
+
+  int *F77_FCN (setall, SETALL) (const int&, const int&);
+
+  int *F77_FCN (getsd, GETSD) (int&, int&);
+
+  int *F77_FCN (setsd, SETSD) (const int&, const int&);
+
+  int *F77_FCN (setcgn, SETCGN) (const int&);
+}
+
+static double
+curr_rand_seed (void)
+{
+  union d2i { double d; int i[2]; };
+  union d2i u;
+  F77_FCN (getsd, GETSD) (u.i[0], u.i[1]);
+  return u.d;
+}
+
+static int
+force_to_fit_range (int i, int lo, int hi)
+{
+  assert (hi > lo && lo >= 0 && hi > lo);
+
+  i = i > 0 ? i : -i;
+
+  if (i < lo)
+    i = lo;
+  else if (i > hi)
+    i = i % hi;
+
+  return i;
+}
+
+static void
+set_rand_seed (double val)
+{
+  union d2i { double d; int i[2]; };
+  union d2i u;
+  u.d = val;
+  int i0 = force_to_fit_range (u.i[0], 1, 2147483563);
+  int i1 = force_to_fit_range (u.i[1], 1, 2147483399);
+  F77_FCN (setsd, SETSD) (i0, i1);
+}
+
+static char *
+curr_rand_dist (void)
+{
+  if (current_distribution == uniform_dist)
+    return "uniform";
+  else if (current_distribution == normal_dist)
+    return "normal";
+  else
+    {
+      panic_impossible ();
+      return 0;
+    }
+}
+
+// Make the random number generator give us a different sequence every
+// time we start octave unless we specifically set the seed.  The
+// technique used below will cycle monthly, but it it does seem to
+// work ok to give fairly different seeds each time Octave starts.
+
+static void
+do_initialization (void)
+{
+  time_t now;
+  struct tm *tm;
+ 
+  time (&now);
+  tm = localtime (&now);
+ 
+  int hour = tm->tm_hour + 1;
+  int minute = tm->tm_min + 1;
+  int second = tm->tm_sec + 1;
+
+  int s0 = tm->tm_mday * hour * minute * second;
+  int s1 = hour * minute * second;
+
+  s0 = force_to_fit_range (s0, 1, 2147483563);
+  s1 = force_to_fit_range (s1, 1, 2147483399);
+
+  F77_FCN (setall, SETALL) (s0, s1);
+
+  initialized = 1;
+}
+
+static octave_value_list
+do_rand (const octave_value_list& args, int nargin)
+{
+  octave_value_list retval;
+
+  int n = 0;
+  int m = 0;
+
+  if (nargin == 0)
+    {
+      n = 1;
+      m = 1;
+
+      goto gen_matrix;
+    }
+  else if (nargin == 1)
+    {
+      octave_value tmp = args(0);
+
+      if (tmp.is_string ())
+	{
+	  string s_arg = tmp.string_value ();
+
+	  if (s_arg == "dist")
+	    {
+	      retval(0) = curr_rand_dist ();
+	    }
+	  else if (s_arg == "seed")
+	    {
+	      retval(0) = curr_rand_seed ();
+	    }
+	  else if (s_arg == "uniform")
+	    {
+	      current_distribution = uniform_dist;
+
+	      F77_FCN (setcgn, SETCGN) (uniform_dist);
+	    }
+	  else if (s_arg == "normal")
+	    {
+	      current_distribution = normal_dist;
+
+	      F77_FCN (setcgn, SETCGN) (normal_dist);
+	    }
+	  else
+	    error ("rand: unrecognized string argument");
+	}
+      else if (tmp.is_scalar_type ())
+	{
+	  double dval = tmp.double_value ();
+
+	  if (xisnan (dval))
+	    {
+	      error ("rand: NaN is invalid a matrix dimension");
+	    }
+	  else
+	    {
+	      m = n = NINT (tmp.double_value ());
+
+	      if (! error_state)
+		goto gen_matrix;
+	    }
+	}
+      else if (tmp.is_range ())
+	{
+	  Range r = tmp.range_value ();
+	  n = 1;
+	  m = r.nelem ();
+	  goto gen_matrix;
+	}
+      else if (tmp.is_matrix_type ())
+	{
+	  // XXX FIXME XXX -- this should probably use the function
+	  // from data.cc.
+
+	  Matrix a = args(0).matrix_value ();
+
+	  if (error_state)
+	    return retval;
+
+	  n = a.rows ();
+	  m = a.columns ();
+
+	  if (n == 1 && m == 2)
+	    {
+	      n = NINT (a (0, 0));
+	      m = NINT (a (0, 1));
+	    }
+	  else if (n == 2 && m == 1)
+	    {
+	      n = NINT (a (0, 0));
+	      m = NINT (a (1, 0));
+	    }
+	  else
+	    warning ("rand (A): use rand (size (A)) instead");
+
+	  goto gen_matrix;
+	}
+      else
+	{
+	  gripe_wrong_type_arg ("rand", tmp);
+	  return retval;
+	}
+    }
+  else if (nargin == 2)
+    {
+      if (args(0).is_string ())
+	{
+	  if (args(0).string_value () == "seed")
+	    {
+	      double d = args(1).double_value ();
+
+	      if (! error_state)
+		set_rand_seed (d);
+	    }
+	}
+      else
+	{
+	  double dval = args(0).double_value ();
+
+	  if (xisnan (dval))
+	    {
+	      error ("rand: NaN is invalid as a matrix dimension");
+	    }
+	  else
+	    {
+	      n = NINT (dval);
+
+	      if (! error_state)
+		{
+		  m = NINT (args(1).double_value ());
+
+		  if (! error_state)
+		    goto gen_matrix;
+		}
+	    }
+	}
+    }
+
+  return retval;
+
+ gen_matrix:
+
+  if (n == 0 || m == 0)
+    {
+      Matrix m;
+      retval.resize (1, m);
+    }
+  else if (n > 0 && m > 0)
+    {
+      Matrix rand_mat (n, m);
+      for (int j = 0; j < m; j++)
+	for (int i = 0; i < n; i++)
+	  {
+	    double val;
+	    switch (current_distribution)
+	      {
+	      case uniform_dist:
+		F77_FCN (dgenunf, DGENUNF) (0.0, 1.0, val);
+		rand_mat (i, j) = val;
+		break;
+
+	      case normal_dist:
+		F77_FCN (dgennor, DGENNOR) (0.0, 1.0, val);
+		rand_mat (i, j) = val;
+		break;
+
+	      default:
+		panic_impossible ();
+		break;
+	      }
+	  }
+
+      retval(0) = rand_mat;
+    }
+  else
+    error ("rand: invalid negative argument");
+
+  return retval;
+}
+
+DEFUN_DLD (rand, args, nargout,
+  "rand            -- generate a random value from a uniform distribution\n\
+\n\
+rand (N)        -- generate N x N matrix\n\
+rand (size (A)) -- generate matrix the size of A\n\
+rand (N, M)     -- generate N x M matrix\n\
+rand (SEED)     -- get current seed\n\
+rand (SEED, N)  -- set seed\n\
+\n\
+See also: randn")
+{
+  octave_value_list retval;
+
+  int nargin = args.length ();
+
+  if (nargin > 2 || nargout > 1)
+    print_usage ("rand");
+  else
+    {
+      if (! initialized)
+	do_initialization ();
+
+      retval = do_rand (args, nargin);
+    }
+
+  return retval;
+}
+
+static void
+reset_rand_generator (void *)
+{
+  F77_FCN (setcgn, SETCGN) (current_distribution);
+}
+
+DEFUN_DLD (randn, args, nargout,
+  "randn            -- generate a random value from a normal distribution\n\
+\n\
+randn (N)        -- generate N x N matrix\n\
+randn (size (A)) -- generate matrix the size of A\n\
+randn (N, M)     -- generate N x M matrix\n\
+randn (SEED)     -- get current seed\n\
+randn (SEED, N)  -- set seed\n\
+\n\
+See also: rand")
+{
+  octave_value_list retval;
+
+  int nargin = args.length ();
+
+  if (nargin > 2 || nargout > 1)
+    print_usage ("randn");
+  else
+    {
+      if (! initialized)
+	do_initialization ();
+
+      begin_unwind_frame ("randn");
+
+      // This relies on the fact that elements are popped from the
+      // unwind stack in the reverse of the order they are pushed
+      // (i.e. current_distribution will be reset before calling
+      // reset_rand_generator()).
+
+      add_unwind_protect (reset_rand_generator, 0);
+      unwind_protect_int (current_distribution);
+
+      current_distribution = normal_dist;
+
+      F77_FCN (setcgn, SETCGN) (normal_dist);
+
+      retval = do_rand (args, nargin);
+
+      run_unwind_frame ("randn");
+    }
+
+  return retval;
+}
+
+/*
+;;; Local Variables: ***
+;;; mode: C++ ***
+;;; End: ***
+*/