# Random.* — an ordinary random number

> A random integer for ordinary work — picking a row, jittering a retry, shuffling. `Random.Next(10)` answers 0..9. It WORKS INSIDE A LINQ QUERY and translates to SQL, so `.OrderBy(f => Random.Next())` is one round trip and `.ThenBy(f => Random.Next())` is a random tiebreak. It is SEEDABLE and therefore reproducible, which is why it must never produce a token: `Security.RandomId` is the unguessable one.

<!-- id: stdlib-random · area: stdlib · stability: stable · html: https://osysharp.com/reference/stdlib/random/ -->

## Summary        {#summary}

`Random.Next` answers a random integer, spelled exactly as C#'s `System.Random.Next`.

```osy syntax
int n = Random.Next(10);        // 0..9   — max is EXCLUSIVE, as in C#
int d = Random.Next(1, 7);      // 1..6   — min inclusive, max exclusive
```

There are two randoms in this platform and choosing between them is the whole of what you need to know:

| you want | use | why |
|---|---|---|
| a number, a pick, a shuffle, a jitter | **`Random.Next`** | ordinary, fast, and reproducible under a test seed |
| a token, a reset code, an api key, a session id | **`Security.RandomId`** / `RandomHex` | cryptographic — see [Security.* — hashing, verifying, tickets, random ids](https://osysharp.com/reference/stdlib/security/) |

## Signature        {#signature}

```osy syntax
int  Random.Next()                   // any non-negative int — the ordering form
int  Random.Next(int max)            // [0, max)   — `max` must be at least 1
int  Random.Next(int min, int max)   // [min, max) — `max` must be greater than `min`
```

An empty range has no value to answer with, so it is refused rather than answered: `Random.Next(0)` and
`Random.Next(5, 5)` both throw, naming the bound. `Random.Next(list.Count)` over an empty list is the usual way to
reach that, so check the count first.

## Description        {#description}

**It is seedable, and that is not an implementation detail.** A test that draws must be able to draw the same
sequence twice, or it can only assert statistically — "at least 25 of 40 were idle" — which is slow and flaky by
construction. Seeding is what makes an exact assertion possible.

**And that is exactly why it must not make a secret.** A value anyone can reproduce is not unguessable. The
sequence is predictable from any earlier value, and nothing about the result *looks* wrong: it is not obviously
sequential, the tests pass, and it stays guessable until somebody enumerates it. C# draws this same line between
`System.Random` and `RandomNumberGenerator`, and gets the same misuse.

`security-weak-random` is a MUST-tier lint on exactly that mistake — a `Random` draw landing in a member named for
a credential (`token`, `secret`, `password`, `apiKey`, `nonce`, `otp`, …). It fires on the DESTINATION, never on
the call, because `Random.Next` is correct nearly everywhere.

## It works inside a LINQ query        {#ordering}

**`Random.Next()` is translated to SQL, so it is usable anywhere in a query — not only in a function body.** There
is no separate `OrderByRandom()` verb to learn, and none is needed: it is an ordinary `OrderBy` over an ordinary
call, which is what a C# author writes, and it therefore composes with `ThenBy`, `Where`, `Take` and the rest
exactly as any other sort key does.

This is how you take one row at random without a `Count()` and a `Skip()`:

```osy title="one row at random, and a fair tiebreak among equals" syntax
// one row at random — ONE round trip
Film any = Film.OrderBy(f => Random.Next()).Take(1).FirstOrDefault();

// the shape most selection rules actually take: least-compared, RANDOM AMONG TIES
Film pick = Film.OrderBy(f => f.Comparisons)
                .ThenBy(f => Random.Next())
                .Take(1).FirstOrDefault();
```

The second is the one worth knowing. Without a random tiebreak, "the least-compared film" returns the *same* film
every time two are tied, so a matchmaker walks the same pair repeatedly. With it, the primary sort still decides and
the tie is broken fairly, in one query.

It runs in the DATABASE, not in the browser and not in memory — `ORDER BY random()` — so the whole draw is one
round trip whatever the table's size. A `.Where(…)` before it narrows the rows the database is choosing among, as
you would expect:

```osy title="narrow first, so the database draws only from the rows you meant" syntax
Film pick = Film.Where(f => !f.Watched)
                .OrderBy(f => Random.Next())
                .Take(1).FirstOrDefault();
```

⚠ **Not inside a `live var`.** A reactive read re-runs whenever its inputs change, and a random order returns a
different row each time — so the value flickers when something unrelated moves. Draw in an `action`, store what you
drew, and let the page read the stored row.

## Examples        {#examples}

```osy test app=random-pick
entity Card { [Required, MaxLength(40)] string Face;
  security { allow read, create when IsAnonymous || IsAuthenticated; } }

// One of a known set, chosen at random.
string DealOne() {
  var faces = ["clubs", "diamonds", "hearts", "spades"];
  return faces[Random.Next(faces.Count)];
}

// A retry jitter — spread out so a hundred clients do not return at the same instant.
int BackoffMillis(int attempt) { return 500 * attempt + Random.Next(250); }
```

## See also        {#see-also}

- [Security.* — hashing, verifying, tickets, random ids](https://osysharp.com/reference/stdlib/security/) — `RandomId` / `RandomHex`, the cryptographic pair, for anything a stranger must not guess.
