diff --git a/members/revsqrt/tests/features/book/revsqrt.feature b/members/revsqrt/tests/features/book/revsqrt.feature index af08e5d..c44302e 100644 --- a/members/revsqrt/tests/features/book/revsqrt.feature +++ b/members/revsqrt/tests/features/book/revsqrt.feature @@ -1,6 +1,122 @@ -Feature: inverted square root feature +Feature: inverted square root calculation - Scenario: If we calculate the inverted square root of a number using fast inverted square root, it's about the same as if we calculate it normally + Scenario: Calculate fast inverted sqrt Given a number - When We calculate the the inverted square root of a number using fast inverted square root - Then The result is about the same as if we calculate it normally + When we calculate the inverted square root of it using the fast inverted square root algorithm + Then the result is about the same as if we calculate it normally + + Scenario: Calculate regular inverted sqrt + Given a number + When we calculate the inverted square root of it normally + Then the result can be calculated + + Scenario: Can the fast inverted sqrt be calculated? + Given a number + When we calculate the inverted square root of it using the fast inverted square root algorithm + Then the result can be calculated + + Scenario: Can the regular inverted sqrt be calculated? + Given a number + When we calculate the inverted square root of it normally + Then the result can be calculated + + Scenario: Calculate fast inverted sqrt with specific numbers + Given the number n + | n | + | 1 | + | 1.1 | + | 100 | + | 1337 | + | 123.45678900 | + | 1337.1337 | + When we calculate the inverted square root of it using the fast inverted square root algorithm + Then the result is about the same as m + | m | + | 1 | + | 0.9534625892455922 | + | 0.1 | + | 0.02734854943722097 | + | 0.0900000004095 | + | 0.027347182112297627 | + + Scenario: Calculate regular inverted sqrt with specific numbers + Given the number n + | n | + | 1 | + | 1.1 | + | 100 | + | 1337 | + | 123.45678900 | + | 1337.1337 | + When we calculate the inverted square root of it normally + Then the result is m + | m | + | 1 | + | 0.9534625892455922 | + | 0.1 | + | 0.02734854943722097 | + | 0.0900000004095 | + | 0.027347182112297627 | + + Scenario: Some numbers are about the same (0) + Given the number n + | n | + | 1 | + | 1.0001 | + | 0.999 | + | 0.9999999999 | + Then they are about the same + + Scenario: Some numbers are about the same (1) + Given the number n + | n | + | 10 | + | 10.0001 | + | 9.997 | + | 10.025 | + Then they are about the same + + Scenario: Some numbers are about the same (-3) + Given the number n + | n | + | -1000 | + | -1000.1 | + | -1001.1 | + Then they are about the same + + Scenario: Some numbers are about the same (3) + Given the number n + | n | + | -1000 | + | -1000.1 | + | -1001.1 | + Then they are about the same + + Scenario: Some numbers are about the same (7) + Given the number n + | n | + | 10000000 | + | 10000000 | + | 10000300 | + | 10000000.1 | + | 10000001.1 | + Then they are about the same + + Scenario: Some numbers are not about the same (1) + Given the number n + | n | + | 2 | + | -2 | + | 0 | + | 20 | + | 20000 | + Then they are not about the same + + Scenario: Some numbers are not about the same (7) + Given the number n + | n | + | 10000000 | + | 10001000 | + | 0 | + | 20000001.1 | + Then they are not about the same diff --git a/members/revsqrt/tests/revsqrt.rs b/members/revsqrt/tests/revsqrt.rs index e14872e..2dfad6d 100644 --- a/members/revsqrt/tests/revsqrt.rs +++ b/members/revsqrt/tests/revsqrt.rs @@ -1,31 +1,133 @@ -use cucumber::{given, then, when, World}; +use std::iter::zip; + +use cucumber::{gherkin::Step, given, then, when, World}; use rand; #[derive(Debug, Default, World)] pub struct NumWorld { - number: f32, - result: f32, + numbers: Vec<(f32, f32)>, } // is n about the same as m? +// This is actually not so easy! How do you measure "about same"ness? +// Also, it is not transitive, as 1 ≈ 1.1 ≈ 1.2 ≈ 1.3 ≈ ... ≈ 2 ≈ ... ≈ 3 ≈ ... ≈ infinity, that's +// a thought of me at least? +#[inline] fn about_same(n: f32, m: f32) -> bool { - (n - m) * 1000f32 < 1f32 + dbg!((n, m)); + dbg!((n - m).abs()); + dbg!(calc_gate(n, m)); + dbg!((n - m).abs() < calc_gate(n, m)); + (n - m).abs() <= calc_gate(n, m) } -// Steps are defined with `given`, `when` and `then` attributes. -#[given(regex = r"^a number$")] -async fn hungry_cat(world: &mut NumWorld) { - world.number = rand::random(); +#[inline] +fn calc_gate(n: f32, m: f32) -> f32 { + 0.01 + ((n.abs().sqrt().min(m.abs().sqrt())).abs() / 10f32) } -#[when("We calculate the the inverted square root of a number using fast inverted square root")] +#[given(regex = r"the number n")] +async fn give_specific_number(world: &mut NumWorld, step: &Step) { + if let Some(table) = step.table.as_ref() { + for row in table.rows.iter().skip(1) { + // NOTE: skip header + let n = row[0].parse::().unwrap(); + world.numbers.push((n, f32::NAN)); + } + } +} + +#[given("a number")] +async fn give_rand_number(world: &mut NumWorld) { + world.numbers.push(rand::random()); +} + +#[when("we calculate the inverted square root of it using the fast inverted square root algorithm")] async fn calc_fast_inv_sqrt(world: &mut NumWorld) { - world.result = revsqrt::fast_inverse_sqrt(world.number); + for pair in &mut world.numbers { + pair.1 = revsqrt::fast_inverse_sqrt(pair.0) + } } -#[then("The result is about the same as if we calculate it normally")] +#[when("we calculate the inverted square root of it normally")] +async fn calc_reg_inv_sqrt(world: &mut NumWorld) { + for pair in &mut world.numbers { + pair.1 = revsqrt::regular_inverse_sqrt(pair.0) + } +} + +#[then("the result is about the same as if we calculate it normally")] async fn comp_result_with_normal(world: &mut NumWorld) { - assert!(about_same(world.number, world.result)); + for pair in &mut world.numbers { + assert!(about_same(pair.1, revsqrt::regular_inverse_sqrt(pair.0))); + } +} + +#[then("the result can be calculated")] +async fn can_be_calculated(world: &mut NumWorld) { + for pair in &mut world.numbers { + assert!(!pair.0.is_nan()); + assert!(!pair.1.is_nan()); + assert!(pair.0.is_finite()); + assert!(pair.1.is_finite()); + } +} + +#[then(regex = r"the result is m")] +async fn result_is(world: &mut NumWorld, step: &Step) { + if let Some(table) = step.table.as_ref() { + for (row, i) in zip(table.rows.iter().skip(1), 0..table.rows.len() - 1) { + // NOTE: skip header + let m = row[0].parse::().unwrap(); + assert_eq!(world.numbers[i].1, m); + } + } +} + +#[then(regex = r"the result is about the same as m")] +async fn result_is_about(world: &mut NumWorld, step: &Step) { + if let Some(table) = step.table.as_ref() { + for (row, i) in zip(table.rows.iter().skip(1), 0..table.rows.len() - 1) { + // NOTE: skip header + let m = row[0].parse::().unwrap(); + assert!( + about_same(world.numbers[i].1, m), + "{} and {} are not about the same!", + world.numbers[i].1, + m + ); + } + } +} + +#[then("they are about the same")] +async fn they_are_about_the_same(world: &mut NumWorld) { + let mut still_same = true; + let mut last_num = world.numbers[0].0; + for tup in &world.numbers { + still_same &= about_same(tup.0, last_num); + assert!( + still_same, + "{} and {} are not about the same! (gate: {})", + tup.0, last_num, calc_gate(tup.0, last_num) + ); + last_num = tup.0; + } +} + +#[then("they are not about the same")] +async fn they_are_not_about_the_same(world: &mut NumWorld) { + let mut found_a_same = false; + let mut last_num = f32::NAN; + for tup in &world.numbers { + found_a_same |= about_same(tup.0, last_num); + assert!( + !found_a_same, + "{} and {} are about the same! (gate: {})", + tup.0, last_num, calc_gate(tup.0, last_num) + ); + last_num = tup.0; + } } #[tokio::main]