"""
   Author: Justin Cappos

   Start Date: 19 July 2008

   Description:

   Miscellaneous functions for the sandbox.   Random, exitall, getruntime, 
   etc.
"""

import restrictions
import nanny
import random           # for random.random()
import nonportable      # for harshexit() and getruntime
import threading        # for Lock()

# Public interface!
def randomfloat():
  """
   <Purpose>
      Return a random number in the range [0.0, 1.0)

   <Arguments>
      None

   <Exceptions>
      None.

   <Side Effects>
      This function is metered because it may involve using a hardware
      source of randomness.

   <Returns>
      The number (a float)
  """

  restrictions.assertisallowed('randomfloat')
  nanny.tattle_quantity('random',1)

  return random.random()

# Public interface!
def getruntime():
  """
   <Purpose>
      Return the amount of time the program has been running.   This is in
      wall clock time.   This function is not guaranteed to always return
      increasing values due to NTP, etc.

   <Arguments>
      None

   <Exceptions>
      None.

   <Side Effects>
      None

   <Remarks>
      Accurate granularity not guaranteed past 1 second.

   <Returns>
      The elapsed time as float
  """
  restrictions.assertisallowed('getruntime')
  return nonportable.getruntime()


# public interface
def exitall():
  """
   <Purpose>
      Allows the user program to stop execution of the program without
      passing an exit event to the main program. 

   <Arguments>
      None.

   <Exceptions>
      None.

   <Side Effects>
      Interactions with timers and connection / message receiving functions 
      are undefined.   These functions may be called after exit and may 
      have undefined state.

   <Returns>
      None.   The current thread does not resume after exit
  """

  restrictions.assertisallowed('exitall')

  nonportable.harshexit(200)




# public interface
def getlock():
  """
   <Purpose>
      Returns a lock object to the user program.    A lock object supports
      two functions: acquire and release.   See threading.Lock() for details

   <Arguments>
      None.

   <Exceptions>
      None.

   <Side Effects>
      None.

   <Returns>
      The lock object.
  """

  restrictions.assertisallowed('getlock')

  # I'm a little worried about this, but it should be safe.
  return threading.Lock()


@@ -152,16 +152,407 @@
 %0A   etc.
+%0A%0A   %3CModified%3E%0A     Anthony - May 7 2009, changed the source of random data which is%0A     used in randomfloat. Now uses os.urandom to get random bytes,%0A     transforms the bytes into a random integer then uses it to%0A     create a float of 53bit resolution.%0A     Modified scheme from the random() function of the SystemRandom class,%0A     as defined in source code python 2.6.2 Lib/random.py 
 %0A%22%22%22%0A%0Aim
@@ -589,22 +589,18 @@
 %0Aimport 
-random
+os
         
@@ -606,28 +606,30 @@
     
+    
 # for 
-random.
+os.u
 random(
+7
 )%0Aim
@@ -780,25 +780,24 @@
 rpose%3E%0A     
- 
 Return a ran
@@ -830,16 +830,135 @@
 .0, 1.0)
+ using sources %0A     provided by the operating system (such as /dev/urandom on Unix or%0A     CryptGenRandom on Windows).
 %0A%0A   %3CAr
@@ -963,33 +963,32 @@
 Arguments%3E%0A     
- 
 None%0A%0A   %3CExcept
@@ -990,38 +990,109 @@
 xceptions%3E%0A     
- None.
+NotImplementedError if a OS-specific source of randomness%0A     is not found. 
 %0A%0A   %3CSide Effec
@@ -1091,25 +1091,24 @@
 de Effects%3E%0A
- 
      This fu
@@ -1165,17 +1165,16 @@
 ardware%0A
- 
      sou
@@ -1207,25 +1207,24 @@
 turns%3E%0A     
- 
 The number (
@@ -1325,33 +1325,460 @@
 ,1)%0A
+  
 %0A  
-return random.random()
+# If an OS-specific source of randomness is not a found%0A  # a NotImplementedError would be raised.%0A  # Get 56 bits of random data%0A  randombytes = os.urandom(7)%0A  %0A  randomint = 0L%0A  for i in range(0, 7):%0A    randomint = (randomint %3C%3C 8) %0A    randomint = randomint + ord(randombytes%5Bi%5D) %0A%0A  # Trim off the excess bits to get 53bits%0A  randomint = randomint %3E%3E 3%0A  # randomint is a number between 0 and 2**(53) - 1%0A  %0A  return randomint * (2**(-53))%0A
 %0A%0A# 

Update emulmisc.py so that randomfloat() now uses os.urandom() as a source for random bytes, instead of just returning random.random()