Пример #1
0
def GetBox(container, size):
    minPt = container.Transform.OfPoint(container.Min)
    maxPt = container.Transform.OfPoint(container.Max)

    randX = 1 - random.WichmannHill().random()
    randY = 1 - random.WichmannHill().random()
    randZ = 1 - random.WichmannHill().random()

    #if randX > 0.5 or randY > 0.5 or randZ > 0.5: print(randX, randY, randZ)

    center = XYZ(randX * (minPt.X) + (1 - randX) * maxPt.X,
                 randY * (minPt.Y) + (1 - randY) * maxPt.Y,
                 randZ * (minPt.Z) + (1 - randZ) * maxPt.Z)

    # ratio = (center.X - minPt.X)/(maxPt.X - minPt.X)
    # print(ratio)

    # center = XYZ(
    # 	random.uniform(minPt.X + (size/2), maxPt.X - (size/2)),
    # 	random.uniform(minPt.Y + (size/2), maxPt.Y - (size/2)),
    # 	random.uniform(maxPt.Z - 7, maxPt.Z)
    # )

    box = GetOutlineAt(center, size)
    # AddBoxToDoc(box) # this is for debugging reasons
    return box
Пример #2
0
    def GeneratePairs(self, num):
        labels = list()
        targets = list()
        # populate these lists now
        i = 0
        while i < num:
            pair = None
            if coinToss() and len(self.elements) > 1:
                pair = random.WichmannHill().sample(self.elements, 2)
                i += 1
            else:
                randElem = random.WichmannHill().sample(self.elements, 1)[0]
                if len(self.matDict[randElem]) < 1:
                    i += 1
                    continue
                else:
                    matName = random.WichmannHill().sample(
                        self.matDict[randElem], 1)[0]
                    pair = [randElem, matName
                            ] if coinToss() else [matName, randElem]
                    i += 1

            labels.append(pair[0])
            targets.append(pair[1])

        return [labels, targets]
Пример #3
0
	def __init__(self, seed = None, randg = None):
		import random
		self.seed = seed
		if randg:
			self.randg = randg
		else:
			try:
				if seed:
					self.randg = random.WichmannHill(seed)
				else:
					self.randg = random.WichmannHill()
			except:
				if seed:
					self.randg = random.SystemRandom(seed)
				else:
					self.randg = random.SystemRandom()
Пример #4
0
    def test_choose_random_word(self):
        model = self.model

        # First, train one sentence and make sure we randomly pick the
        # only possible option.
        model.train(u"one two three")
        context = [u"one", u"two"]

        self.assertEqual(u"three", model.choose_random_word(context))

        # Make sure a context that hasn't been trained comes back None
        self.assertIsNone(model.choose_random_word([u"missing", u"context"]))

        # Train another sentence and make sure we pick both options
        # with carefully chosen seeding. Explicitly use Python's (old)
        # WichmannHill PRNG to ensure reproducability, since the
        # default PRNG generator could conceivably change in a future
        # release.
        model.train(u"one two four")

        rng = random.WichmannHill()

        rng.seed(0)
        self.assertEqual(u"three", model.choose_random_word(context, rng=rng))
        self.assertEqual(u"four", model.choose_random_word(context, rng=rng))
Пример #5
0
def GetAnagrams(letters):

    index = letters.find("anagram") + 7

    letters = letters[index:]

    if len(letters) > 30:
        "Sorry, please reduce the length of your anagram request. (Maximum 30 characters)."

    try:
        if len(letters) < 4:
            return "Please follow: !anagram (thing to be anagrammed)"
    except:
        return "Please follow: !anagram (thing to be anagrammed)"

    anagram_string = ""
    anagram_list = ReturnAnagrams(letters)

    
    if anagram_list == False:
        return "Sorry, I couldn't find any decent anagrams! Try something else!"

    rng=random.WichmannHill()
    rng.shuffle(anagram_list)
    first = True
    for i in anagram_list:
        
        if first != True:
            anagram_string += " "
        else:
            first = False
            
        anagram_string += i    

    return '"' + anagram_string + '" is an anagram of "' + letters + '".'
Пример #6
0
    def test_odd_flush(self):
        # Test for odd flushing bugs noted in 2.0, and hopefully fixed in 2.1
        import random

        if hasattr(zlib, 'Z_SYNC_FLUSH'):
            # Testing on 17K of "random" data

            # Create compressor and decompressor objects
            co = zlib.compressobj(zlib.Z_BEST_COMPRESSION)
            dco = zlib.decompressobj()

            # Try 17K of data
            # generate random data stream
            try:
                # In 2.3 and later, WichmannHill is the RNG of the bug report
                gen = random.WichmannHill()
            except AttributeError:
                try:
                    # 2.2 called it Random
                    gen = random.Random()
                except AttributeError:
                    # others might simply have a single RNG
                    gen = random
            gen.seed(1)
            data = genblock(1, 17 * 1024, generator=gen)

            # compress, sync-flush, and decompress
            first = co.compress(data)
            second = co.flush(zlib.Z_SYNC_FLUSH)
            expanded = dco.decompress(first + second)

            # if decompressed data is different from the input data, choke.
            self.assertEqual(expanded, data, "17K random source doesn't match")
Пример #7
0
class WichmannHill_TestBasicOps(TestBasicOps):
    gen = random.WichmannHill()

    def test_strong_jumpahead(self):
        # tests that jumpahead(n) semantics correspond to n calls to random()
        N = 1000
        s = self.gen.getstate()
        self.gen.jumpahead(N)
        r1 = self.gen.random()
        # now do it the slow way
        self.gen.setstate(s)
        for i in xrange(N):
            self.gen.random()
        r2 = self.gen.random()
        self.assertEqual(r1, r2)

    def test_gauss_with_whseed(self):
        # Ensure that the seed() method initializes all the hidden state.  In
        # particular, through 2.2.1 it failed to reset a piece of state used
        # by (and only by) the .gauss() method.

        for seed in 1, 12, 123, 1234, 12345, 123456, 654321:
            self.gen.whseed(seed)
            x1 = self.gen.random()
            y1 = self.gen.gauss(0, 1)

            self.gen.whseed(seed)
            x2 = self.gen.random()
            y2 = self.gen.gauss(0, 1)

            self.assertEqual(x1, x2)
            self.assertEqual(y1, y2)
Пример #8
0
def random_index_multipart(count=10,
                           dummy=0,
                           dcs=0x00,
                           mid=-1,
                           data=None,
                           datalen=134):
    out = []
    r = random.WichmannHill()
    if mid == -1:
        mid = r.randrange(0, 0xff)
    ts = Utils.hex2bin("99309251619580", 0)
    for i in range(0, count):
        if dcs == -1:
            dcsuse = randdcs()
        else:
            dcsuse = dcs
        if data == None:
            datause = SMSFuzzData.getSMSFuzzData()
        else:
            datause = data[:datalen]
        if datalen < 0:
            datause = datause[:r.randrange(0, datalen * -1)]
        msg = concat("49177123456", "49177123456", ts, count,
                     r.randrange(0, 256), mid, datause, dcsuse)
        line = Utils.bin2hex(msg, 1)
        leng = (len(line) / 2) - 8
        out.append((line, leng))
    return out
Пример #9
0
    def reset(self):
        """
        Resets thread data model
        """

        self.disableStdOut = False
        self.hashDBCursor = None
        self.inTransaction = False
        self.lastCode = None
        self.lastComparisonPage = None
        self.lastComparisonHeaders = None
        self.lastComparisonCode = None
        self.lastComparisonRatio = None
        self.lastErrorPage = None
        self.lastHTTPError = None
        self.lastRedirectMsg = None
        self.lastQueryDuration = 0
        self.lastPage = None
        self.lastRequestMsg = None
        self.lastRequestUID = 0
        self.lastRedirectURL = None
        self.random = random.WichmannHill()
        self.resumed = False
        self.retriesCount = 0
        self.seqMatcher = difflib.SequenceMatcher(None)
        self.shared = shared
        self.validationRun = 0
        self.valueStack = []
Пример #10
0
    def __init__(self, name):
        self.name = name

        #init Wichmann-Hill prng with 256 bits (32*8) seed
        self.rand = random.WichmannHill(os.urandom(32))
        #self.rand.setstate([1, (8556, 14019, 30321), None])
        print name, self.rand.getstate()
Пример #11
0
 def get_rng(self, index=0):
     if self.rngvec:
         seed = self.rngvec[index % len(self.rngvec)] + index
     else:
         seed = index
     rng = random.WichmannHill(seed)
     return rng
Пример #12
0
 def OnStart(self, effect, t):
     if self.currentStartTime is None:
         if effect.rangeChance or effect.electronicChance or effect.propulsionChance:
             self.random = random.WichmannHill(blue.os.GetWallclockTimeNow())
             self.registrations = []
     self.currentStartTime = t
     if self.random is not None:
         self.currentSeed = self.GetRandomSeed()
Пример #13
0
def create_sample_data(session):
    rnd = random.WichmannHill(1)

    account_1234 = Account(id=1234, name='user1234', apikey='26ec5efff4a6')
    session.add(account_1234)
    account_1235 = Account(id=1235,
                           name='lukz',
                           mbuser='******',
                           apikey='2112c535e9e6',
                           is_admin=True)
    session.add(account_1235)

    application_1234 = Application(account=account_1234,
                                   name='Test App',
                                   version='1.0',
                                   apikey='617f98b2d7cc')
    session.add(application_1234)

    unflushed = 0
    dt = datetime.datetime(2014, 1, 2)
    while dt < datetime.datetime.now():
        obj = StatsLookups(date=dt.date(),
                           hour=dt.hour,
                           application=application_1234)
        obj.count_nohits = rnd.randint(0, 1000)
        obj.count_hits = rnd.randint(0, 10000)
        session.add(obj)
        dt += datetime.timedelta(hours=1)
        unflushed += 1
        if unflushed > 100:
            session.flush()
            unflushed = 0

    application_1235 = Application(account=account_1235,
                                   name='Test App 2',
                                   version='0.2',
                                   apikey='c24d2e31d8db')
    session.add(application_1235)

    unflushed = 0
    dt = datetime.datetime(2015, 2, 3)
    while dt < datetime.datetime.now():
        obj = StatsLookups(date=dt.date(),
                           hour=dt.hour,
                           application=application_1235)
        obj.count_nohits = rnd.randint(0, 200)
        obj.count_hits = rnd.randint(0, 1000)
        session.add(obj)
        dt += datetime.timedelta(hours=1)
        unflushed += 1
        if unflushed > 100:
            session.flush()
            unflushed = 0

    session.commit()
Пример #14
0
def RandomDiceRoll(MinNum=1,
                   MaxNum=6,
                   RandType=1,
                   RandSeed=random.seed(),
                   DiceArray=None):
    if (len(re.findall("^([\-]?[0-9]+)$", str(MinNum))) < 1):
        MinNum = 1
    try:
        MinNum = int(MinNum)
    except ValueError:
        return [False]
    if (len(re.findall("^([\-]?[0-9]+)$", str(MaxNum))) < 1):
        MaxNum = 6
    try:
        MaxNum = int(MaxNum)
    except ValueError:
        return [False]
    if (len(re.findall("^([\-]?[1-5]+)$", str(RandType))) < 1):
        RandType = 1
    try:
        RandType = int(RandType)
    except ValueError:
        RandType = 1
    if (MinNum > MaxNum):
        TmpMinNum = MaxNum
        TmpMaxNum = MinNum
        MaxNum = TmpMaxNum
        MinNum = TmpMinNum
    RandType = int(RandType)
    if (RandType < 1):
        RandType = 1
    if (RandType > 5):
        RandType = 5
    if (RandType == 1):
        DiceRollValue = random.randint(MinNum, MaxNum)
    if (RandType == 2):
        randtype = random.WichmannHill(RandSeed)
        DiceRollValue = randtype.randint(MinNum, MaxNum)
    if (RandType == 3):
        randtype = random.SystemRandom(RandSeed)
        DiceRollValue = randtype.randint(MinNum, MaxNum)
    if (RandType == 4):
        DiceRollValue = random.randrange(MinNum, MaxNum + 1)
    if (RandType == 5):
        icount = 1
        ilist = []
        while (icount <= MaxNum):
            ilist.append(icount)
            icount += 1
        DiceRollValue = random.choice(ilist)
    if (DiceArray is not None and type(DiceArray) is dict):
        DiceRollValue = GetDictValueFromDiceNumber(DiceRollValue, DiceArray)[0]
    return [DiceRollValue]
Пример #15
0
def random_int(length=4, seed=None):
    """
	>>> random.seed(0)
	>>> randomInt(6)
	874254
	"""

    choice = random.WichmannHill(
        seed).choice if seed is not None else random.choice

    return int("".join(
        choice(string.digits if _ != 0 else string.digits.replace('0', ''))
        for _ in xrange(0, length)))
Пример #16
0
def throw_fish_rod(vmodel):
	if vmodel.Status['fishing'] == 'throw':
		return False

	random_wichman = random.WichmannHill(datetime.datetime.now())
	rand_width, rand_height = random_wichman.randint(-10, 10), random_wichman.randint(-10, 10)

	x, y, width, height = vmodel.App.PyArea
	vmodel.App.StoreCursorPosition(width * 0.7 + rand_width, height * 0.2 + rand_height)
	vmodel.App.KeyPress('W', 200)
	vmodel.App.RestoreCursorPosition()
	vmodel.Status['fishing'] = 'throw'
	vmodel.Status['throw time'] = datetime.datetime.now()
	return True
Пример #17
0
 def setrng(self, rngname):
     if rngname == headerdata.SF_DIEROLLER_RNGSTRINGS[0]:
         self.rng = random.Random()
         self.rngfunc = self.rng.randint
     elif rngname == headerdata.SF_DIEROLLER_RNGSTRINGS[1]:
         self.rng = random.WichmannHill()
         self.rngfunc = self.rng.randint
     elif rngname == headerdata.SF_DIEROLLER_RNGSTRINGS[2]:
         self.rng = random.SystemRandom()
         self.rngfunc = self.rng.randint
     else:
         return False
     if headerdata.options.verbose:
         print "Using RNG: " + rngname
Пример #18
0
    def __init__(self,
                 prefix="!",
                 client_id=None,
                 settings=Settings,
                 **kwargs):
        self.__parent = None
        self.prefix = prefix
        self.__commands = {}
        self.__nodes = {}
        self.__listeners = {
            "on_send_message": [self._on_send_message],
            "on_command_error": [self.on_command_error],
            "on_error": [self.on_error],
            "on_parse": [self.parse],
            "on_message": [self.on_message]
        }

        self._parser = None
        self.__script_globals = {}
        self._do_parameters = kwargs.get("do_parameters", True)
        self.settings = settings()

        # i dont know the platform until the first data event comes through
        # so just set it to `None` for now
        self._platform = None
        self._scheduled_events = []
        GroupMapping.__init__(self, **kwargs)

        # no, this is not a mistake. we do this because when a command is registered, GroupMapping passes self._bot
        # to the command.
        self._bot = self

        self.random = random.WichmannHill()
        self.stream = self.get_channel(
            Platforms.twitch
        )  # this works, as all the streaming channels are the same.
        self.discord = self.get_channel(Platforms.discord)
        self._live_dt = None
        self._api = BrowserWindow(self, time.time())
        self._events = EventsNode(self)

        if kwargs.get("enable_debug", False):
            self._debug = True
            self.add_node(Debug(self))

        else:
            self._debug = False

        self.client_id = client_id
Пример #19
0
def randomInt(length=4, seed=None):
    """
    Returns random integer value with provided number of digits

    >>> random.seed(0)
    >>> randomInt(6)
    874254
    """

    choice = random.WichmannHill(
        seed).choice if seed is not None else random.choice

    return int("".join(
        choice(string.digits if _ != 0 else string.digits.replace('0', ''))
        for _ in xrange(0, length)))
Пример #20
0
class WichmannHill_TestBasicOps(TestBasicOps):
    gen = random.WichmannHill()

    def test_setstate_first_arg(self):
        self.assertRaises(ValueError, self.gen.setstate, (2, None, None))

    def test_strong_jumpahead(self):
        # tests that jumpahead(n) semantics correspond to n calls to random()
        N = 1000
        s = self.gen.getstate()
        self.gen.jumpahead(N)
        r1 = self.gen.random()
        # now do it the slow way
        self.gen.setstate(s)
        for i in xrange(N):
            self.gen.random()
        r2 = self.gen.random()
        self.assertEqual(r1, r2)

    def test_gauss_with_whseed(self):
        # Ensure that the seed() method initializes all the hidden state.  In
        # particular, through 2.2.1 it failed to reset a piece of state used
        # by (and only by) the .gauss() method.

        for seed in 1, 12, 123, 1234, 12345, 123456, 654321:
            self.gen.whseed(seed)
            x1 = self.gen.random()
            y1 = self.gen.gauss(0, 1)

            self.gen.whseed(seed)
            x2 = self.gen.random()
            y2 = self.gen.gauss(0, 1)

            self.assertEqual(x1, x2)
            self.assertEqual(y1, y2)

    def test_bigrand(self):
        # bug in _warnings
        if test_support.due_to_ironpython_bug(
                "http://www.codeplex.com/IronPython/WorkItem/View.aspx?WorkItemId=21116"
        ):
            return
        # Verify warnings are raised when randrange is too large for random()
        with warnings.catch_warnings():
            warnings.filterwarnings("error", "Underlying random")
            self.assertRaises(UserWarning, self.gen.randrange, 2**60)
Пример #21
0
 def test_odd_flush(self):
     import random
     co = zlib.compressobj(zlib.Z_BEST_COMPRESSION)
     dco = zlib.decompressobj()
     try:
         gen = random.WichmannHill()
     except AttributeError:
         try:
             gen = random.Random()
         except AttributeError:
             gen = random
     gen.seed(1)
     data = genblock(1, 17 * 1024, generator=gen)
     first = co.compress(data)
     second = co.flush(zlib.Z_SYNC_FLUSH)
     expanded = dco.decompress(first + second)
     self.assertEqual(expanded, data, "17K random source doesn't match")
Пример #22
0
    def parse_args(self, args, values=None):
        (options, args) = OptionParser.parse_args(self, args, values)

        if self.random_options:
            if options.random_generator is None or options.random_generator == "MersenneTwister":
                r = random.Random()
            elif options.random_generator == "WichmannHill":
                r = random.WichmannHill()
            else:
                self.error(
                    "Acceptible generators are MersenneTwister (default) or WichmannHill"
                )
            if options.random_seed:
                r.seed(options.random_seed)

            options.__dict__["random"] = r

        return (options, args)
Пример #23
0
def generate_txstring():

    txdata = {}

    #Generate our random string, using the current date and time as a seed.
    next_minute = datetime.utcnow() + timedelta(0, 0, 0, 0, 1, 0)

    txdata["timeseed"] = next_minute.strftime("%d-%m-%Y %H:%M")

    new_random = random.WichmannHill()
    new_random.seed(binascii.crc32(txdata["timeseed"]))
    txdata["randomdata"] = ''.join(
        new_random.choice(string.ascii_uppercase + string.ascii_lowercase +
                          string.digits) for x in range(64))
    txdata[
        "txstring"] = "DE VK5QI VK5QI $$$$$" + txdata["randomdata"] + "$$$$$"

    return txdata
Пример #24
0
class WichmannHill_TestBasicOps(TestBasicOps):
    gen = random.WichmannHill()

    def test_setstate_first_arg(self):
        self.assertRaises(ValueError, self.gen.setstate, (2, None, None))

    def test_strong_jumpahead(self):
        # tests that jumpahead(n) semantics correspond to n calls to random()
        N = 1000
        s = self.gen.getstate()
        self.gen.jumpahead(N)
        r1 = self.gen.random()
        # now do it the slow way
        self.gen.setstate(s)
        for i in xrange(N):
            self.gen.random()
        r2 = self.gen.random()
        self.assertEqual(r1, r2)

    def test_gauss_with_whseed(self):
        # Ensure that the seed() method initializes all the hidden state.  In
        # particular, through 2.2.1 it failed to reset a piece of state used
        # by (and only by) the .gauss() method.

        for seed in 1, 12, 123, 1234, 12345, 123456, 654321:
            self.gen.whseed(seed)
            x1 = self.gen.random()
            y1 = self.gen.gauss(0, 1)

            self.gen.whseed(seed)
            x2 = self.gen.random()
            y2 = self.gen.gauss(0, 1)

            self.assertEqual(x1, x2)
            self.assertEqual(y1, y2)

    def test_bigrand(self):
        # Verify warnings are raised when randrange is too large for random()
        oldfilters = warnings.filters[:]
        warnings.filterwarnings("error", "Underlying random")
        self.assertRaises(UserWarning, self.gen.randrange, 2**60)
        warnings.filters[:] = oldfilters
Пример #25
0
def random_str(length=4, lowercase=False, alphabet=None, seed=None):
    """
	>>> random.seed(0)
	>>> randomStr(6)
	'RNvnAv'
	"""

    choice = random.WichmannHill(
        seed).choice if seed is not None else random.choice

    if alphabet:
        ret_val = "".join(choice(alphabet) for _ in xrange(0, length))
    elif lowercase:
        ret_val = "".join(
            choice(string.ascii_lowercase) for _ in xrange(0, length))
    else:
        ret_val = "".join(
            choice(string.ascii_letters) for _ in xrange(0, length))

    return ret_val
Пример #26
0
    def test_random_policy(self):
        """
        testing random policy
        """
        rdrwh = random.WichmannHill(0)
        policy = RandomPolicy(self.session)
        policy.key = lambda _: rdrwh.random()

        torrents_start, torrents_stop = policy.apply(self.torrents,
                                                     6,
                                                     force=True)
        ids_start = [
            torrent["metainfo"].get_infohash() for torrent in torrents_start
        ]
        self.assertEqual(1, len(ids_start),
                         "Start failed %s vs %s" % (ids_start, torrents_start))
        ids_stop = [
            torrent["metainfo"].get_infohash() for torrent in torrents_stop
        ]
        self.assertEqual(0, len(ids_stop),
                         "Stop failed %s vs %s" % (ids_stop, torrents_stop))
Пример #27
0
def randomStr(length=4, lowercase=False, alphabet=None, seed=None):
    """
    Returns random string value with provided number of characters

    >>> random.seed(0)
    >>> randomStr(6)
    'RNvnAv'
    """

    choice = random.WichmannHill(
        seed).choice if seed is not None else random.choice

    if alphabet:
        retVal = "".join(choice(alphabet) for _ in xrange(0, length))
    elif lowercase:
        retVal = "".join(
            choice(string.ascii_lowercase) for _ in xrange(0, length))
    else:
        retVal = "".join(
            choice(string.ascii_letters) for _ in xrange(0, length))

    return retVal
Пример #28
0
    def test_choose_random_context(self):
        model = self.model

        # First, train one sentence and make sure we randomly pick the
        # only possible option.
        model.train(u"one two three")

        self.assertEqual([u"one", u"two", u"three"],
                         model.choose_random_context(u"one"))

        # Make sure a context that hasn't been trained comes back None
        self.assert_(model.choose_random_context(u"missing") is None)

        # Train another sentence and make sure we pick both options
        # with carefully chosen seeding.
        model.train(u"one two four")

        rng = random.WichmannHill()

        rng.seed(0)
        self.assertEqual([u"one", u"two", u"three"],
                         model.choose_random_context(u"one", rng=rng))
        self.assertEqual([u"one", u"two", u"four"],
                         model.choose_random_context(u"one", rng=rng))
Пример #29
0
def displayid(n, words_in_random_id = DEFAULT_WORDS_IN_RANDOM_ID):
    badrandom_value = badrandom.WichmannHill()
    badrandom_value.seed(n)
    return ' '.join(badrandom_value.choice(words) for x in range(words_in_random_id))
Пример #30
0
    print "%.8f" % r.gammavariate(20.0, 1.0)
    print "%.8f" % r.gauss(0.0, 1.0)
    print "%.8f" % r.betavariate(3.0, 3.0)
    print "%.8f" % r.paretovariate(1.0)
    print "%.8f" % r.weibullvariate(1.0, 1.0)
    #print "%.8f" % r.stdgamma(1.0, 1.0, 1.0, 1.0) # deprecated in CPython
    #print "%.8f" % r.cunifvariate(0.0, 1.0)       # deprecated in CPython
    print ''

# --- random.Random (Mersenne Twister)
mt = random.Random()
mt.seed()
mt.seed(79)
mtstate = mt.getstate()   # (state is not cross-compatible with CPython)
mt.setstate(mtstate)
#mt.jumpahead(1000000)    # (not yet supported)
for i in range(25): runrng(mt)

# --- random.WichmannHill
wh = random.WichmannHill()
wh.seed()
wh.seed(86)
wh.whseed()
wh.whseed(41)
whstate = wh.getstate()   # (state is not cross-compatible with CPython)
wh.setstate(whstate)
wh.jumpahead(1000000)
for i in range(25): runrng(wh)