feat: first working version with parameters
This commit is contained in:
38
packages/utils/src/encoding.rs
Normal file
38
packages/utils/src/encoding.rs
Normal file
@ -0,0 +1,38 @@
|
||||
pub fn encode_float(f: f32) -> (i32, i32) {
|
||||
let bits = f.to_bits();
|
||||
let mantissa = (bits & 0x007FFFFF) as i32;
|
||||
let exponent = ((bits >> 23) & 0xFF) as i32;
|
||||
let sign = if f < 0.0 { 1 } else { 0 }; // Determine sign as 1 for negative, 0 for positive
|
||||
(mantissa | (sign << 23), exponent) // Include the sign bit in the mantissa
|
||||
}
|
||||
|
||||
pub fn decode_float(mantissa: i32, exponent: i32) -> f32 {
|
||||
let sign_bit = ((mantissa >> 23) & 1) as u32; // Extract the sign bit
|
||||
let mantissa_bits = (mantissa & 0x007FFFFF) as u32;
|
||||
let exponent_bits = (exponent as u32 & 0xFF) << 23;
|
||||
let bits = (sign_bit << 31) | exponent_bits | mantissa_bits; // Reconstruct all bits including sign
|
||||
f32::from_bits(bits)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[test]
|
||||
fn test_encode_decode() {
|
||||
let original_floats = [
|
||||
0.0, 1.0, -1.0, 123.456, -123.456, 1e-10, -1e10, f32::MAX, f32::MIN,
|
||||
];
|
||||
for &original in &original_floats {
|
||||
let (mantissa, exponent) = encode_float(original);
|
||||
let decoded = decode_float(mantissa, exponent);
|
||||
assert!(
|
||||
(decoded - original).abs() < 1e-6,
|
||||
"Mismatch: original {} vs decoded {}",
|
||||
original,
|
||||
decoded
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
78
packages/utils/src/helpers.rs
Normal file
78
packages/utils/src/helpers.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use serde_json::Value;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
pub fn unwrap_int(val: JsValue) -> i32 {
|
||||
if val.is_undefined() || val.is_null() {
|
||||
panic!("Value is undefined");
|
||||
}
|
||||
return val.as_f64().unwrap() as i32;
|
||||
}
|
||||
|
||||
pub fn unwrap_float(val: JsValue) -> f64 {
|
||||
if val.is_undefined() || val.is_null() {
|
||||
panic!("Value is undefined");
|
||||
}
|
||||
return val.as_f64().unwrap();
|
||||
}
|
||||
|
||||
pub fn unwrap_string(val: JsValue) -> String {
|
||||
if val.is_undefined() || val.is_null() {
|
||||
panic!("Value is undefined");
|
||||
}
|
||||
return val.as_string().unwrap();
|
||||
}
|
||||
|
||||
pub fn evaluate_parameters(val: JsValue) -> f64 {
|
||||
let str = unwrap_string(val);
|
||||
let v: Value = serde_json::from_str(&str).unwrap();
|
||||
let index = RefCell::new(0.0);
|
||||
return walk_json(&v, &index);
|
||||
}
|
||||
|
||||
fn walk_json(value: &Value, depth: &RefCell<f64>) -> f64 {
|
||||
*depth.borrow_mut() += 1.0;
|
||||
match value {
|
||||
// If it's an object, recursively walk through its fields
|
||||
Value::Object(obj) => {
|
||||
let obj_type = obj.get("__type").unwrap();
|
||||
if obj_type == "random" {
|
||||
let min = walk_json(obj.get("min").unwrap(), depth);
|
||||
let max = walk_json(obj.get("max").unwrap(), depth);
|
||||
let seed = (obj.get("seed").unwrap().as_f64().unwrap() + *depth.borrow() * 2000.0)
|
||||
/ 1000000.0;
|
||||
let range = max - min;
|
||||
let seed = seed % range;
|
||||
return seed - min;
|
||||
} else if obj_type == "math" {
|
||||
let a = walk_json(obj.get("a").unwrap(), depth);
|
||||
let b = walk_json(obj.get("b").unwrap(), depth);
|
||||
let op_type = obj.get("op_type").unwrap();
|
||||
if op_type == 0 {
|
||||
return a + b;
|
||||
} else if op_type == 1 {
|
||||
return a - b;
|
||||
} else if op_type == 2 {
|
||||
return a * b;
|
||||
} else if op_type == 3 {
|
||||
return a / b;
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
Value::Array(arr) => {
|
||||
for val in arr {
|
||||
walk_json(val, depth);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
Value::Number(num) => {
|
||||
return num.as_f64().unwrap();
|
||||
}
|
||||
// If it's a primitive value, print it
|
||||
_ => {
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
}
|
18
packages/utils/src/lib.rs
Normal file
18
packages/utils/src/lib.rs
Normal file
@ -0,0 +1,18 @@
|
||||
mod encoding;
|
||||
mod helpers;
|
||||
mod nodes;
|
||||
mod tree;
|
||||
pub use encoding::*;
|
||||
pub use helpers::*;
|
||||
pub use tree::*;
|
||||
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
18
packages/utils/src/nodes.rs
Normal file
18
packages/utils/src/nodes.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use crate::encoding;
|
||||
|
||||
pub fn math_node(args: &[i32]) -> (i32, i32) {
|
||||
let math_type = args[0];
|
||||
|
||||
let a = encoding::decode_float(args[1], args[2]);
|
||||
let b = encoding::decode_float(args[3], args[4]);
|
||||
|
||||
let result = match math_type {
|
||||
0 => a + b,
|
||||
1 => a - b,
|
||||
2 => a * b,
|
||||
3 => a / b,
|
||||
_ => 0.0,
|
||||
};
|
||||
|
||||
encoding::encode_float(result)
|
||||
}
|
169
packages/utils/src/tree.rs
Normal file
169
packages/utils/src/tree.rs
Normal file
@ -0,0 +1,169 @@
|
||||
pub fn get_args(args: &[i32]) -> Vec<&[i32]> {
|
||||
let mut idx: usize = 0;
|
||||
let mut depth = -1;
|
||||
|
||||
let mut next_bracket_index = 0;
|
||||
|
||||
let mut out_args: Vec<&[i32]> = Vec::new();
|
||||
|
||||
let length = args.len();
|
||||
|
||||
let mut arg_start_index = 2;
|
||||
|
||||
while idx < length {
|
||||
let is_bracket = idx == next_bracket_index;
|
||||
let value = args[idx];
|
||||
|
||||
// if we are at a bracket
|
||||
if is_bracket {
|
||||
// if we are at the end of the args
|
||||
if idx >= length - 1 {
|
||||
break;
|
||||
}
|
||||
|
||||
next_bracket_index = 1 + idx + args[idx + 1] as usize;
|
||||
|
||||
if value == 0 {
|
||||
depth += 1;
|
||||
} else if value == 1 {
|
||||
depth -= 1;
|
||||
}
|
||||
|
||||
if depth == 0 {
|
||||
if value == 1 {
|
||||
out_args.push(&args[arg_start_index..idx]);
|
||||
arg_start_index = idx + 2;
|
||||
}
|
||||
// skip over the bracket encoding
|
||||
idx += 2;
|
||||
} else {
|
||||
// skip to the next bracket if we are at depth > 0
|
||||
idx = next_bracket_index;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
out_args.push(&args[arg_start_index..=idx]);
|
||||
arg_start_index = idx + 1;
|
||||
|
||||
// this is at the end of args where normally multiple ] are encoded
|
||||
if depth < 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
out_args
|
||||
}
|
||||
|
||||
pub fn evaluate_node(input_args: &[i32]) -> (i32, i32) {
|
||||
let node_type = input_args[0];
|
||||
|
||||
match node_type {
|
||||
0 => crate::nodes::math_node(&input_args[1..]),
|
||||
_ => (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn evaluate_args(input_args: &[i32]) -> Vec<i32> {
|
||||
let args = get_args(input_args);
|
||||
|
||||
let mut resolved: Vec<i32> = Vec::new();
|
||||
|
||||
for arg in args {
|
||||
if arg.len() == 1 {
|
||||
resolved.push(arg[0]);
|
||||
} else if arg.len() == 4 && arg[0] == 0 && arg[1] == 3 {
|
||||
resolved.push(arg[2]);
|
||||
resolved.push(arg[3]);
|
||||
} else {
|
||||
let res = evaluate_args(arg);
|
||||
resolved.push(res[0]);
|
||||
resolved.push(res[1]);
|
||||
}
|
||||
}
|
||||
|
||||
println!("resolved: {:?}", resolved);
|
||||
|
||||
if resolved.len() > 1 {
|
||||
let res = evaluate_node(&resolved);
|
||||
vec![res.0, res.1]
|
||||
} else {
|
||||
resolved
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::encoding::decode_float;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_resursive_evaluation() {
|
||||
let input = vec![0, 3, 0, 0, 0, 7, 0, 2, 0, 128, 0, 128, 1, 6, 0, 128];
|
||||
// this is an encoded version of a math node that multiplies 2 * 2
|
||||
// and another math node that adds 2 to that result
|
||||
// the numbers are f32 floats encoded as two i32's
|
||||
|
||||
let result = evaluate_args(&input);
|
||||
let decoded = decode_float(result[0], result[1]);
|
||||
|
||||
assert_eq!(decoded, 6.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_args_input_a() {
|
||||
let input_a = vec![0, 4, 1, 2, 3, 0, 7, 1, 2, 4, 2, 4, 1, 1, 1, 1];
|
||||
// -> [1, 2, 3, [1, 2, 4, 2, 4]]
|
||||
|
||||
let args = get_args(&input_a);
|
||||
|
||||
assert_eq!(args.len(), 4);
|
||||
assert_eq!(args[0], [1]);
|
||||
assert_eq!(args[1], [2]);
|
||||
assert_eq!(args[2], [3]);
|
||||
assert_eq!(args[3], [0, 7, 1, 2, 4, 2, 4, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_args_input_b() {
|
||||
let input_b = vec![0, 3, 7, 1, 0, 4, 4, 2, 4, 1, 2, 2, 0, 3, 2, 3, 1, 1, 1, 1];
|
||||
// -> [1,[4,2,4], 2, [2,3]]
|
||||
|
||||
let args = get_args(&input_b);
|
||||
|
||||
assert_eq!(args.len(), 5);
|
||||
assert_eq!(args[0], [7]);
|
||||
assert_eq!(args[1], [1]);
|
||||
assert_eq!(args[2], [0, 4, 4, 2, 4]);
|
||||
assert_eq!(args[3], [2]);
|
||||
assert_eq!(args[4], [0, 3, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_args_nested_inputs() {
|
||||
let input = vec![
|
||||
0, 3, 0, 2, 0, 3, 0, 0, 0, 3, 7549747, 127, 1, 1, 0, 3, 0, 128, 1, 1, 1, 1, 0, 3, 0,
|
||||
128, 1, 1, 1, 1,
|
||||
];
|
||||
|
||||
// each math node has 4 numbers
|
||||
// 0 -> type of node (0 -> math node)
|
||||
// 1 -> op_type for math operation (0 -> add, 1 -> sub, 2 -> mul, 3 -> div)
|
||||
// 2 -> first number
|
||||
// 3 -> second number
|
||||
|
||||
let args = get_args(&input);
|
||||
|
||||
assert_eq!(args.len(), 4);
|
||||
assert_eq!(args[0], [0]);
|
||||
assert_eq!(args[1], [2]);
|
||||
assert_eq!(args[3], [0, 3, 0, 128]);
|
||||
|
||||
let nested_args = get_args(args[2]);
|
||||
|
||||
assert_eq!(nested_args.len(), 4);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user