use super::harness::*; use cagire::forth::ResolvedValue; #[test] fn rand_in_range() { let f = forth_seeded(12345); f.evaluate("0 10 rand", &default_ctx()).unwrap(); let val = stack_float(&f); assert!(val >= 0.0 && val < 10.0, "rand {} not in [0, 10)", val); } #[test] fn rand_deterministic() { let f1 = forth_seeded(99); let f2 = forth_seeded(99); f1.evaluate("0 100 rand", &default_ctx()).unwrap(); f2.evaluate("0 100 rand", &default_ctx()).unwrap(); assert_eq!(f1.stack(), f2.stack()); } #[test] fn seed_resets() { let f1 = forth_seeded(1); f1.evaluate("42 seed 0 100 rand", &default_ctx()).unwrap(); let f2 = forth_seeded(999); f2.evaluate("42 seed 0 100 rand", &default_ctx()).unwrap(); assert_eq!(f1.stack(), f2.stack()); } #[test] fn coin_binary() { let f = forth_seeded(42); f.evaluate("coin", &default_ctx()).unwrap(); let val = stack_int(&f); assert!(val == 0 || val == 1); } #[test] fn chance_zero() { // 0.0 probability should never execute the quotation let f = run("99 { 42 } 0.0 chance"); assert_eq!(stack_int(&f), 99); // quotation not executed, 99 still on stack } #[test] fn chance_one() { // 1.0 probability should always execute the quotation let f = run("{ 42 } 1.0 chance"); assert_eq!(stack_int(&f), 42); } #[test] fn choose_from_list() { let f = forth_seeded(42); f.evaluate("10 20 30 3 choose", &default_ctx()).unwrap(); let val = stack_int(&f); assert!(val == 10 || val == 20 || val == 30); } #[test] fn choose_underflow() { expect_error("1 2 5 choose", "stack underflow"); } #[test] fn cycle_deterministic() { for runs in 0..6 { let ctx = ctx_with(|c| c.runs = runs); let f = run_ctx("10 20 30 3 cycle", &ctx); let expected = [10, 20, 30][runs % 3]; assert_eq!(stack_int(&f), expected, "cycle at runs={}", runs); } } #[test] fn cycle_zero_count() { expect_error("1 2 3 0 cycle", "cycle count must be > 0"); } #[test] fn mtof_a4() { expect_float("69 mtof", 440.0); } #[test] fn mtof_octave() { expect_float("81 mtof", 880.0); } #[test] fn mtof_c4() { expect_floats_close("60 mtof", 261.6255653, 0.001); } #[test] fn ftom_440() { expect_float("440 ftom", 69.0); } #[test] fn ftom_880() { expect_float("880 ftom", 81.0); } #[test] fn mtof_ftom_roundtrip() { expect_float("60 mtof ftom", 60.0); } #[test] fn exprand_in_range() { let f = forth_seeded(12345); f.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap(); let val = stack_float(&f); assert!(val >= 1.0 && val <= 100.0, "exprand {} not in [1, 100]", val); } #[test] fn exprand_deterministic() { let f1 = forth_seeded(99); let f2 = forth_seeded(99); f1.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap(); f2.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap(); assert_eq!(f1.stack(), f2.stack()); } #[test] fn exprand_swapped_args() { let f1 = forth_seeded(42); let f2 = forth_seeded(42); f1.evaluate("1.0 100.0 exprand", &default_ctx()).unwrap(); f2.evaluate("100.0 1.0 exprand", &default_ctx()).unwrap(); assert_eq!(f1.stack(), f2.stack()); } #[test] fn exprand_requires_positive() { expect_error("0.0 10.0 exprand", "exprand requires positive values"); expect_error("-1.0 10.0 exprand", "exprand requires positive values"); expect_error("1.0 0.0 exprand", "exprand requires positive values"); } #[test] fn logrand_in_range() { let f = forth_seeded(12345); f.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap(); let val = stack_float(&f); assert!(val >= 1.0 && val <= 100.0, "logrand {} not in [1, 100]", val); } #[test] fn logrand_deterministic() { let f1 = forth_seeded(99); let f2 = forth_seeded(99); f1.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap(); f2.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap(); assert_eq!(f1.stack(), f2.stack()); } #[test] fn logrand_swapped_args() { let f1 = forth_seeded(42); let f2 = forth_seeded(42); f1.evaluate("1.0 100.0 logrand", &default_ctx()).unwrap(); f2.evaluate("100.0 1.0 logrand", &default_ctx()).unwrap(); assert_eq!(f1.stack(), f2.stack()); } #[test] fn logrand_requires_positive() { expect_error("0.0 10.0 logrand", "logrand requires positive values"); expect_error("-1.0 10.0 logrand", "logrand requires positive values"); expect_error("1.0 0.0 logrand", "logrand requires positive values"); } #[test] fn rand_equal_bounds() { expect_float("5.0 5.0 rand", 5.0); } // bounce #[test] fn bounce_sequence() { let expected = [60, 64, 67, 72, 67, 64, 60, 64]; for (runs, &exp) in expected.iter().enumerate() { let ctx = ctx_with(|c| c.runs = runs); let f = run_ctx("60 64 67 72 4 bounce", &ctx); assert_eq!(stack_int(&f), exp, "bounce at runs={}", runs); } } #[test] fn bounce_single() { for runs in 0..5 { let ctx = ctx_with(|c| c.runs = runs); let f = run_ctx("42 1 bounce", &ctx); assert_eq!(stack_int(&f), 42, "bounce single at runs={}", runs); } } #[test] fn bounce_zero_count() { expect_error("1 2 3 0 bounce", "bounce count must be > 0"); } #[test] fn bounce_underflow() { expect_error("1 2 5 bounce", "stack underflow"); } // pbounce #[test] fn pbounce_by_iter() { let expected = [60, 64, 67, 72, 67, 64, 60, 64]; for (iter, &exp) in expected.iter().enumerate() { let ctx = ctx_with(|c| c.iter = iter); let f = run_ctx("60 64 67 72 4 pbounce", &ctx); assert_eq!(stack_int(&f), exp, "pbounce at iter={}", iter); } } #[test] fn pbounce_single() { for iter in 0..5 { let ctx = ctx_with(|c| c.iter = iter); let f = run_ctx("42 1 pbounce", &ctx); assert_eq!(stack_int(&f), 42, "pbounce single at iter={}", iter); } } #[test] fn pbounce_with_bracket() { let expected = [60, 64, 67, 64, 60, 64]; for (iter, &exp) in expected.iter().enumerate() { let ctx = ctx_with(|c| c.iter = iter); let f = run_ctx("[ 60 64 67 ] pbounce", &ctx); assert_eq!(stack_int(&f), exp, "pbounce bracket at iter={}", iter); } } // wchoose #[test] fn wchoose_all_weight_one_item() { for _ in 0..10 { let f = forth_seeded(42); f.evaluate("10 0.0 20 1.0 2 wchoose", &default_ctx()) .unwrap(); assert_eq!(stack_int(&f), 20); } } #[test] fn wchoose_deterministic() { let f1 = forth_seeded(99); let f2 = forth_seeded(99); f1.evaluate("60 0.6 64 0.3 67 0.1 3 wchoose", &default_ctx()) .unwrap(); f2.evaluate("60 0.6 64 0.3 67 0.1 3 wchoose", &default_ctx()) .unwrap(); assert_eq!(f1.stack(), f2.stack()); } #[test] fn wchoose_zero_count() { expect_error("0 wchoose", "wchoose count must be > 0"); } #[test] fn wchoose_underflow() { expect_error("10 0.5 2 wchoose", "stack underflow"); } #[test] fn wchoose_negative_weight() { expect_error("10 -0.5 20 1.0 2 wchoose", "negative weight"); } #[test] fn wchoose_quotation() { let f = forth_seeded(42); f.evaluate("{ 10 } 0.0 { 20 } 1.0 2 wchoose", &default_ctx()) .unwrap(); assert_eq!(stack_int(&f), 20); } #[test] fn choose_trace_resolved_span() { let script = "sine tri 2 choose"; let (_f, trace) = run_with_trace(script); assert_eq!(trace.resolved.len(), 1, "expected 1 resolved entry: {:?}", trace.resolved); let (span, ref val) = trace.resolved[0]; assert_eq!(span.start, 11); assert_eq!(span.end, 17); assert!(matches!(val, ResolvedValue::Str(s) if s.as_ref() == "sine" || s.as_ref() == "tri")); }