一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
71 行
1.9 KiB
TypeScript
71 行
1.9 KiB
TypeScript
|
|
// SeededRandom provides a very simple random number generator
|
|
// that can be seeded (since NodeJS built-ins can't).
|
|
//
|
|
// See also:
|
|
// - https://arxiv.org/pdf/1704.00358
|
|
// - https://en.wikipedia.org/wiki/Middle-square_method
|
|
|
|
// Some large odd number, see https://en.wikipedia.org/wiki/Weyl_sequence
|
|
const step = BigInt('0x12345678ABCDE123'); // uint64
|
|
const maxSize = BigInt(1) << BigInt(64);
|
|
|
|
const extractMiddle = (value: bigint, halfSize: bigint) => {
|
|
// Remove the lower quarter
|
|
const quarterSize = halfSize / BigInt(2);
|
|
value >>= quarterSize;
|
|
|
|
// Remove the upper quarter
|
|
const halfMaximumValue = BigInt(1) << halfSize;
|
|
value %= halfMaximumValue;
|
|
|
|
return value;
|
|
};
|
|
|
|
export interface RandomState {
|
|
value: bigint|string;
|
|
step: bigint|string;
|
|
}
|
|
|
|
export default class SeededRandom {
|
|
private value_: bigint;
|
|
private nextStep_: bigint = step;
|
|
private halfSize_ = BigInt(32);
|
|
|
|
public constructor(
|
|
seed: number|bigint|string,
|
|
) {
|
|
this.value_ = typeof seed !== 'bigint' ? BigInt(seed) : seed;
|
|
}
|
|
|
|
public static fromState({ value, step }: RandomState) {
|
|
const random = new SeededRandom(value);
|
|
random.nextStep_ = typeof step !== 'bigint' ? BigInt(step) : step;
|
|
return random;
|
|
}
|
|
|
|
// Returns a state that can be used to save/restore this random number generator.
|
|
public get state(): RandomState {
|
|
return { value: this.value_, step: this.nextStep_ };
|
|
}
|
|
|
|
public next() {
|
|
this.value_ = this.value_ * this.value_ + this.nextStep_;
|
|
|
|
// Move to the next item in the sequence. Mod to prevent from getting
|
|
// too large. See https://en.wikipedia.org/wiki/Weyl_sequence.
|
|
this.nextStep_ = (step + this.nextStep_) % maxSize;
|
|
|
|
this.value_ = extractMiddle(this.value_, this.halfSize_);
|
|
return this.value_;
|
|
}
|
|
|
|
// The resultant range includes `a` but excludes `b`.
|
|
public nextInRange(a: number, b: number) {
|
|
if (b <= a + 1) return a;
|
|
|
|
const range = b - a;
|
|
return Number(this.next() % BigInt(range)) + a;
|
|
}
|
|
}
|