1use luau_common::flags;
2use web_time::{SystemTime, UNIX_EPOCH};
3
4use crate::handle::RawHandle;
5use crate::native::{NativeCallContext, NativeCallResult, NativeFunction};
6use crate::thread::{LUA_TNONE, Thread};
7use crate::types::LUA_TNIL;
8
9const LUAU_PI: f64 = core::f64::consts::PI;
10const RADIANS_PER_DEGREE: f64 = LUAU_PI / 180.0;
11const LUAU_E: f64 = core::f64::consts::E;
12#[allow(clippy::excessive_precision)] const LUAU_PHI: f64 = 1.61803398874989484820;
14const LUAU_SQRT2: f64 = core::f64::consts::SQRT_2;
15const LUAU_TAU: f64 = core::f64::consts::TAU;
16const PCG32_INC: u64 = 105;
17
18static MATH_LIB: [NativeFunction; 37] = [
19 NativeFunction {
20 name: "abs",
21 function: math_abs,
22 },
23 NativeFunction {
24 name: "acos",
25 function: math_acos,
26 },
27 NativeFunction {
28 name: "asin",
29 function: math_asin,
30 },
31 NativeFunction {
32 name: "atan2",
33 function: math_atan2,
34 },
35 NativeFunction {
36 name: "atan",
37 function: math_atan,
38 },
39 NativeFunction {
40 name: "ceil",
41 function: math_ceil,
42 },
43 NativeFunction {
44 name: "cosh",
45 function: math_cosh,
46 },
47 NativeFunction {
48 name: "cos",
49 function: math_cos,
50 },
51 NativeFunction {
52 name: "deg",
53 function: math_deg,
54 },
55 NativeFunction {
56 name: "exp",
57 function: math_exp,
58 },
59 NativeFunction {
60 name: "floor",
61 function: math_floor,
62 },
63 NativeFunction {
64 name: "fmod",
65 function: math_fmod,
66 },
67 NativeFunction {
68 name: "frexp",
69 function: math_frexp,
70 },
71 NativeFunction {
72 name: "ldexp",
73 function: math_ldexp,
74 },
75 NativeFunction {
76 name: "log10",
77 function: math_log10,
78 },
79 NativeFunction {
80 name: "log",
81 function: math_log,
82 },
83 NativeFunction {
84 name: "max",
85 function: math_max,
86 },
87 NativeFunction {
88 name: "min",
89 function: math_min,
90 },
91 NativeFunction {
92 name: "modf",
93 function: math_modf,
94 },
95 NativeFunction {
96 name: "pow",
97 function: math_pow,
98 },
99 NativeFunction {
100 name: "rad",
101 function: math_rad,
102 },
103 NativeFunction {
104 name: "random",
105 function: math_random,
106 },
107 NativeFunction {
108 name: "randomseed",
109 function: math_randomseed,
110 },
111 NativeFunction {
112 name: "sinh",
113 function: math_sinh,
114 },
115 NativeFunction {
116 name: "sin",
117 function: math_sin,
118 },
119 NativeFunction {
120 name: "sqrt",
121 function: math_sqrt,
122 },
123 NativeFunction {
124 name: "tanh",
125 function: math_tanh,
126 },
127 NativeFunction {
128 name: "tan",
129 function: math_tan,
130 },
131 NativeFunction {
132 name: "noise",
133 function: math_noise,
134 },
135 NativeFunction {
136 name: "clamp",
137 function: math_clamp,
138 },
139 NativeFunction {
140 name: "sign",
141 function: math_sign,
142 },
143 NativeFunction {
144 name: "round",
145 function: math_round,
146 },
147 NativeFunction {
148 name: "map",
149 function: math_map,
150 },
151 NativeFunction {
152 name: "lerp",
153 function: math_lerp,
154 },
155 NativeFunction {
156 name: "isnan",
157 function: math_isnan,
158 },
159 NativeFunction {
160 name: "isinf",
161 function: math_isinf,
162 },
163 NativeFunction {
164 name: "isfinite",
165 function: math_isfinite,
166 },
167];
168
169const PERLIN_HASH: [u8; 257] = [
170 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69,
171 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219,
172 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
173 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230,
174 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76,
175 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173,
176 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206,
177 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163,
178 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232,
179 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162,
180 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204,
181 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141,
182 128, 195, 78, 66, 215, 61, 156, 180, 151,
183];
184
185const PERLIN_GRAD: [[f32; 3]; 16] = [
186 [1.0, 1.0, 0.0],
187 [-1.0, 1.0, 0.0],
188 [1.0, -1.0, 0.0],
189 [-1.0, -1.0, 0.0],
190 [1.0, 0.0, 1.0],
191 [-1.0, 0.0, 1.0],
192 [1.0, 0.0, -1.0],
193 [-1.0, 0.0, -1.0],
194 [0.0, 1.0, 1.0],
195 [0.0, -1.0, 1.0],
196 [0.0, 1.0, -1.0],
197 [0.0, -1.0, -1.0],
198 [1.0, 1.0, 0.0],
199 [0.0, -1.0, 1.0],
200 [-1.0, 1.0, 0.0],
201 [0.0, -1.0, -1.0],
202];
203
204fn pcg32_random(state: &mut u64) -> u32 {
206 let old_state = *state;
207 *state = old_state
208 .wrapping_mul(6364136223846793005)
209 .wrapping_add(PCG32_INC | 1);
210 let xorshifted = (((old_state >> 18) ^ old_state) >> 27) as u32;
211 let rot = (old_state >> 59) as u32;
212 xorshifted.rotate_right(rot)
213}
214
215fn pcg32_seed(state: &mut u64, seed: u64) {
217 *state = 0;
218 let _ = pcg32_random(state);
219 *state = state.wrapping_add(seed);
220 let _ = pcg32_random(state);
221}
222
223fn perlin_fade(t: f32) -> f32 {
225 t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
226}
227
228fn perlin_lerp(t: f32, a: f32, b: f32) -> f32 {
230 a + t * (b - a)
231}
232
233fn perlin_grad(hash: i32, x: f32, y: f32, z: f32) -> f32 {
235 let grad = PERLIN_GRAD[(hash & 15) as usize];
236 grad[0] * x + grad[1] * y + grad[2] * z
237}
238
239fn perlin(x: f32, y: f32, z: f32) -> f32 {
241 let x_floor = x.floor();
242 let y_floor = y.floor();
243 let z_floor = z.floor();
244
245 let xi = (x_floor as i32) & 255;
246 let yi = (y_floor as i32) & 255;
247 let zi = (z_floor as i32) & 255;
248
249 let xf = x - x_floor;
250 let yf = y - y_floor;
251 let zf = z - z_floor;
252
253 let u = perlin_fade(xf);
254 let v = perlin_fade(yf);
255 let w = perlin_fade(zf);
256
257 let p = &PERLIN_HASH;
258
259 let a = (p[xi as usize] as i32 + yi) & 255;
260 let aa = (p[a as usize] as i32 + zi) & 255;
261 let ab = (p[(a + 1) as usize] as i32 + zi) & 255;
262
263 let b = (p[(xi + 1) as usize] as i32 + yi) & 255;
264 let ba = (p[b as usize] as i32 + zi) & 255;
265 let bb = (p[(b + 1) as usize] as i32 + zi) & 255;
266
267 let la = perlin_lerp(
268 u,
269 perlin_grad(p[aa as usize] as i32, xf, yf, zf),
270 perlin_grad(p[ba as usize] as i32, xf - 1.0, yf, zf),
271 );
272 let lb = perlin_lerp(
273 u,
274 perlin_grad(p[ab as usize] as i32, xf, yf - 1.0, zf),
275 perlin_grad(p[bb as usize] as i32, xf - 1.0, yf - 1.0, zf),
276 );
277 let la1 = perlin_lerp(
278 u,
279 perlin_grad(p[(aa + 1) as usize] as i32, xf, yf, zf - 1.0),
280 perlin_grad(p[(ba + 1) as usize] as i32, xf - 1.0, yf, zf - 1.0),
281 );
282 let lb1 = perlin_lerp(
283 u,
284 perlin_grad(p[(ab + 1) as usize] as i32, xf, yf - 1.0, zf - 1.0),
285 perlin_grad(p[(bb + 1) as usize] as i32, xf - 1.0, yf - 1.0, zf - 1.0),
286 );
287
288 perlin_lerp(w, perlin_lerp(v, la, lb), perlin_lerp(v, la1, lb1))
289}
290
291fn modf_parts(value: f64) -> (f64, f64) {
292 let integer = value.trunc();
293 let fractional = value - integer;
294 (integer, fractional)
295}
296
297fn frexp_parts(value: f64) -> (f64, i32) {
298 if value == 0.0 || !value.is_finite() {
299 return (value, 0);
300 }
301
302 let bits = value.to_bits();
303 let sign = bits & (1u64 << 63);
304 let exponent = ((bits >> 52) & 0x7ff) as i32;
305 let mantissa = bits & ((1u64 << 52) - 1);
306
307 if exponent == 0 {
308 let (mantissa, exponent) = frexp_parts(value * ((1u64 << 53) as f64));
309 return (mantissa, exponent - 53);
310 }
311
312 let normalized = f64::from_bits(sign | (1022u64 << 52) | mantissa);
313 (normalized, exponent - 1022)
314}
315
316fn ldexp_value(value: f64, exponent: i32) -> f64 {
317 value * (2.0f64).powi(exponent)
318}
319
320fn math_abs(ctx: NativeCallContext) -> NativeCallResult {
322 ctx.push_number(ctx.arg(1).number()?.abs())?;
323 Ok(1)
324}
325
326fn math_sin(ctx: NativeCallContext) -> NativeCallResult {
328 ctx.push_number(ctx.arg(1).number()?.sin())?;
329 Ok(1)
330}
331
332fn math_sinh(ctx: NativeCallContext) -> NativeCallResult {
334 ctx.push_number(ctx.arg(1).number()?.sinh())?;
335 Ok(1)
336}
337
338fn math_cos(ctx: NativeCallContext) -> NativeCallResult {
340 ctx.push_number(ctx.arg(1).number()?.cos())?;
341 Ok(1)
342}
343
344fn math_cosh(ctx: NativeCallContext) -> NativeCallResult {
346 ctx.push_number(ctx.arg(1).number()?.cosh())?;
347 Ok(1)
348}
349
350fn math_tan(ctx: NativeCallContext) -> NativeCallResult {
352 ctx.push_number(ctx.arg(1).number()?.tan())?;
353 Ok(1)
354}
355
356fn math_tanh(ctx: NativeCallContext) -> NativeCallResult {
358 ctx.push_number(ctx.arg(1).number()?.tanh())?;
359 Ok(1)
360}
361
362fn math_asin(ctx: NativeCallContext) -> NativeCallResult {
364 ctx.push_number(ctx.arg(1).number()?.asin())?;
365 Ok(1)
366}
367
368fn math_acos(ctx: NativeCallContext) -> NativeCallResult {
370 ctx.push_number(ctx.arg(1).number()?.acos())?;
371 Ok(1)
372}
373
374fn math_atan(ctx: NativeCallContext) -> NativeCallResult {
376 ctx.push_number(ctx.arg(1).number()?.atan())?;
377 Ok(1)
378}
379
380fn math_atan2(ctx: NativeCallContext) -> NativeCallResult {
382 ctx.push_number(ctx.arg(1).number()?.atan2(ctx.arg(2).number()?))?;
383 Ok(1)
384}
385
386fn math_ceil(ctx: NativeCallContext) -> NativeCallResult {
388 ctx.push_number(ctx.arg(1).number()?.ceil())?;
389 Ok(1)
390}
391
392fn math_floor(ctx: NativeCallContext) -> NativeCallResult {
394 ctx.push_number(ctx.arg(1).number()?.floor())?;
395 Ok(1)
396}
397
398fn math_fmod(ctx: NativeCallContext) -> NativeCallResult {
400 ctx.push_number(ctx.arg(1).number()? % ctx.arg(2).number()?)?;
401 Ok(1)
402}
403
404fn math_modf(ctx: NativeCallContext) -> NativeCallResult {
406 let thread = ctx.raw_thread();
407 let (integer, fractional) = modf_parts(unsafe { thread.check_number(1)? });
408 unsafe {
409 thread.push_number(integer)?;
410 thread.push_number(fractional)?;
411 }
412 Ok(2)
413}
414
415fn math_sqrt(ctx: NativeCallContext) -> NativeCallResult {
417 ctx.push_number(ctx.arg(1).number()?.sqrt())?;
418 Ok(1)
419}
420
421fn math_pow(ctx: NativeCallContext) -> NativeCallResult {
423 ctx.push_number(ctx.arg(1).number()?.powf(ctx.arg(2).number()?))?;
424 Ok(1)
425}
426
427fn math_log(ctx: NativeCallContext) -> NativeCallResult {
429 let thread = ctx.raw_thread();
430 let result = unsafe {
431 let x = thread.check_number(1)?;
432 if matches!(thread.type_of(2), LUA_TNONE | LUA_TNIL) {
433 x.ln()
434 } else {
435 let base = thread.check_number(2)?;
436 if base == 2.0 {
437 x.log2()
438 } else if base == 10.0 {
439 x.log10()
440 } else {
441 x.ln() / base.ln()
442 }
443 }
444 };
445
446 ctx.push_number(result)?;
447 Ok(1)
448}
449
450fn math_log10(ctx: NativeCallContext) -> NativeCallResult {
452 ctx.push_number(ctx.arg(1).number()?.log10())?;
453 Ok(1)
454}
455
456fn math_exp(ctx: NativeCallContext) -> NativeCallResult {
458 ctx.push_number(ctx.arg(1).number()?.exp())?;
459 Ok(1)
460}
461
462fn math_deg(ctx: NativeCallContext) -> NativeCallResult {
464 ctx.push_number(ctx.arg(1).number()? / RADIANS_PER_DEGREE)?;
465 Ok(1)
466}
467
468fn math_rad(ctx: NativeCallContext) -> NativeCallResult {
470 ctx.push_number(ctx.arg(1).number()? * RADIANS_PER_DEGREE)?;
471 Ok(1)
472}
473
474fn math_frexp(ctx: NativeCallContext) -> NativeCallResult {
476 let thread = ctx.raw_thread();
477 let (mantissa, exponent) = frexp_parts(unsafe { thread.check_number(1)? });
478 unsafe {
479 thread.push_number(mantissa)?;
480 thread.push_integer(exponent)?;
481 }
482 Ok(2)
483}
484
485fn math_ldexp(ctx: NativeCallContext) -> NativeCallResult {
487 ctx.push_number(ldexp_value(ctx.arg(1).number()?, ctx.arg(2).integer()?))?;
488 Ok(1)
489}
490
491fn math_min(ctx: NativeCallContext) -> NativeCallResult {
493 let mut result = ctx.arg(1).number()?;
494 for argument in ctx.args().skip(1) {
495 let value = argument.number()?;
496 if value < result {
497 result = value;
498 }
499 }
500 ctx.push_number(result)?;
501 Ok(1)
502}
503
504fn math_max(ctx: NativeCallContext) -> NativeCallResult {
506 let mut result = ctx.arg(1).number()?;
507 for argument in ctx.args().skip(1) {
508 let value = argument.number()?;
509 if value > result {
510 result = value;
511 }
512 }
513 ctx.push_number(result)?;
514 Ok(1)
515}
516
517fn math_random(ctx: NativeCallContext) -> NativeCallResult {
519 let thread = ctx.raw_thread();
520 let global = unsafe { thread.global() };
521 let state = unsafe { &mut global.as_ptr().as_mut().unwrap_unchecked().rng_state };
522 unsafe {
523 match thread.get_top() {
524 0 => {
525 let low = pcg32_random(state);
526 let high = pcg32_random(state);
527 let result = ldexp_value((low as u64 | ((high as u64) << 32)) as f64, -64);
528 thread.push_number(result)?;
529 }
530 1 => {
531 let upper = thread.check_integer(1)?;
532 if upper < 1 {
533 return thread
534 .lua_arg_error(1, "interval is empty")
535 .map_err(Into::into);
536 }
537
538 let x = upper as u64 * pcg32_random(state) as u64;
539 let result = 1 + (x >> 32) as i32;
540 thread.push_integer(result)?;
541 }
542 2 => {
543 let lower = thread.check_integer(1)?;
544 let upper = thread.check_integer(2)?;
545 if lower > upper {
546 return thread
547 .lua_arg_error(2, "interval is empty")
548 .map_err(Into::into);
549 }
550
551 let interval = (upper as u32).wrapping_sub(lower as u32);
552 if interval == u32::MAX {
553 return thread
554 .lua_arg_error(2, "interval is too large")
555 .map_err(Into::into);
556 }
557
558 let x = (interval as u64 + 1) * pcg32_random(state) as u64;
559 let result = lower.wrapping_add((x >> 32) as i32);
560 thread.push_integer(result)?;
561 }
562 _ => {
563 return crate::error!(thread, "wrong number of arguments").map_err(Into::into);
564 }
565 }
566 }
567 Ok(1)
568}
569
570fn math_randomseed(ctx: NativeCallContext) -> NativeCallResult {
572 let seed = ctx.arg(1).integer()? as u64;
573 let global = unsafe { ctx.raw_thread().global() };
574 pcg32_seed(
575 unsafe { &mut global.as_ptr().as_mut().unwrap_unchecked().rng_state },
576 seed,
577 );
578 Ok(0)
579}
580
581fn math_noise(ctx: NativeCallContext) -> NativeCallResult {
583 let mut x = ctx.arg(1).number()?;
584 let mut y = ctx.arg(2).number_or(0.0)?;
585 let mut z = ctx.arg(3).number_or(0.0)?;
586
587 if flags::FixMathNoisePrecision.get() {
588 x %= 256.0;
589 y %= 256.0;
590 z %= 256.0;
591 }
592
593 ctx.push_number(perlin(x as f32, y as f32, z as f32) as f64)?;
594 Ok(1)
595}
596
597fn math_clamp(ctx: NativeCallContext) -> NativeCallResult {
599 let value = ctx.arg(1).number()?;
600 let min = ctx.arg(2).number()?;
601 let max = ctx.arg(3).number()?;
602 if min > max {
603 return ctx
604 .arg(3)
605 .error("max must be greater than or equal to min")
606 .map_err(Into::into);
607 }
608
609 ctx.push_number(value.clamp(min, max))?;
610 Ok(1)
611}
612
613fn math_sign(ctx: NativeCallContext) -> NativeCallResult {
615 let value = ctx.arg(1).number()?;
616 ctx.push_number(if value > 0.0 {
617 1.0
618 } else if value < 0.0 {
619 -1.0
620 } else {
621 0.0
622 })?;
623 Ok(1)
624}
625
626fn math_round(ctx: NativeCallContext) -> NativeCallResult {
628 ctx.push_number(ctx.arg(1).number()?.round())?;
629 Ok(1)
630}
631
632fn math_map(ctx: NativeCallContext) -> NativeCallResult {
634 let x = ctx.arg(1).number()?;
635 let in_min = ctx.arg(2).number()?;
636 let in_max = ctx.arg(3).number()?;
637 let out_min = ctx.arg(4).number()?;
638 let out_max = ctx.arg(5).number()?;
639
640 ctx.push_number(out_min + (x - in_min) * (out_max - out_min) / (in_max - in_min))?;
641 Ok(1)
642}
643
644fn math_lerp(ctx: NativeCallContext) -> NativeCallResult {
646 let a = ctx.arg(1).number()?;
647 let b = ctx.arg(2).number()?;
648 let t = ctx.arg(3).number()?;
649 ctx.push_number(if t == 1.0 { b } else { a + (b - a) * t })?;
650 Ok(1)
651}
652
653fn math_isnan(ctx: NativeCallContext) -> NativeCallResult {
655 ctx.push_boolean(ctx.arg(1).number()?.is_nan())?;
656 Ok(1)
657}
658
659fn math_isinf(ctx: NativeCallContext) -> NativeCallResult {
661 ctx.push_boolean(ctx.arg(1).number()?.is_infinite())?;
662 Ok(1)
663}
664
665fn math_isfinite(ctx: NativeCallContext) -> NativeCallResult {
667 ctx.push_boolean(ctx.arg(1).number()?.is_finite())?;
668 Ok(1)
669}
670
671impl Thread {
672 pub unsafe fn open_math(&self) -> NativeCallResult {
674 unsafe {
675 let mut seed = self.encode_pointer(self.as_ptr() as usize) as u64;
676 seed ^= SystemTime::now()
677 .duration_since(UNIX_EPOCH)
678 .map_or(0, |duration| duration.as_secs());
679 seed ^= crate::clock().to_bits();
680
681 pcg32_seed(
682 &mut self.global().as_ptr().as_mut().unwrap_unchecked().rng_state,
683 seed,
684 );
685
686 self.register(Some(super::LUA_MATHLIB_NAME), &MATH_LIB[..])?;
687 self.push_number(LUAU_PI)?;
688 self.raw_set_field(-2, "pi")?;
689 self.push_number(f64::INFINITY)?;
690 self.raw_set_field(-2, "huge")?;
691 self.push_number(f64::NAN)?;
692 self.raw_set_field(-2, "nan")?;
693 self.push_number(LUAU_E)?;
694 self.raw_set_field(-2, "e")?;
695 self.push_number(LUAU_PHI)?;
696 self.raw_set_field(-2, "phi")?;
697 self.push_number(LUAU_SQRT2)?;
698 self.raw_set_field(-2, "sqrt2")?;
699 self.push_number(LUAU_TAU)?;
700 self.raw_set_field(-2, "tau")?;
701 Ok(1)
702 }
703 }
704}