(* ::Package:: *)

(* ::Section:: *)
(*Header*)


(* File: RandFile.m *)
(* Description: Mathematica package for generating random numbers from random data stored in files. *)
(* Authors: Jaroslaw Miszczak <miszczak@iitis.pl> *)
(* License: GPLv3 *)


BeginPackage["RandFile`"];


Unprotect@@Names["RandFile`*"];


Clear@@Names["RandFile`*" ];


(* ::Subtitle::Closed:: *)
(*Public part*)


(* ::Section::Closed:: *)
(*Manipulating the source of random data*)


SetTrueRandomDataFile::usage = "SetTrueRandomDataFile[fName] sets the global variable TrueRandomDataFile used by the functions for generating random numbers. For example SetTrueRandomDataFile[\"/home/user/data/random_file.bin\"].";


GetTrueRandomDataFile::usage = "GetTrueRandomDataFile[] displays the value of the global variable TrueRandomDataFile.";


CloseTrueRandomDataFile::usage = "CloseTrueRandomDataFile[] closes the file assigned by the global variable set using SetTrueRandomDataFile.";


TrueRandomDataFile::usage = "TrueRandomDataFile is the global variable, defined in RandFile`Private context, storing a string with the name of the file containing random data.";


TrueRandomDataFileBytesCount::usage = "TrueRandomDataFileBytesCount is the global variable for storing a number of bytes in the TrueRandomDataFile.";


BlockTrueRandom::usage = "BlockTrueRandom[ex] evaluates expression ex and stores the starting position in the file RandFile`Private`TrueRandomDataFile. The position in the file is restored after the execution. Note that this function works only with expressions using the global variable for pointing to a file with random data. See also BlockRandom built-in Mathematica function.";


TrueRandomSequence::usage = "TrueRandomSequence[] changes the current position in the file assigned by global variable TrueRandomDataFile so that the numbers generated after the execution of this function will not overlap with the previously generated numbers.\n";


TrueRandomSequence::usage = TrueRandomSequence::usage <> "TrueRandomSequence[pos] marks the current position in the file with random data or, if the position pos is already marked, returns to this position in the file.";


TrueRandomDataMarkers::usage = "TrueRandomDataMarkers is a global variable for accumulating positions in the file with random data used by TrueRandomSequence function. Note that this array is sorted with respect to the second argument.";


SetMaxTrueRandomSequenceLength::usage = "SetMaxTrueRandomSequenceLength[len] declares the maximal length, expressed in bytes, of the true random sequence used during the session.";


GetMaxTrueRandomSequenceLength::usage = "GetMaxTrueRandomSequenceLength[] displays the maximal length of the random sequence which can be used during the session.";


GetTrueRandomDataMarkers::usage = "GetTrueRandomDataMarkers[] displays the currently set markers in the data files that are used by TrueRandomDataSequence.";


(* ::Section::Closed:: *)
(*Random integers*)


TrueRandomInteger::usage = "TrueRandomInteger[] produces an integer number in [0,1].\n";


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[{a,b}] produces an integer number in [a,b).\n";


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[{a,b), n] produces n integer numbers in [a,b).\n";


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[{a,b}, {d1,d2,...,dk}] produces a {d1,d2,...,dk}-dimensional array of integer numbers in [a,b).\n";


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[fName] uses bytes from file fName to produce a integer number in [0,1).\n";


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[{a,b}, fName] uses bytes from file fName to produce an integer number in [a,b).\n";


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[{a,b}, n, fName] uses bytes from file fName to produce n integer numbers in [a,b).\n";


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[{a,b}, {d1,d2,...,dk}, fName] uses bytes from file fName to produce a {d1,d2,...,dk}-dimensional array of integer numbers in [a,b).\n";


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[PoissonDistribution[\[Mu]]] produces random integer from PoissonDistribution[\[Mu]].\n"


TrueRandomInteger::usage = TrueRandomInteger::usage <> "TrueRandomInteger[PoissonDistribution[\[Mu]], fName] uses bytes from file fName to produce random integer from PoissonDistribution[\[Mu]]."


(* ::Section::Closed:: *)
(*Random reals*)


TrueRandomReal::usage = "TrueRandomReal[] produces 32-bit real number in [a,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[{a,b}] produces 32-bit real number in [a,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[b] produces 32-bit real number in [0,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[{a,b}, n] produces n 32-bit real numbers in [a,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[{a,b}, {d1,d2,...,dk}] produces a {d1,d2,...,dk}-dimensional array of 32-bit real numbers in [a,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[fName] uses bytes from the file fName to produce 32-bit real number in [0,1].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[{a,b}, fName] uses bytes from the file fName to produce 32-bit real number in [a,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[b, fName] uses bytes from the file fName to produce 32-bit real number in [0,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[{a,b}, n, fName] uses bytes from the file fName to produce n 32-bit real numbers in [a,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[{a,b}, {d1,d2,...,dk}, fName] uses bytes from the file fName to produce a {d1,d2,...,dk}-dimensional array of 32-bit real numbers in [a,b].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[NormalDistribution[\[Mu],\[Sigma]]] produces 32-bit real number from NormalDistribution[\[Mu],\[Sigma]].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[NormalDistribution[\[Mu],\[Sigma]], n] produces n 32-bit real numbers from NormalDistribution[\[Mu],\[Sigma]].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[NormalDistribution[\[Mu],\[Sigma]], {d1,d2,...,dk}] produces a {d1,d2,...,dk}-dimensional array of 32-bit real numbers from NormalDistribution[\[Mu],\[Sigma]].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[NormalDistribution[\[Mu],\[Sigma]], fName] uses bytes from file fName to produce 32-bit real number from NormalDistribution[\[Mu],\[Sigma]].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[NormalDistribution[\[Mu],\[Sigma]], n, fName] uses bytes from file fName to produce n 32-bit real numbers from NormalDistribution[\[Mu],\[Sigma]].\n";


TrueRandomReal::usage = TrueRandomReal::usage <> "TrueRandomReal[NormalDistribution[\[Mu],\[Sigma]], {d1,d2,...,dk}, fName] uses bytes from file fName to produce a {d1,d2,...,dk}-dimensional array of 32-bit real numbers from NormalDistribution[\[Mu],\[Sigma]].";


(* ::Section::Closed:: *)
(*Random complex*)


TrueRandomComplex::usage = "TrueRandomComplex[] gives a true random complex number with real and imaginary parts in the range 0 to 1.\n";


TrueRandomComplex::usage = TrueRandomComplex::usage <> "TrueRandomComplex[{min,max}] gives a true random complex number in the rectangle with corners given by the complex numbers min and max.\n"


TrueRandomComplex::usage = TrueRandomComplex::usage <> "TrueRandomComplex[{min,max}, n] gives a list of d true random complex numbers in a given rectangle.\n";


TrueRandomComplex::usage = TrueRandomComplex::usage <> "TrueRandomComplex[{min,max}, {d1,d2,...,dk}] gives a {d1,d2,...,dk}-simensional array of true random complex numbers in a given rectangle.";


(* ::Section::Closed:: *)
(*Deprecated Random[] interface*)


TrueRandom::usage = "TrueRandom[type,range] gives a true random number of type Real, Integer or Complex in a specified range. See built-in Mathematica function Random.";


(* ::Section::Closed:: *)
(*TrueRandomChoice*)


TrueRandomChoice::usage = "TrueRandomChoice[{e1,e2,...,ek}] gives a true random choice of one of {e1,e2,...,en}.\n";


TrueRandomChoice::usage = TrueRandomChoice::usage <> "TrueRandomChoice[{e1,e2,...,ek}, n] gives a list of n true random choices.\n";


TrueRandomChoice::usage = TrueRandomChoice::usage <> "TrueRandomChoice[elist, {d1,d2,...,dk}] gives a {d1,d2,...,dk}-dimensional array of true random choices.\n";


TrueRandomChoice::usage = TrueRandomChoice::usage <> "TrueRandomChoice[{w1,w2,...,wn} -> {e1,e2,...,en}] gives a true random choice weighted by {w1,w2,...,wn}.\n";


TrueRandomChoice::usage = TrueRandomChoice::usage <> "TrueRandomChoice[{w1,w2,...,wn} -> {e1,e2,...,en}, k] gives k true random choices weighted by {w1,w2,...,wn}.\n";


TrueRandomChoice::usage = TrueRandomChoice::usage <> "TrueRandomChoice[{w1,w2,...,wn} -> {e1,e2,...,en}, {d1,d2,...,dk}] gives a {d1,d2,...,dk}-dimensional array of true random choices weighted by {w1,w2,...,wn}.";


(* ::Section::Closed:: *)
(*TrueRandomSample*)


TrueRandomSample::usage = "TrueRandomSample[l] gives a true random permutation of the list l.\n";


TrueRandomSample::usage = TrueRandomSample::usage <> "TrueRandomSample[l,n] gives n elements from the true random sample of the list l. Note that it is possible to take at most Length[l] elements.\n";


TrueRandomSample::usage = TrueRandomSample::usage <> "TrueRandomSample[{w1,w2,...,wn} -> {e1,e2,...,en}, k] gives k elements from the non-uniform sample of elements {e1,e2,...,en} with weights {w1,w2,...,wn}.";


(* ::Subtitle:: *)
(*Private part*)


Begin["`Private`"];


(* ::Section:: *)
(*Change log, version information*)


randfileHistory = {
	{"0.0.1", "09/10/2012", "Jarek", "Initial version with basic implementation of TrueRandomReal and TrueRandomInteger."},
	{"0.0.2", "18/10/2012", "Jarek", "Tests with functions using global variable."},
	{"0.0.3", "18/10/2012", "Jarek", "More error checking."},
	{"0.0.4", "26/10/2012", "Jarek", "Two patterns for reading random reals using global variable, functions for 64-bit real numbers."},
	{"0.0.5", "06/11/2012", "Jarek", "BlockTrueRandom, spelling improvements."},
	{"0.0.6", "21/11/2012", "Jarek", "Basic versions of TrueRandom and TrueRandomChoice, private function TrueRandomChoiceIntervals."},
	{"0.0.7", "22/11/2012", "Jarek", "More patterns for TrueRandomChoice, TrueRandomInteger, TrueRandomReal and TrueRandomDouble, private functions CallAsTableVersion and CallAsArrayVersion."},
	{"0.0.8", "26/11/2012", "Jarek", "Poisson distribution and normal distribution."},
	{"0.0.9", "03/12/2012", "Jarek", "TrueRandomSample and TrueRandomComplex, preliminary version of TrueRandomSequence, spelling improvements."},
	{"0.0.10", "04/12/2012", "Jarek", "Fixed pattern maching in TrueRandomInteger, changes in TrueRandomSequence, fixed SetTrueRandomDataFile."},
	{"0.0.11", "05/12/2012", "Jarek", "Major changes in TrueRandomSequence, added TrueRandomDataMarkers global variable, improved SetTrueRandomDataFile."},
	{"0.0.12", "14/12/2012", "Jarek", "Allocation and checking mechanism for random sequences."},
	{"0.0.13", "16/01/2013", "Jarek", "Documentation improvements."},
	{"0.0.14", "20/01/2013", "Jarek", "One new pattern for integers and reals, documentation improvements."},
	{"0.0.15", "21/01/2013", "Jarek", "Fixed functions for producing complex numbers."},
	{"0.0.16", "23/01/2013", "Jarek", "Removed TrueRandomDouble functions."},
	{"0.0.17", "25/01/2013", "Jarek", "Major documentation improvements, TrueRandomChoice fixed."}
};


randfileVersion = Last[randfileHistory][[1]];


randfileLastModification = Last[randfileHistory][[2]];


(* ::Section:: *)
(*Error messages*)


RandFileNoMoreDataError::argerr = "EndOfFile occured in `1`! I'm unable to provide more random data.";


RandFileNoSuchFileError::argerr = "File \"`1`\" not found! Please make sure that the provided path is correct.";


RandFileWrongFormatError::argerr = "I can't read from \"`1`\"! It should be a binary file.";


RandFileStreamAlreayOpen::fileerr = "File `1` is already opened for reading. I won't open new stream for this file.";


RandFileIntegerRangeError::argerr = "Provided range [`1`, `2`) is too wide! I can provide integers within at most 128 bit range.";


RandFileUnknownTypeError::arrerr = "I can't handel data of type \"`1`\"! Please use \"Integer\", \"Real\" or \"Complex\" instead.";


SetMaxTrueRandomSequenceLength::lenerr = "I can't allocate `1` in file \"`2`\" (containing `3` bytes). Please change the requested length - it should be non-zero.";


SetMaxTrueRandomSequenceLength::deferr = "Value of MaxTrueRandomSequenceLength variable cannot be redefined! You need to restart Mathematica kernel in order to define new value of this variable.";


TrueRandomSequence::markererr = "Unexpected problem with function TrueRandomSequence. Please contact the developer of this package."


TrueRandomSequence::allocerr = "I'm unable to allocate sequence with label `1`, as it requires a file with more than `2` bytes (used file have only `3` bytes).";


TrueRandomSequence::rangeoverlap = "Requested usage of data from the next sequence. This is not allowed, as the two different sequences cannot overlap.";


(* ::Section:: *)
(*Private patterns for calling functions in array versions*)


CallAsArrayVersion[funCall_,dims_List]:=Fold[Partition,Table[funCall,{Times@@Flatten[dims]}],Reverse[dims]][[1]];


CallAsTableVersion[funCall_,n_Integer]:=Table[funCall,{n}];


SetAttributes[CallAsArrayVersion,HoldAll];


SetAttributes[CallAsTableVersion,HoldAll];


(* ::Section:: *)
(*Manipulating the source of random data*)


SetTrueRandomDataFile[fileName_String]:=Block[{findFile=FindFile[fileName]}, 
	If[findFile==ToString[$Failed],
		Message[RandFileNoSuchFileError::argerr,findFile],
		(*else*)
		If[FileFormat[findFile]=="Binary",		
			RandFile`Private`TrueRandomDataFile = findFile;
			RandFile`Private`TrueRandomDataFileBytesCount = FileByteCount[findFile];
			If[Length[Streams[RandFile`Private`TrueRandomDataFile]]==1,
				Message[RandFileStreamAlreayOpen::fileerr,findFile],
				OpenRead[RandFile`Private`TrueRandomDataFile,BinaryFormat->True];
				Print["Using file \"" <> fileName <> "\" which contains " <> ToString[RandFile`Private`TrueRandomDataFileBytesCount] <> " bytes of data."];
			]
			,
			(*else*)
			Message[RandFileWrongFormatError::argerr,findFile]
		]
	]
];


GetTrueRandomDataFile[]:=If[ValueQ[RandFile`Private`TrueRandomDataFile],Print[RandFile`Private`TrueRandomDataFile]];


CloseTrueRandomDataFile[]:=Close[RandFile`Private`TrueRandomDataFile];


BlockTrueRandom[f_]:=Module[{startPos,result},
	startPos=StreamPosition[RandFile`Private`TrueRandomDataFile];
	result=f;
	SetStreamPosition[RandFile`Private`TrueRandomDataFile,startPos];
	result
];


SetAttributes[BlockTrueRandom,HoldAll];


SetMaxTrueRandomSequenceLength[len_Integer]:=Block[{},
	If[ValueQ[RandFile`Private`MaxTrueRandomSequenceLength],
		Message[SetMaxTrueRandomSequenceLength::deferr],
		If[0<len<=RandFile`Private`TrueRandomDataFileBytesCount,
			RandFile`Private`MaxTrueRandomSequenceLength = len;,
			Message[SetMaxTrueRandomSequenceLength::lenerr,len,RandFile`Private`TrueRandomDataFile,RandFile`Private`TrueRandomDataFileBytesCount]		
		];
	];
];


GetMaxTrueRandomSequenceLength[] = RandFile`Private`MaxTrueRandomSequenceLength;


GetTrueRandomDataMarkers[] = RandFile`Private`TrueRandomDataMarkers;


TrueRandomSequence[pos_Integer]:=Block[{posMarkers,allMarkers},
	posMarkers=Cases[RandFile`Private`TrueRandomDataMarkers,{pos,_}];
	allMarkers=Cases[RandFile`Private`TrueRandomDataMarkers,{_,_}];
	Switch[Length[posMarkers],
		0, (* no markers at this position yet *)
			If[Length[allMarkers]==0, (* this is the first one marker to set *)
				RandFile`Private`TrueRandomDataCurrentMarker={pos,0};
				AppendTo[RandFile`Private`TrueRandomDataMarkers,RandFile`Private`TrueRandomDataCurrentMarker];
				Sort[RandFile`Private`TrueRandomDataMarkers,#1[[2]]<#2[[2]]&];
				SetStreamPosition[RandFile`Private`TrueRandomDataFile,(RandFile`Private`TrueRandomDataCurrentMarker)[[2]]];
				(* there are alreay some markers so we need to allocate this one after the one with the largets position *),
				If[Max[allMarkers\[Transpose][[2]]]+RandFile`Private`MaxTrueRandomSequenceLength<RandFile`Private`TrueRandomDataFileBytesCount,
					RandFile`Private`TrueRandomDataCurrentMarker={pos,Max[allMarkers\[Transpose][[2]]]+RandFile`Private`MaxTrueRandomSequenceLength};					
					AppendTo[RandFile`Private`TrueRandomDataMarkers,RandFile`Private`TrueRandomDataCurrentMarker];
					Sort[RandFile`Private`TrueRandomDataMarkers,#1[[2]]<#2[[2]]&];
					SetStreamPosition[RandFile`Private`TrueRandomDataFile,(RandFile`Private`TrueRandomDataCurrentMarker)[[2]]];				
					,
					Message[TrueRandomSequence::allocerr,pos,Max[allMarkers\[Transpose][[2]]]+RandFile`Private`MaxTrueRandomSequenceLength,RandFile`Private`TrueRandomDataFileBytesCount];
				];
			];,
		1,
			RandFile`Private`TrueRandomDataCurrentMarker = posMarkers[[1]];
			SetStreamPosition[RandFile`Private`TrueRandomDataFile,(RandFile`Private`TrueRandomDataCurrentMarker)[[2]]];,
		_,
			Message[TrueRandomSequence::markererr];
	];
];


TrueRandomSequence[]:=Block[{pseudoRandLabel},
	pseudoRandLabel = RandomInteger[{-RandFile`Private`TrueRandomDataFileBytesCount,-1}];
	(* make sure that the label is not used *)
	While[Count[RandFile`Private`TrueRandomDataMarkers\[Transpose][[1]],pseudoRandLabel]!=0,
		pseudoRandLabel = RandomInteger[{-RandFile`Private`TrueRandomDataFileBytesCount,-1}];
	];
	TrueRandomSequence[pseudoRandLabel];
];


RandFile`Private`TrueRandomDataMarkers = {};


(* ::Section:: *)
(*Random integers*)


TrueRandomInteger[{min_,max_},fileName_String]:=Block[{range,bitRange,byteRange,maxRange,rndLimit,readForm,rawRndInt,curSeqPos},
	range=Abs[max-min]+1;
	bitRange=Ceiling[Log2[range]];
	byteRange=IntegerPart[(bitRange-1)/8]+1;
	
	If[Length[RandFile`Private`TrueRandomDataMarkers]>0,
		curSeqPos = Position[RandFile`Private`TrueRandomDataMarkers,RandFile`Private`TrueRandomDataCurrentMarker][[1,1]];
	];

	(*TODO: add check if we even use TrueRandomDataMarkers table *)
	(*SUGGESTION: Evaluation pattern similar to CallAsTable etc. but controlled with a global variable or a length of andFile`Private`TrueRandomDataMarkers array *)
	If[Length[RandFile`Private`TrueRandomDataMarkers]==0 || StreamPosition[RandFile`Private`TrueRandomDataFile]+byteRange <= RandFile`Private`TrueRandomDataMarkers[[curSeqPos,2]]+RandFile`Private`MaxTrueRandomSequenceLength,
		(* there is enough data in the current chunk *)	

		(* assign range and form used in BinaryRead and for scaling *)
		Which[ 
			bitRange<=8, readForm="UnsignedInteger8";maxRange=2^8,
			8<bitRange<=16, readForm="UnsignedInteger16";maxRange=2^16,
			16<bitRange<=24, readForm="UnsignedInteger24";maxRange=2^24,
			24<bitRange<=32, readForm="UnsignedInteger24";maxRange=2^32,
			32<bitRange<=64, readForm="UnsignedInteger24";maxRange=2^64,
			64<bitRange<=128, readForm="UnsignedInteger24";maxRange=2^128,
			_,Message[RandFileIntegerRangeError::argerr, min, max];
		];

		(* use only raw numbers from [0, rndLimit-1] *)
		rndLimit=maxRange-Mod[maxRange,range]; 

		(* read one raw integer number *)
		rawRndInt=BinaryRead[fileName,readForm]; 

		Switch[rawRndInt,
			_Integer (* there are still data to read *),
			(* discard the number if it is out of the range *)
			While[rawRndInt>=rndLimit, 
				rawRndInt=BinaryRead[fileName,readForm];
			];
			(* scale the raw number *)
			Mod[rawRndInt,range]+min,
			EndOfFile,	(* no more data to read - return Null *)
			Message[RandFileNoMoreDataError::argerr,fileName];
			Null
		],
		(* else for checking if the sequence is OK starts here *)
		Message[TrueRandomSequence::rangeoverlap];
	]
];


TrueRandomInteger[{min_,max_}]:=TrueRandomInteger[{min,max}, RandFile`Private`TrueRandomDataFile];


TrueRandomInteger[{min_,max_},n_Integer]:=CallAsTableVersion[TrueRandomInteger[{min,max}],n];


TrueRandomInteger[{min_,max_},dims_List]:=CallAsArrayVersion[TrueRandomInteger[{min,max}],dims];


TrueRandomInteger[fileName_String]:=TrueRandomInteger[{0,1},fileName];


TrueRandomInteger[fileName_String]:=TrueRandomInteger[{0,1},fileName];


TrueRandomInteger[n_Integer, fileName_String]:=CallAsTableVersion[TrueRandomInteger[fileName],n];


TrueRandomInteger[dims_List, fileName_String]:=CallAsArrayVersion[TrueRandomInteger[fileName],dims];


TrueRandomInteger[]:=TrueRandomInteger[RandFile`Private`TrueRandomDataFile];


TrueRandomInteger[dist_PoissonDistribution]:=InverseCDF[dist,TrueRandomReal[]];


TrueRandomInteger[dist_PoissonDistribution, fileName_String]:=InverseCDF[dist,TrueRandomReal[fileName]];


(* ::Section:: *)
(*Random reals*)


TrueRandomReal[fileName_String]:=Block[{rawRndInt,bits,byteRange,curSeqPos},

	byteRange=4;
	
	If[Length[RandFile`Private`TrueRandomDataMarkers]>0,
		curSeqPos = Position[RandFile`Private`TrueRandomDataMarkers,RandFile`Private`TrueRandomDataCurrentMarker][[1,1]];
	];

	(*TODO: add check if we even use TrueRandomDataMarkers table *)
	(*SUGGESTION: Evaluation pattern similar to CallAsTable etc. but controlled with a global variable or a length of andFile`Private`TrueRandomDataMarkers array *)
	If[Length[RandFile`Private`TrueRandomDataMarkers]==0 || StreamPosition[RandFile`Private`TrueRandomDataFile]+byteRange <= RandFile`Private`TrueRandomDataMarkers[[curSeqPos,2]]+RandFile`Private`MaxTrueRandomSequenceLength,

		rawRndInt=BinaryRead[fileName, "UnsignedInteger32"];

		Switch[rawRndInt,
			_Integer (* there are still data to read *),
				bits=IntegerDigits[rawRndInt,2];
				Reverse[bits].Map[2.0^-#&,Range[Length[bits]]],
			EndOfFile,	(* no more data to read - return Null*)
				Message[RandFileNoMoreDataError::argerr,fileName];
				Null
		],
		(* else for checking if the sequence is OK starts here *)
		Message[TrueRandomSequence::rangeoverlap];
	]
];


TrueRandomReal[{a_,b_},fileName_String]:=Block[{delta=Abs[b-a]},
	a + delta*TrueRandomReal[fileName]
];


TrueRandomReal[b_,fileName_String]:=TrueRandomReal[{0,b},fileName];


TrueRandomReal[{a_,b_},n_Integer,fileName_String]:=CallAsTableVersion[TrueRandomReal[{a,b},fileName],n];


TrueRandomReal[{a_,b_},dims_List,fileName_String]:=CallAsArrayVersion[TrueRandomReal[{a,b},fileName],dims];


TrueRandomReal[]:=TrueRandomReal[RandFile`Private`TrueRandomDataFile];


TrueRandomReal[{a_,b_}]:=TrueRandomReal[{a,b},RandFile`Private`TrueRandomDataFile];


TrueRandomReal[b_]:=TrueRandomReal[{0,b}];


TrueRandomReal[{a_,b_},n_Integer]:=CallAsTableVersion[TrueRandomReal[{a,b}],n];


TrueRandomReal[{a_,b_},dims_List]:=CallAsArrayVersion[TrueRandomReal[{a,b}],dims];


TrueRandomReal[dist_NormalDistribution]:=Mean[dist]+Sqrt[2]Sqrt[Variance[dist]] InverseErf[-1+2 TrueRandomReal[{0,1}]];


TrueRandomReal[dist_NormalDistribution,n_Integer]:=CallAsTableVersion[TrueRandomReal[dist],n];


TrueRandomReal[dist_NormalDistribution,dims_List]:=CallAsArrayVersion[TrueRandomReal[dist],dims];


TrueRandomReal[dist_NormalDistribution,fileName_String]:=Mean[dist]+Sqrt[2]Sqrt[Variance[dist]] InverseErf[-1+2 TrueRandomReal[{0,1},fileName]];


TrueRandomReal[dist_NormalDistribution,n_Integer,fileName_String]:=CallAsTableVersion[TrueRandomReal[dist,fileName],n];


TrueRandomReal[dist_NormalDistribution,dims_List,fileName_String]:=CallAsArrayVersion[TrueRandomReal[dist,fileName],dims];


(* ::Section:: *)
(*Random complex*)


TrueRandomComplex[{min_,max_},file_:RandFile`Private`TrueRandomDataFile]:=Block[{reRange=Abs[Re[max]-Re[min]],imRange=Abs[Im[max]-Im[min]]},
	TrueRandomReal[reRange,file] + I TrueRandomReal[imRange,file] + Min[Re[min],Re[max]] + I Min[Im[min],Im[max]]
];


TrueRandomComplex[file_:RandFile`Private`TrueRandomDataFile]:=TrueRandomComplex[{0,1+I},file];


TrueRandomComplex[max_,file_:RandFile`Private`TrueRandomDataFile]:=TrueRandomComplex[{0,max},file];


TrueRandomComplex[{min_,max_},n_Integer,file_:RandFile`Private`TrueRandomDataFile]:=CallAsTableVersion[TrueRandomComplex[{min,max},file],n];


TrueRandomComplex[{min_,max_},dims_list,file_:RandFile`Private`TrueRandomDataFile]:=CallAsArrayVersion[TrueRandomComplex[{min,max},file],dims];


(* ::Section:: *)
(*Deprecated Random[] interface*)


(* NOTE: This is added only to provide a basic compatibility layer and may fail to give the proper results in some cases. *)
TrueRandom[type_:Real,range_:{0,1},file_:RandFile`Private`TrueRandomDataFile]:=Switch[type,
	Integer,TrueRandomInteger[range,file],
	Real,TrueRandomReal[range,file],
	Complex,TrueRandomComplex[range,file],
	_,Message[RandFileUnknownTypeError::argerr,type]
];


(* ::Section:: *)
(*TrueRandomChoice*)


TrueRandomChoice[list_List]:=TrueRandomChoice[Rule[ConstantArray[1,Length[list]],list]];


TrueRandomChoice[list_List,n_Integer]:=Table[TrueRandomChoice[list],{n}];


TrueRandomChoice[list_List,dims_List]:=CallAsArrayVersion[TrueRandomChoice[list],dims];


TrueRandomChoice[rule_Rule,n_Integer]:=Table[TrueRandomChoice[rule],{n}];


TrueRandomChoice[rule_Rule,dims_List]:=CallAsArrayVersion[TrueRandomChoice[rule],dims];


TrueRandomChoice[rule_Rule]:=Module[{sortRule,choiceWeights,indexReal,byteRange,curSeqPos},

	byteRange=4; (* we need only one 32-bit real number for each call *)
	
	If[Length[RandFile`Private`TrueRandomDataMarkers]>0,
		curSeqPos = Position[RandFile`Private`TrueRandomDataMarkers,RandFile`Private`TrueRandomDataCurrentMarker][[1,1]];
	];

	(*TODO: add check if we even use TrueRandomDataMarkers table *)
	(*SUGGESTION: Evaluation pattern similar to CallAsTable etc. but controlled with a global variable or a length of andFile`Private`TrueRandomDataMarkers array *)
	If[Length[RandFile`Private`TrueRandomDataMarkers]==0 || StreamPosition[RandFile`Private`TrueRandomDataFile]+byteRange <= RandFile`Private`TrueRandomDataMarkers[[curSeqPos,2]]+RandFile`Private`MaxTrueRandomSequenceLength,

		sortRule=Sort[{rule[[1]],rule[[2]]}\[Transpose],#1[[1]]<#2[[1]]&]\[Transpose];
		choiceWeights=TrueRandomChoiceIntervals[sortRule[[1]]];

		(* this functions makes use of the global source of the randomenss *)
		indexReal=TrueRandomReal[{0,Last[choiceWeights]}];

		Return[(sortRule[[2]])[[Position[Map[indexReal<=#&,choiceWeights],True][[1,1]]]]],

		(* else for checking if the sequence is OK starts here *)
		Message[TrueRandomSequence::rangeoverlap];
	];
];


TrueRandomChoiceIntervals[ruleWeights_]:=TrueRandomChoiceIntervals[ruleWeights]=Block[{choiceIntervals,extRuleWeights},
	extRuleWeights=Prepend[ruleWeights,0];
	choiceIntervals=ConstantArray[0,Length[extRuleWeights]];
	choiceIntervals[[1]]=extRuleWeights[[1]];
	choiceIntervals[[2]]=extRuleWeights[[2]];
	For[i=3,i<=Length[extRuleWeights],i++,
	 choiceIntervals[[i]]=choiceIntervals[[i-1]]+(extRuleWeights[[i]]/extRuleWeights[[i-1]])(choiceIntervals[[i-1]]-choiceIntervals[[i-2]]);
	];
	choiceIntervals=Drop[choiceIntervals,1]
];


(* ::Section:: *)
(*TrueRandomSample*)


TrueRandomSample[list_List]:=TrueRandomChoice[Permutations[list]];


TrueRandomSample[list_List,n_Integer]:=Take[TrueRandomSample[list],n];


TrueRandomSample[rule_Rule,n_Integer]:=Block[{sample={}},
	While[Length[sample]<n,sample=Union[Append[sample,RandomChoice[Rule[w,list]]]]];
	sample
];


TrueRandomSample[rule_Rule,1]:=TrueRandomChoice[rule];


(* ::Section:: *)
(*Welcome message*)


Print["Package RandFile version ", RandFile`Private`randfileVersion, " (last modification: ", RandFile`Private`randfileLastModification, ")."];


Print["Usage notes:"];


Print["1) Almost all provided functions require to set a global variable pointing to file with random data! This can be done by using \!\(\*
StyleBox[\"SetTrueRandomDataFile\",\nFontWeight->\"Bold\"]\) function. For example \!\(\*
StyleBox[\"SetTrueRandomDataFile\",\nFontWeight->\"Bold\"]\)[\"/home/user/data/random_file.bin\"]. Please mind that it is advised to use this function only once during session."];


Print["2) If you intend to use \!\(\*
StyleBox[\"TrueRandomSequence\",\nFontWeight->\"Bold\"]\) function you must use \!\(\*
StyleBox[\"SetMaxTrueRandomSequenceLength\",\nFontWeight->\"Bold\"]\) and declare at least one sequence. Currently declared sequences can be displayed by calling \!\(\*
StyleBox[\"GetTrueRandomDataMarkers\",\nFontWeight->\"Bold\"]\)[]. Once defined, the used maximal length cannot be changed during the session."]


End[];


(* ::Section:: *)
(*Footer*)


Protect@@Names["RandFile`*"]


EndPackage[];
