diff --git a/.gitignore b/.gitignore index d696abdc..79205137 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ result* tests/resources/hashlib-test-large-local.txt tests/function-library-index.json .claude/settings.local.json +.codex diff --git a/devel/215_18.md b/devel/215_18.md new file mode 100644 index 00000000..87c22ff4 --- /dev/null +++ b/devel/215_18.md @@ -0,0 +1,84 @@ +# [215_18] 实现 (liii random) 模块 + +## 任务相关的代码文件 +- `goldfish/liii/random.scm` - (liii random) 导出模块 +- `goldfish/srfi/srfi-27.scm` - SRFI-27 实现 +- `tests/goldfish/liii/random-test.scm` - 测试文件 + +## 如何测试 +```bash +# 1. 构建 +xmake b goldfish + +# 2. 格式化代码 +bin/gf fix goldfish/liii/random.scm +bin/gf fix goldfish/srfi/srfi-27.scm +bin/gf fix tests/goldfish/liii/random-test.scm + +# 3. 运行测试 +bin/gf tests/goldfish/liii/random-test.scm +``` + +## 2025-04-03 实现 (liii random) 模块 + +### What +实现基于 SRFI-27 的随机数生成模块,包括: + +1. **创建文件 `goldfish/liii/random.scm`** + - 作为导出模块,导入并重导出 (srfi srfi-27) 的所有功能 + +2. **创建文件 `goldfish/srfi/srfi-27.scm`** + - 基于 s7.c 内置的 random 函数实现 SRFI-27 规范 + - 使用 Sebastian Egner 的版权和许可证(SRFI-27 原始版权) + - 实现 random-source 记录类型 + - 实现 SRFI-27 标准函数 + +3. **创建测试文件 `tests/liii/random-test.scm`** + - 作为函数分类索引和文档说明 + +4. **创建单个函数测试文件 `tests/liii/random/` 目录** + - `random-integer-test.scm` - random-integer 测试 + - `random-real-test.scm` - random-real 测试 + - `make-random-source-test.scm` - make-random-source 测试 + - `random-source-p-test.scm` - random-source? 测试 + - `random-source-state-ref-test.scm` - random-source-state-ref 测试 + - `random-source-state-set-test.scm` - random-source-state-set! 测试 + - `random-source-randomize-test.scm` - random-source-randomize! 测试 + - `random-source-pseudo-randomize-test.scm` - random-source-pseudo-randomize! 测试 + - `random-source-make-integers-test.scm` - random-source-make-integers 测试 + - `random-source-make-reals-test.scm` - random-source-make-reals 测试 + - `reproducibility-test.scm` - 可复现性测试 + +### Why +Goldfish Scheme 需要标准的随机数生成接口,SRFI-27 是 Scheme 社区广泛接受的标准。 +s7.c 内置了 `random` 函数和 `random-state` 类型,基于这些内置功能实现 SRFI-27 可以: +1. 利用 s7.c 的高性能随机数生成器 +2. 提供符合标准 SRFI-27 的接口 +3. 保持与现有 Scheme 生态系统的兼容性 + +### How + +#### s7.c 内置功能 +- `(random n)` - 返回 [0, n) 范围内的随机数 +- `(random n state)` - 使用指定的 random-state +- `(random-state seed)` - 创建新的 random-state +- `(random-state? obj)` - 检查是否为 random-state +- `(random-state->list state)` - 将状态转换为列表 `(seed carry)` + +#### SRFI-27 与 s7.c 的映射 +SRFI-27 的 random-source 是一个记录类型,包含: +- state-ref: 返回状态 +- state-set!: 设置状态 +- randomize!: 用时间随机化 +- pseudo-randomize!: 用索引伪随机化 +- make-integers: 创建整数生成器 +- make-reals: 创建实数生成器 + +状态格式:s7.c 的 random-state 可以用 `(random-state->list state)` 获取为 `(seed carry)` 列表 + +#### 实现要点 +1. 使用 `define-record-type` 定义 random-source 类型 +2. 使用 s7.c 的 `(random-state seed)` 创建新的状态 +3. 使用 `(random-state->list state)` 获取和设置状态 +4. 使用 `(random n state)` 生成随机数 +5. 用当前时间(纳秒)作为 `random-source-randomize!` 的熵源 diff --git a/goldfish/liii/random.scm b/goldfish/liii/random.scm new file mode 100644 index 00000000..65b9fa86 --- /dev/null +++ b/goldfish/liii/random.scm @@ -0,0 +1,50 @@ +; +; Copyright (C) 2025 The Goldfish Scheme Authors +; +; Licensed under the Apache License, Version 2.0 (the "License"); +; you may not use this file except in compliance with the License. +; You may obtain a copy of the License at +; +; http://www.apache.org/licenses/LICENSE-2.0 +; +; Unless required by applicable law or agreed to in writing, software +; distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +; WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +; License for the specific language governing permissions and limitations +; under the License. + +;; +;; (liii random) - Random number generation +;; +;; This module exports the SRFI-27 interface for random number generation. +;; It re-exports all symbols from (srfi srfi-27). +;; +;; Exported functions: +;; random-integer - Generate random integer in [0, n-1] +;; random-real - Generate random real in (0, 1) +;; default-random-source - The default random source +;; make-random-source - Create a new random source +;; random-source? - Check if object is a random source +;; random-source-state-ref - Get the state of a random source +;; random-source-state-set!- Set the state of a random source +;; random-source-randomize!- Randomize a source with current time +;; random-source-pseudo-randomize! - Deterministically set source state +;; random-source-make-integers - Create integer generator from source +;; random-source-make-reals - Create real generator from source + +(define-library (liii random) + (import (srfi srfi-27)) + (export + random-integer + random-real + default-random-source + make-random-source + random-source? + random-source-state-ref + random-source-state-set! + random-source-randomize! + random-source-pseudo-randomize! + random-source-make-integers + random-source-make-reals + ) ;export +) ;define-library diff --git a/goldfish/srfi/srfi-27.scm b/goldfish/srfi/srfi-27.scm new file mode 100644 index 00000000..f4edbcb6 --- /dev/null +++ b/goldfish/srfi/srfi-27.scm @@ -0,0 +1,247 @@ +;; SRFI-27 Implementation for Goldfish Scheme +;; +;; This is an implementation of SRFI-27 "Sources of Random Bits". +;; It is based on s7.c's built-in random functions. +;; +;; Copyright (C) Sebastian Egner (2002). All Rights Reserved. +;; +;; Permission is hereby granted, free of charge, to any person obtaining +;; a copy of this software and associated documentation files (the +;; "Software"), to deal in the Software without restriction, including +;; without limitation the rights to use, copy, modify, merge, publish, +;; distribute, sublicense, and/or sell copies of the Software, and to +;; permit persons to whom the Software is furnished to do so, subject to +;; the following conditions: +;; +;; The above copyright notice and this permission notice shall be +;; included in all copies or substantial portions of the Software. +;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;; MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +;; LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +;; OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +;; WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +(define-library (srfi srfi-27) + (import (scheme base) + (srfi srfi-19) + (liii error) + ) ;import + (export + random-integer + random-real + default-random-source + make-random-source + random-source? + random-source-state-ref + random-source-state-set! + random-source-randomize! + random-source-pseudo-randomize! + random-source-make-integers + random-source-make-reals + ) ;export + (begin + + ;; ==================== + ;; Random Source Record Type + ;; ==================== + ;; A random-source is a record containing: + ;; - state: the underlying s7 random-state object + ;; - state-ref: thunk to get the state as external representation + ;; - state-set!: procedure to set state from external representation + ;; - randomize!: procedure to randomize state + ;; - pseudo-randomize!: procedure to pseudo-randomize with indices + ;; - make-integers: procedure returning a random-integer generator + ;; - make-reals: procedure returning a random-real generator + + (define-record-type + (%make-random-source state state-ref state-set! randomize! pseudo-randomize! make-integers make-reals) + random-source? + (state random-source-internal-state) + (state-ref random-source-state-ref-proc) + (state-set! random-source-state-set-proc) + (randomize! random-source-randomize-proc) + (pseudo-randomize! random-source-pseudo-randomize-proc) + (make-integers random-source-make-integers-proc) + (make-reals random-source-make-reals-proc) + ) ;define-record-type + + ;; ==================== + ;; Internal Helpers + ;; ==================== + + ;; Get current time in nanoseconds as integer + ;; Used for randomization + (define (current-time-nanoseconds) + (let ((t (current-time TIME-UTC))) + (+ (* (time-second t) 1000000000) + (time-nanosecond t) + ) ;+ + ) ;let + ) ;define + + ;; Create a new s7 random-state with given seed and carry + (define (make-s7-random-state seed carry) + (random-state seed carry) + ) ;define + + ;; Get state as list (seed carry) + (define (get-s7-state state) + (random-state->list state) + ) ;define + + ;; ==================== + ;; Random Source Operations + ;; ==================== + + (define (make-random-source) + (let ((state (random-state 0))) ; Create initial state with seed 0 + (%make-random-source + state + ;; state-ref: return external representation + (lambda () + (cons 'random-source-state (random-state->list state)) + ) ;lambda + ;; state-set!: set state from external representation + (lambda (new-state) + (unless (and (pair? new-state) + (eq? (car new-state) 'random-source-state) + (= (length new-state) 3)) + (error 'wrong-type-arg "invalid random source state" new-state) + ) ;unless + (let ((seed (cadr new-state)) + (carry (caddr new-state))) + (set! state (random-state seed carry)) + ) ;let + ) ;lambda + ;; randomize!: use current time to randomize + (lambda () + (let ((ns (current-time-nanoseconds))) + ;; Use nanoseconds to create a pseudo-random seed + (let ((seed (modulo ns 4294967296)) + (carry (modulo (quotient ns 4294967296) 4294967296))) + (set! state (random-state seed carry)) + ) ;let + ) ;let + ) ;lambda + ;; pseudo-randomize!: use i, j indices + (lambda (i j) + (unless (and (integer? i) (exact? i) (>= i 0)) + (error 'wrong-type-arg "pseudo-randomize! i must be a non-negative exact integer" i) + ) ;unless + (unless (and (integer? j) (exact? j) (>= j 0)) + (error 'wrong-type-arg "pseudo-randomize! j must be a non-negative exact integer" j) + ) ;unless + ;; Create a deterministic state based on i and j + ;; Using a simple hash of i and j to create seed and carry + (let ((seed (modulo (+ (* i 12345) j) 4294967296)) + (carry (modulo (+ (* j 54321) i) 4294967296))) + (set! state (random-state seed carry)) + ) ;let + ) ;lambda + ;; make-integers: return a procedure that generates random integers + (lambda () + (lambda (n) + (unless (and (integer? n) (exact? n) (positive? n)) + (error 'wrong-type-arg "random-integer: n must be a positive exact integer" n) + ) ;unless + ;; s7's random returns [0, n), we need [0, n-1] which is the same + (random n state) + ) ;lambda + ) ;lambda + ;; make-reals: return a procedure that generates random reals + (lambda args + (let ((unit #f)) + (if (pair? args) + (begin + (set! unit (car args)) + (unless (and (real? unit) (< 0 unit 1)) + (error 'wrong-type-arg "random-source-make-reals: unit must be a real in (0,1)" unit) + ) ;unless + ) ;begin + ) ;if + (lambda () + (let ((r (random 1.0 state))) + ;; random returns [0.0, 1.0), but SRFI-27 requires (0, 1) + ;; s7's random for reals already returns (0, 1) when n > 0 + ;; But we need to ensure we never return 0 or 1 + (if (zero? r) + 0.0000000000000001 ; smallest positive value + r + ) ;if + ) ;let + ) ;lambda + ) ;let + ) ;lambda + ) ;%make-random-source + ) ;let + ) ;define + + ;; ==================== + ;; Standard Interface + ;; ==================== + + (define default-random-source (make-random-source)) + + (define (random-integer n) + ((random-source-make-integers default-random-source) n) + ) ;define + + (define (random-real) + ((random-source-make-reals default-random-source)) + ) ;define + + ;; ==================== + ;; Random Source State Operations + ;; ==================== + + (define (random-source-state-ref s) + (unless (random-source? s) + (error 'wrong-type-arg "random-source-state-ref: expected random-source" s) + ) ;unless + ((random-source-state-ref-proc s)) + ) ;define + + (define (random-source-state-set! s new-state) + (unless (random-source? s) + (error 'wrong-type-arg "random-source-state-set!: expected random-source" s) + ) ;unless + ((random-source-state-set-proc s) new-state) + ) ;define + + (define (random-source-randomize! s) + (unless (random-source? s) + (error 'wrong-type-arg "random-source-randomize!: expected random-source" s) + ) ;unless + ((random-source-randomize-proc s)) + ) ;define + + (define (random-source-pseudo-randomize! s i j) + (unless (random-source? s) + (error 'wrong-type-arg "random-source-pseudo-randomize!: expected random-source" s) + ) ;unless + ((random-source-pseudo-randomize-proc s) i j) + ) ;define + + ;; ==================== + ;; Random Source Generator Creation + ;; ==================== + + (define (random-source-make-integers s) + (unless (random-source? s) + (error 'wrong-type-arg "random-source-make-integers: expected random-source" s) + ) ;unless + ((random-source-make-integers-proc s)) + ) ;define + + (define (random-source-make-reals s . unit) + (unless (random-source? s) + (error 'wrong-type-arg "random-source-make-reals: expected random-source" s) + ) ;unless + (apply (random-source-make-reals-proc s) unit) + ) ;define + + ) ;begin +) ;define-library diff --git a/tests/liii/random-test.scm b/tests/liii/random-test.scm new file mode 100644 index 00000000..4b31f830 --- /dev/null +++ b/tests/liii/random-test.scm @@ -0,0 +1,48 @@ +;; (liii random) 模块函数分类索引 +;; +;; (liii random) 是基于 SRFI-27 的随机数生成模块, +;; 提供随机整数、随机实数、随机源管理等功能。 + +;; ==== 常见用法示例 ==== +(import (liii random)) + +;; 示例1:生成随机整数 +(random-integer 100) ; => [0, 99] 范围内的随机整数 + +;; 示例2:生成随机实数 +(random-real) ; => (0, 1) 范围内的随机实数 + +;; 示例3:创建独立随机源 +(let ((s (make-random-source))) + (random-source-pseudo-randomize! s 1 2) + ((random-source-make-integers s) 100) +) ;let + +;; ==== 如何查看函数的文档和用例 ==== +;; bin/gf doc liii/random "random-integer" +;; bin/gf doc liii/random "make-random-source" + +;; ==== 函数分类索引 ==== + +;; 一、基本随机数生成 +;; 使用默认随机源生成随机数 +;; random-integer - 生成 [0, n-1] 范围内的随机整数 +;; random-real - 生成 (0, 1) 范围内的随机实数 + +;; 二、随机源管理 +;; 创建和管理独立的随机源 +;; make-random-source - 创建新的随机源 +;; random-source? - 检查是否为随机源 +;; default-random-source - 默认随机源 + +;; 三、随机源状态操作 +;; 保存、恢复和修改随机源状态 +;; random-source-state-ref - 获取随机源状态 +;; random-source-state-set! - 设置随机源状态 +;; random-source-randomize! - 用当前时间随机化 +;; random-source-pseudo-randomize! - 用索引伪随机化 + +;; 四、生成器创建 +;; 从随机源创建特定的随机数生成器 +;; random-source-make-integers - 创建整数生成器 +;; random-source-make-reals - 创建实数生成器 diff --git a/tests/liii/random/make-random-source-test.scm b/tests/liii/random/make-random-source-test.scm new file mode 100644 index 00000000..30eb9953 --- /dev/null +++ b/tests/liii/random/make-random-source-test.scm @@ -0,0 +1,47 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; make-random-source +;; 创建一个新的随机源。 +;; +;; 语法 +;; ---- +;; (make-random-source) +;; +;; 返回值 +;; ---- +;; random-source +;; 返回一个新的独立随机源对象。 +;; +;; 示例 +;; ---- +;; (make-random-source) => 新的随机源 +;; +;; 注意 +;; ---- +;; 多个随机源是独立的,互不影响。 + +; 创建随机源 +(let ((s1 (make-random-source)) + (s2 (make-random-source))) + (check (random-source? s1) => #t) + (check (random-source? s2) => #t) +) + +; 多个随机源独立 +(let ((s1 (make-random-source)) + (s2 (make-random-source))) + (let ((rand1 (random-source-make-integers s1)) + (rand2 (random-source-make-integers s2))) + (let ((r1 (rand1 1000000)) + (r2 (rand2 1000000))) + (check (integer? r1) => #t) + (check (integer? r2) => #t) + ) + ) +) + +(check-report) diff --git a/tests/liii/random/random-integer-test.scm b/tests/liii/random/random-integer-test.scm new file mode 100644 index 00000000..ef251101 --- /dev/null +++ b/tests/liii/random/random-integer-test.scm @@ -0,0 +1,57 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-integer +;; 生成 [0, n-1] 范围内的随机整数。 +;; +;; 语法 +;; ---- +;; (random-integer n) +;; +;; 参数 +;; ---- +;; n : positive-integer +;; 范围上限(不包含),必须是正整数。 +;; +;; 返回值 +;; ---- +;; integer +;; 返回 [0, n-1] 范围内的随机整数。 +;; +;; 示例 +;; ---- +;; (random-integer 10) => 0~9 之间的随机整数 +;; (random-integer 100) => 0~99 之间的随机整数 +;; +;; 错误处理 +;; ---- +;; wrong-type-arg 当 n 不是正整数时抛出。 + +; 基本功能测试 +(let ((r (random-integer 10))) + (check (integer? r) => #t) + (check (exact? r) => #t) + (check (>= r 0) => #t) + (check (< r 10) => #t) +) + +; 范围边界测试 +(check (>= (random-integer 1) 0) => #t) +(check (< (random-integer 1) 1) => #t) + +; 大范围测试 +(let ((r (random-integer 100))) + (check (>= r 0) => #t) + (check (< r 100) => #t) +) + +; 错误处理 +(check-catch 'wrong-type-arg (random-integer 0)) +(check-catch 'wrong-type-arg (random-integer -1)) +(check-catch 'wrong-type-arg (random-integer 3.14)) +(check-catch 'wrong-type-arg (random-integer 'not-a-number)) + +(check-report) diff --git a/tests/liii/random/random-real-test.scm b/tests/liii/random/random-real-test.scm new file mode 100644 index 00000000..eef97bc4 --- /dev/null +++ b/tests/liii/random/random-real-test.scm @@ -0,0 +1,46 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-real +;; 生成 (0, 1) 范围内的随机实数。 +;; +;; 语法 +;; ---- +;; (random-real) +;; +;; 返回值 +;; ---- +;; real +;; 返回 (0, 1) 范围内的随机实数,不包含 0 和 1。 +;; +;; 示例 +;; ---- +;; (random-real) => (0, 1) 之间的随机实数,如 0.34567 +;; +;; 注意 +;; ---- +;; 多次调用返回不同的值(概率极高)。 + +; 基本功能测试 +(let ((r (random-real))) + (check (real? r) => #t) + (check (> r 0) => #t) + (check (< r 1) => #t) +) + +; 多次调用返回不同值 +(let ((r1 (random-real)) + (r2 (random-real))) + (check (not (= r1 r2)) => #t) +) + +; 范围验证 +(let ((r (random-real))) + (check (>= r 0.0) => #t) + (check (<= r 1.0) => #t) +) + +(check-report) diff --git a/tests/liii/random/random-source-make-integers-test.scm b/tests/liii/random/random-source-make-integers-test.scm new file mode 100644 index 00000000..0bd32773 --- /dev/null +++ b/tests/liii/random/random-source-make-integers-test.scm @@ -0,0 +1,65 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-source-make-integers +;; 从随机源创建整数生成器。 +;; +;; 语法 +;; ---- +;; (random-source-make-integers s) +;; +;; 参数 +;; ---- +;; s : random-source +;; 用于生成随机数的随机源。 +;; +;; 返回值 +;; ---- +;; procedure +;; 返回一个过程 (lambda (n) ...),生成 [0, n-1] 的随机整数。 +;; +;; 示例 +;; ---- +;; (let* ((s (make-random-source)) +;; (rand-int (random-source-make-integers s))) +;; (rand-int 100)) => 0~99 之间的随机整数 +;; +;; 错误处理 +;; ---- +;; wrong-type-arg 当 s 不是随机源时抛出。 + +; 创建整数生成器 +(let* ((s (make-random-source)) + (rand-int (random-source-make-integers s))) + (check (procedure? rand-int) => #t) +) + +; 生成器工作正常 +(let* ((s (make-random-source)) + (rand-int (random-source-make-integers s))) + (let ((r (rand-int 100))) + (check (integer? r) => #t) + (check (>= r 0) => #t) + (check (< r 100) => #t) + ) +) + +; 多次调用 +(let* ((s (make-random-source)) + (rand-int (random-source-make-integers s))) + (let ((r1 (rand-int 100)) + (r2 (rand-int 100)) + (r3 (rand-int 100))) + (check (integer? r1) => #t) + (check (integer? r2) => #t) + (check (integer? r3) => #t) + ) +) + +; 错误处理 +(check-catch 'wrong-type-arg (random-source-make-integers 'not-a-source)) + +(check-report) diff --git a/tests/liii/random/random-source-make-reals-test.scm b/tests/liii/random/random-source-make-reals-test.scm new file mode 100644 index 00000000..940e04cc --- /dev/null +++ b/tests/liii/random/random-source-make-reals-test.scm @@ -0,0 +1,73 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-source-make-reals +;; 从随机源创建实数生成器。 +;; +;; 语法 +;; ---- +;; (random-source-make-reals s) +;; (random-source-make-reals s unit) +;; +;; 参数 +;; ---- +;; s : random-source +;; 用于生成随机数的随机源。 +;; +;; unit : real (可选) +;; 精度单位,必须是 (0, 1) 范围内的实数。 +;; +;; 返回值 +;; ---- +;; procedure +;; 返回一个无参数过程,生成 (0, 1) 的随机实数。 +;; +;; 示例 +;; ---- +;; (let* ((s (make-random-source)) +;; (rand-real (random-source-make-reals s))) +;; (rand-real)) => (0, 1) 之间的随机实数 +;; +;; 错误处理 +;; ---- +;; wrong-type-arg 当 s 不是随机源或 unit 无效时抛出。 + +; 创建实数生成器 +(let* ((s (make-random-source)) + (rand-real (random-source-make-reals s))) + (check (procedure? rand-real) => #t) +) + +; 生成器工作正常 +(let* ((s (make-random-source)) + (rand-real (random-source-make-reals s))) + (let ((r (rand-real))) + (check (real? r) => #t) + (check (> r 0) => #t) + (check (< r 1) => #t) + ) +) + +; 带 unit 参数 +(let* ((s (make-random-source)) + (rand-real (random-source-make-reals s 0.001))) + (let ((r (rand-real))) + (check (real? r) => #t) + (check (> r 0) => #t) + (check (< r 1) => #t) + ) +) + +; 错误处理 +(check-catch 'wrong-type-arg (random-source-make-reals 'not-a-source)) + +(let ((s (make-random-source))) + (check-catch 'wrong-type-arg (random-source-make-reals s 0)) + (check-catch 'wrong-type-arg (random-source-make-reals s 1)) + (check-catch 'wrong-type-arg (random-source-make-reals s -0.5)) +) + +(check-report) diff --git a/tests/liii/random/random-source-p-test.scm b/tests/liii/random/random-source-p-test.scm new file mode 100644 index 00000000..32aed7b3 --- /dev/null +++ b/tests/liii/random/random-source-p-test.scm @@ -0,0 +1,46 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-source? +;; 检查对象是否为随机源。 +;; +;; 语法 +;; ---- +;; (random-source? obj) +;; +;; 参数 +;; ---- +;; obj : any +;; 待检查的对象。 +;; +;; 返回值 +;; ---- +;; boolean +;; 当 obj 是随机源时返回 #t,否则返回 #f。 +;; +;; 示例 +;; ---- +;; (random-source? (make-random-source)) => #t +;; (random-source? 'not-a-source) => #f +;; (random-source? default-random-source) => #t + +; 随机源测试 +(let ((s (make-random-source))) + (check (random-source? s) => #t) +) + +; 非随机源测试 +(check (random-source? 'not-a-source) => #f) +(check (random-source? 123) => #f) +(check (random-source? "string") => #f) +(check (random-source? '(1 2 3)) => #f) +(check (random-source? #t) => #f) +(check (random-source? 3.14) => #f) + +; 默认随机源 +(check (random-source? default-random-source) => #t) + +(check-report) diff --git a/tests/liii/random/random-source-pseudo-randomize-test.scm b/tests/liii/random/random-source-pseudo-randomize-test.scm new file mode 100644 index 00000000..30cc7c1f --- /dev/null +++ b/tests/liii/random/random-source-pseudo-randomize-test.scm @@ -0,0 +1,69 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-source-pseudo-randomize! +;; 使用索引 (i, j) 确定性地设置随机源状态。 +;; +;; 语法 +;; ---- +;; (random-source-pseudo-randomize! s i j) +;; +;; 参数 +;; ---- +;; s : random-source +;; 要设置的随机源。 +;; +;; i, j : non-negative-exact-integer +;; 索引参数,必须是非负精确整数。 +;; +;; 返回值 +;; ---- +;; undefined +;; 副作用:修改 s 的状态。 +;; +;; 示例 +;; ---- +;; (let ((s (make-random-source))) +;; (random-source-pseudo-randomize! s 5 10) +;; s) ; s 现在处于确定状态 +;; +;; 注意 +;; ---- +;; 相同的索引对总是产生相同的状态。 + +; 相同索引产生相同状态 +(let ((s1 (make-random-source)) + (s2 (make-random-source))) + (random-source-pseudo-randomize! s1 0 0) + (random-source-pseudo-randomize! s2 0 0) + (let ((state1 (random-source-state-ref s1)) + (state2 (random-source-state-ref s2))) + (check (equal? state1 state2) => #t) + ) +) + +; 不同索引产生不同状态 +(let ((s (make-random-source))) + (random-source-pseudo-randomize! s 0 0) + (let ((state1 (random-source-state-ref s))) + (random-source-pseudo-randomize! s 1 2) + (let ((state2 (random-source-state-ref s))) + (check (not (equal? state1 state2)) => #t) + ) + ) +) + +; 错误处理 +(check-catch 'wrong-type-arg (random-source-pseudo-randomize! 'not-a-source 0 0)) + +(let ((s (make-random-source))) + (check-catch 'wrong-type-arg (random-source-pseudo-randomize! s -1 0)) + (check-catch 'wrong-type-arg (random-source-pseudo-randomize! s 0 -1)) + (check-catch 'wrong-type-arg (random-source-pseudo-randomize! s 3.14 0)) + (check-catch 'wrong-type-arg (random-source-pseudo-randomize! s 0 3.14)) +) + +(check-report) diff --git a/tests/liii/random/random-source-randomize-test.scm b/tests/liii/random/random-source-randomize-test.scm new file mode 100644 index 00000000..4491d725 --- /dev/null +++ b/tests/liii/random/random-source-randomize-test.scm @@ -0,0 +1,59 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-source-randomize! +;; 使用当前时间随机化随机源的状态。 +;; +;; 语法 +;; ---- +;; (random-source-randomize! s) +;; +;; 参数 +;; ---- +;; s : random-source +;; 要随机化的随机源。 +;; +;; 返回值 +;; ---- +;; undefined +;; 副作用:修改 s 的状态。 +;; +;; 示例 +;; ---- +;; (let ((s (make-random-source))) +;; (random-source-randomize! s) +;; s) ; s 现在处于随机状态 +;; +;; 错误处理 +;; ---- +;; wrong-type-arg 当 s 不是随机源时抛出。 + +; 随机化改变状态 +(let ((s (make-random-source))) + (let ((state1 (random-source-state-ref s))) + (random-source-randomize! s) + (let ((state2 (random-source-state-ref s))) + (check (not (equal? state1 state2)) => #t) + ) + ) +) + +; 多次随机化产生不同状态 +(let ((s (make-random-source))) + (random-source-randomize! s) + (let ((state1 (random-source-state-ref s))) + (random-source-randomize! s) + (let ((state2 (random-source-state-ref s))) + (check (not (equal? state1 state2)) => #t) + ) + ) +) + +; 错误处理 +(check-catch 'wrong-type-arg (random-source-randomize! 'not-a-source)) +(check-catch 'wrong-type-arg (random-source-randomize! 123)) + +(check-report) diff --git a/tests/liii/random/random-source-state-ref-test.scm b/tests/liii/random/random-source-state-ref-test.scm new file mode 100644 index 00000000..28023436 --- /dev/null +++ b/tests/liii/random/random-source-state-ref-test.scm @@ -0,0 +1,59 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-source-state-ref +;; 获取随机源的当前状态。 +;; +;; 语法 +;; ---- +;; (random-source-state-ref s) +;; +;; 参数 +;; ---- +;; s : random-source +;; 要获取状态的随机源。 +;; +;; 返回值 +;; ---- +;; list +;; 返回形式为 (random-source-state seed carry) 的列表。 +;; +;; 示例 +;; ---- +;; (random-source-state-ref (make-random-source)) +;; => (random-source-state 0 1675393560) +;; +;; 错误处理 +;; ---- +;; wrong-type-arg 当 s 不是随机源时抛出。 + +; 基本功能测试 +(let ((s (make-random-source))) + (let ((state (random-source-state-ref s))) + (check (pair? state) => #t) + (check (eq? (car state) 'random-source-state) => #t) + (check (= (length state) 3) => #t) + ) +) + +; 状态在生成随机数后变化 +(let ((s (make-random-source))) + (let ((state1 (random-source-state-ref s))) + (let ((rand-int (random-source-make-integers s))) + (rand-int 100) + (let ((state2 (random-source-state-ref s))) + (check (not (equal? state1 state2)) => #t) + ) + ) + ) +) + +; 错误处理 +(check-catch 'wrong-type-arg (random-source-state-ref 'not-a-source)) +(check-catch 'wrong-type-arg (random-source-state-ref 123)) +(check-catch 'wrong-type-arg (random-source-state-ref "string")) + +(check-report) diff --git a/tests/liii/random/random-source-state-set-test.scm b/tests/liii/random/random-source-state-set-test.scm new file mode 100644 index 00000000..2414c39d --- /dev/null +++ b/tests/liii/random/random-source-state-set-test.scm @@ -0,0 +1,54 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; random-source-state-set! +;; 设置随机源的状态。 +;; +;; 语法 +;; ---- +;; (random-source-state-set! s state) +;; +;; 参数 +;; ---- +;; s : random-source +;; 要设置状态的随机源。 +;; +;; state : list +;; 状态列表,格式为 (random-source-state seed carry)。 +;; +;; 示例 +;; ---- +;; (let ((s (make-random-source))) +;; (random-source-state-set! s '(random-source-state 123 456))) +;; +;; 错误处理 +;; ---- +;; wrong-type-arg 当 s 不是随机源或 state 格式无效时抛出。 + +; 保存和恢复状态 +(let ((s (make-random-source))) + (let ((saved-state (random-source-state-ref s))) + (let ((rand-int (random-source-make-integers s))) + (rand-int 100) + (rand-int 100) + (random-source-state-set! s saved-state) + (let ((reset-state (random-source-state-ref s))) + (check (equal? saved-state reset-state) => #t) + ) + ) + ) +) + +; 错误处理 +(check-catch 'wrong-type-arg (random-source-state-set! 'not-a-source '(random-source-state 0 0))) + +(let ((s (make-random-source))) + (check-catch 'wrong-type-arg (random-source-state-set! s 'invalid-state)) + (check-catch 'wrong-type-arg (random-source-state-set! s '(not-the-right-tag 0 0))) + (check-catch 'wrong-type-arg (random-source-state-set! s '(random-source-state))) +) + +(check-report) diff --git a/tests/liii/random/reproducibility-test.scm b/tests/liii/random/reproducibility-test.scm new file mode 100644 index 00000000..5ccdcf50 --- /dev/null +++ b/tests/liii/random/reproducibility-test.scm @@ -0,0 +1,50 @@ +(import (liii random) + (liii check) +) ;import + +(check-set-mode! 'report-failed) + +;; 可复现性测试 +;; 验证保存和恢复随机源状态可以重现相同的随机数序列。 +;; +;; 示例 +;; ---- +;; 保存状态 -> 生成随机数 -> 恢复状态 -> 生成相同随机数 + +; 保存/恢复状态产生相同序列 +(let ((s (make-random-source))) + (let ((saved-state (random-source-state-ref s))) + (let ((rand-int (random-source-make-integers s))) + (let ((r1 (rand-int 100)) + (r2 (rand-int 100)) + (r3 (rand-int 100))) + (random-source-state-set! s saved-state) + (let ((rand-int2 (random-source-make-integers s))) + (let ((r1b (rand-int2 100)) + (r2b (rand-int2 100)) + (r3b (rand-int2 100))) + (check (= r1 r1b) => #t) + (check (= r2 r2b) => #t) + (check (= r3 r3b) => #t) + ) + ) + ) + ) + ) +) + +; 相同伪随机化索引产生相同序列 +(let ((s1 (make-random-source)) + (s2 (make-random-source))) + (random-source-pseudo-randomize! s1 5 10) + (random-source-pseudo-randomize! s2 5 10) + (let ((rand1 (random-source-make-integers s1)) + (rand2 (random-source-make-integers s2))) + (let ((r1 (rand1 1000)) + (r2 (rand2 1000))) + (check (= r1 r2) => #t) + ) + ) +) + +(check-report)