/* Copyright (c) Microsoft Corporation. Licensed under the MIT License. */ /*************************************************************************** Author: ShonK Project: Kauai Reviewed: Copyright (c) Microsoft Corporation Some "random" stuff ***************************************************************************/ #include "util.h" ASSERTNAME RTCLASS(RND) RTCLASS(SFL) /*************************************************************************** Constructs a pseudo-random number generator. If luSeed is zero, generates a seed from the current system time (TsCurrentSystem). ***************************************************************************/ RND::RND(ulong luSeed) { if (0 == luSeed) { luSeed = TsCurrentSystem(); SwapBytesRglw((long *)&luSeed, 1); } _luSeed = luSeed; AssertThis(0); } /*************************************************************************** Return the next pseudo-random number within the range 0 to lwLim - 1, inclusive. ***************************************************************************/ long RND::LwNext(long lwLim) { AssertThis(0); AssertIn(lwLim, 1, kcbMax); // high bits are more random than the low ones // See Knuth vol 2, page 102, line 24 of table 1. // value of kdluRand doesn't matter much const ulong kluRandMul = 1566083941L; const kdluRand = 2531011L; long lw; _luSeed = _luSeed * kluRandMul + kdluRand; // multiply lw by lwLim and divide by 2^32 #ifdef IN_80386 ulong luSeedT = _luSeed; __asm { mov eax,luSeedT mul lwLim mov lw,edx } #else //!IN_80386 double dou; dou = (double)_luSeed * lwLim / (double)0x40000000 / 4; lw = (long)dou; #endif //!IN_80386 Assert(lw < lwLim, "random number out of range"); return lw; } /*************************************************************************** Constructs a shuffled array. ***************************************************************************/ SFL::SFL(ulong luSeed) : RND(luSeed) { _clw = 0; _ilw = 0; _fCustom = fFalse; _hqrglw = hqNil; AssertThis(0); } /*************************************************************************** Destructs a shuffled array. ***************************************************************************/ SFL::~SFL(void) { AssertThis(0); FreePhq(&_hqrglw); } #ifdef DEBUG /*************************************************************************** Assert the validity of a SFL. ***************************************************************************/ void SFL::AssertValid(ulong grf) { SFL_PAR::AssertValid(0); if (_hqrglw != hqNil) { AssertHq(_hqrglw); Assert(CbOfHq(_hqrglw) == LwMul(_clw, size(long)), "HQ wrong size"); } else Assert(0 == _clw, "_clw wrong"); } /*************************************************************************** Mark memory for the SFL. ***************************************************************************/ void SFL::MarkMem(void) { AssertValid(0); SFL_PAR::MarkMem(); MarkHq(_hqrglw); } #endif //DEBUG /*************************************************************************** Shuffle the numbers [0, lwLim). ***************************************************************************/ void SFL::Shuffle(long lwLim) { AssertThis(0); AssertIn(lwLim, 0, kcbMax); long ilw; long *qrglw; if (!_FEnsureHq(lwLim)) return; _ilw = 0; Assert(_clw == lwLim, "wrong _clw"); qrglw = (long *)QvFromHq(_hqrglw); // fill the array with [0, _clw) for (ilw = 0; ilw < _clw; ilw++) qrglw[ilw] = ilw; _fCustom = fFalse; _ShuffleCore(); } /*************************************************************************** Fill the SFL with the values in prglw and shuffle them. ***************************************************************************/ void SFL::ShuffleRglw(long clw, long *prglw) { AssertThis(0); AssertIn(clw, 0, kcbMax); AssertPvCb(prglw, LwMul(clw, size(long))); if (!_FEnsureHq(clw)) return; _ilw = 0; Assert(_clw == clw, "wrong _clw"); // fill the HQ with the stuff in prglw CopyPb(prglw, QvFromHq(_hqrglw), LwMul(clw, size(long))); _fCustom = fTrue; _ShuffleCore(); } /*************************************************************************** Shuffle the entries in the HQ. ***************************************************************************/ void SFL::_ShuffleCore(void) { AssertThis(0); Assert(_clw > 0, 0); long lw; long ilw, ilwSwap; long *qrglw; //swap stuff qrglw = (long *)QvFromHq(_hqrglw); for (ilw = _clw; --ilw > 0; ) { ilwSwap = RND::LwNext(ilw + 1); if (ilwSwap < ilw) { lw = qrglw[ilw]; qrglw[ilw] = qrglw[ilwSwap]; qrglw[ilwSwap] = lw; } } } /*************************************************************************** Make sure the HQ is the correct size and set clw appropriately. ***************************************************************************/ bool SFL::_FEnsureHq(long clw) { AssertThis(0); AssertIn(clw, 0, kcbMax); if (clw <= 0) goto LFail; if (_hqrglw == hqNil && !FAllocHq(&_hqrglw, LwMul(_clw = clw, size(long)), fmemNil, mprNormal)) { goto LFail; } if (clw != _clw && !FResizePhq(&_hqrglw, LwMul(_clw = clw, size(long)), fmemNil, mprNormal)) { LFail: // we are low on memory, so be nice and give some up FreePhq(&_hqrglw); _clw = 0; AssertThis(0); return fFalse;; } AssertThis(0); return fTrue; } /*************************************************************************** Returns the next number in a shuffled array of numbers. If lwLim is zero, uses the numbers already in the SFL. Otherwise, numbers range from 0 to (lwLim - 1), inclusive. ***************************************************************************/ long SFL::LwNext(long lwLim) { AssertThis(0); AssertIn(lwLim, 0, kcbMax); if (lwLim > 0 && (lwLim != _clw || _fCustom)) { //need to reshuffle with standard values Shuffle(lwLim); _ilw = 0; if (0 == _clw) { //shuffling failed, just use the regular random number return RND::LwNext(lwLim); } } else if (_clw == 0) { //no values in the HQ, just return 0 _ilw = 0; return 0; } else if (_ilw >= _clw) { //need to reshuffle the values already in the HQ _ShuffleCore(); _ilw = 0; } AssertIn(_ilw, 0, _clw); return ((long *)QvFromHq(_hqrglw))[_ilw++]; }