Решение на (Floating) Points and Vectors от Александър Дойков

Обратно към всички решения

Към профила на Александър Дойков

Резултати

  • 11 точки от тестове
  • 1 бонус точка
  • 12 точки общо
  • 8 успешни тест(а)
  • 7 неуспешни тест(а)

Код

use std::f64;
use std::ops::Add;
use std::ops::BitXor;
use std::ops::Mul;
use std::ops::Sub;
#[derive(Debug, Clone, Copy)]
pub struct Point {
x: f64,
y: f64,
z: f64,
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Point { x, y, z }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
x: f64,
y: f64,
z: f64,
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Vector { x, y, z }
}
pub fn len(self) -> f64 {
(self * self).sqrt()
}
}
impl Add<Vector> for Point {
type Output = Point;
fn add(self, vector: Vector) -> Point {
Point {
x: self.x + vector.x,
y: self.y + vector.y,
z: self.z + vector.z,
}
}
}
impl Add for Vector {
type Output = Vector;
fn add(self, vector: Vector) -> Vector {
Vector {
x: self.x + vector.x,
y: self.y + vector.y,
z: self.z + vector.z,
}
}
}
pub fn nearly_equal(a: f64, b: f64) -> bool {
let abs_a = a.abs();
let abs_b = b.abs();
let diff = (a - b).abs();
if a == b {
// Handle infinities.
true
} else if a == 0.0 || b == 0.0 || diff < f64::MIN_POSITIVE {
// One of a or b is zero (or both are extremely close to it,) use absolute error.
diff < (f64::EPSILON * f64::MIN_POSITIVE)
} else {
// Use relative error.
(diff / f64::min(abs_a + abs_b, f64::MAX)) < f64::EPSILON
}
}

Ще ти дам бонус точка, задето си се опитал да имплементираш относително сравнение, но това не сработва съвсем. С останалите поправки, премахването на * f64::MIN_POSITIVE от втория клон минава тестовете. Реално, умножението на две числа по-малки от 1 намалява абсолютната стойност на числата, така че тази проверка е доста по-стриктна от просто сравнение с EPSILON.

Забавното е, че diff < f64::MIN_POSITIVE не мисля че би върнало истина, освен ако diff не е точно 0. Иначе как ще имаш float, който е по-малък от минималното положително число, което се репрезентира в f64? :)

impl PartialEq for Point {
fn eq(&self, other: &Point) -> bool {
nearly_equal(self.x, other.x)
&& nearly_equal(self.y, other.y)
&& nearly_equal(self.z, other.z)
}
}
impl PartialEq for Vector {
fn eq(&self, other: &Vector) -> bool {
nearly_equal(self.x, other.x)
&& nearly_equal(self.y, other.y)
&& nearly_equal(self.z, other.z)
}
}
impl Sub for Point {
type Output = Vector;
fn sub(self, other: Point) -> Vector {
Vector {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
impl Mul<Vector> for f64 {
type Output = Vector;
fn mul(self, other: Vector) -> Vector {
Vector {
x: self * other.x,
y: self * other.y,
z: self * other.z,
}
}
}
impl Mul for Vector {
type Output = f64;
fn mul(self, other: Vector) -> Self::Output {
self.x * other.x + self.y * other.y + self.z * other.z
}
}
impl BitXor for Vector {
type Output = Vector;
fn bitxor(self, other: Vector) -> Self::Output {
Vector {
x: self.y * other.z - self.z * other.y,
y: self.z * other.x - self.x * other.z,
z: self.x * other.y - self.y * other.x,
}
}
}
#[derive(Debug)]
pub struct Line {
vector: Vector,
point: Point,
}
impl Line {
/// Конструиране на линия през две точки, които минават през нея. Две различни точки са
/// достатъчни, за да дефинират еднозначно линия.
///
/// Можете да получите точка и вектор, като извадите едната от другата точка.
///
/// Ако точките са една и съща, очакваме да върнете None.
///
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1 == p2 {
None
} else {
Some(Line {
vector: Vector::new(p2.x - p1.x, p2.y - p1.y, p2.z - p2.z),

Още един малшанс, p2.z - p2.z вероятно е трябвало да бъде p2.z - p1.z :). Още по-жалко е, че просто можеше да напишеш p2 - p1, понеже имаш имплементирано вадене на точки (затова го имаше в условието).

point: p1,
})
}
}
/// Конструиране на линия през точка за начало, и вектор, който определя посоката. Стига
/// вектора да е ненулев, това е достатъчно, за да дефинира еднозначно линия.
///
/// Може да получите две точки, като съберете дадената с вектора.
///
/// Ако вектора е нулев, очакваме да върнете None.
///
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v.x == 0f64 && v.y == 0f64 && v.z == 0f64 {
None
} else {
Some(Line {
vector: v,
point: p,
})
}
}
}
impl Line {
pub fn distance(&self, target: Point) -> f64 {
let v = Vector::new(
self.point.x - target.x,
self.point.y - target.y,
self.point.z - target.z,
);
let cross_product = v ^ self.vector;
println!("{:?}", self.vector);
cross_product.len() / cross_product.len()
}
}
impl PartialEq for Line {
fn eq(&self, other: &Line) -> bool {
if self.point == other.point && self.vector ^ other.vector == Vector::new(0.0, 0.0, 0.0) {
true
} else {
if self.distance(other.point) != 0.0 || other.distance(self.point) != 0.0 {
false
} else {
self.vector ^ other.vector == Vector::new(0.0, 0.0, 0.0)
}
}
}

Първото условие има някакъв смисъл, но е доста специфично -- Двете точки не е нужно да бъдат една и съща, така че рядко би се случило то да върне истина.

Второто условие е малко странно. Разстоянието на self до other.point и това на other до self.point може да са равни на 0, но това да е просто защото point е едно и също. Тук има и проблема, че сравняваш директно с 0, вместо да използваш nearly_equal. Ей това би ти свършило работа:

fn eq(&self, other: &Line) -> bool {
    nearly_equal(self.distance(other.point), 0.0) && nearly_equal(self.distance(other.point + other.vector), 0.0)
}
}
fn main() {
let v1 = Vector::new(1.0, 1.0, 1.0);
let v2 = Vector::new(2.0, 2.0, 2.0);
let p1 = Point::new(0.1, 0.1, 0.1);
let p2 = Point::new(2.0, 2.0, 2.0);
println!(
"{}",
nearly_equal((p1 + v2).x, Vector::new(2.1, 2.1, 2.1).x)
);
assert!(v1 == v1);
assert!(p1 == p1);
assert!(v1 != v2);
assert!(p1 != p2);
assert!(p1.x == 1.0);
assert_eq!(p1 + v1, p2);
assert_eq!(p2 - p1, v1);
assert_eq!(v1 + v1, v2);
assert_eq!(2.0 * v1, v2);
assert_eq!(v1 * v2, 6.0);
assert_eq!(v1 ^ v2, Vector::new(0.0, 0.0, 0.0));
assert_eq!(
Line::from_pv(Point::new(0.0, 0.0, 0.0), Vector::new(1.0, 1.0, 1.0)),
Line::from_pv(Point::new(0.0, 0.0, 0.0), Vector::new(2.0, 2.0, 2.0))
);
}

Лог от изпълнението

Compiling solution v0.1.0 (/tmp/d20190123-22631-375pn6/solution)
warning: function is never used: `main`
   --> src/lib.rs:208:1
    |
208 | fn main() {
    | ^^^^^^^^^
    |
    = note: #[warn(dead_code)] on by default

    Finished dev [unoptimized + debuginfo] target(s) in 5.31s
     Running target/debug/deps/solution-2e785d603b538f71

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/solution_test-29808948fb50ed3a

running 15 tests
test solution_test::test_equailty_symmetry ... FAILED
test solution_test::test_equality_basic ... ok
test solution_test::test_equality_floating ... FAILED
test solution_test::test_line_constructors ... ok
test solution_test::test_line_equality_by_points ... FAILED
test solution_test::test_line_equality_by_points_and_vectors ... FAILED
test solution_test::test_line_equality_by_vectors ... FAILED
test solution_test::test_line_validity ... ok
test solution_test::test_number_by_vector ... ok
test solution_test::test_number_vector_multiplication_with_precision ... FAILED
test solution_test::test_point_distance ... FAILED
test solution_test::test_points_minus_points ... ok
test solution_test::test_points_plus_vectors ... ok
test solution_test::test_vector_by_vector ... ok
test solution_test::test_vector_by_vector_cross ... ok

failures:

---- solution_test::test_equailty_symmetry stdout ----
thread 'solution_test::test_equailty_symmetry' panicked at 'assertion failed: `(left == right)`
  left: `Point { x: 0.0, y: 0.0, z: 0.0 }`,
 right: `Point { x: 0.0000000000000000002220446049250313, y: 0.0, z: 0.0 }`', tests/solution_test.rs:63:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

---- solution_test::test_equality_floating stdout ----
thread 'solution_test::test_equality_floating' panicked at 'assertion failed: `(left == right)`
  left: `Point { x: 0.0000000000000000002220446049250313, y: 0.0, z: 0.0 }`,
 right: `Point { x: 0.0, y: 0.0, z: 0.0 }`', tests/solution_test.rs:42:5

---- solution_test::test_line_equality_by_points stdout ----
Vector { x: 0.1, y: 0.1, z: 0.0 }
thread 'solution_test::test_line_equality_by_points' panicked at 'assertion failed: `(left == right)`
  left: `Some(Line { vector: Vector { x: 0.1, y: 0.1, z: 0.0 }, point: Point { x: 0.0, y: 0.0, z: 0.0 } })`,
 right: `Some(Line { vector: Vector { x: -0.1, y: -0.1, z: 0.0 }, point: Point { x: 0.1, y: 0.1, z: 0.1 } })`', tests/solution_test.rs:206:5

---- solution_test::test_line_equality_by_points_and_vectors stdout ----
Vector { x: 0.3, y: -0.1, z: 0.0 }
thread 'solution_test::test_line_equality_by_points_and_vectors' panicked at 'assertion failed: `(left == right)`
  left: `Some(Line { vector: Vector { x: 0.3, y: -0.1, z: 0.0 }, point: Point { x: 0.0, y: 0.0, z: 0.0 } })`,
 right: `Some(Line { vector: Vector { x: 0.3, y: -0.1, z: 0.25 }, point: Point { x: 0.0, y: 0.0, z: 0.0 } })`', tests/solution_test.rs:243:5

---- solution_test::test_line_equality_by_vectors stdout ----
Vector { x: 0.1, y: -0.2, z: 0.5 }
thread 'solution_test::test_line_equality_by_vectors' panicked at 'assertion failed: `(left == right)`
  left: `Some(Line { vector: Vector { x: 0.1, y: -0.2, z: 0.5 }, point: Point { x: 0.0, y: 0.4, z: 0.0 } })`,
 right: `Some(Line { vector: Vector { x: 0.1, y: -0.2, z: 0.5 }, point: Point { x: 0.0000000000000000002220446049250313, y: 0.4, z: 0.0000000000000000002220446049250313 } })`', tests/solution_test.rs:230:5

---- solution_test::test_number_vector_multiplication_with_precision stdout ----
thread 'solution_test::test_number_vector_multiplication_with_precision' panicked at 'assertion failed: `(left == right)`
  left: `Vector { x: 0.0000000000000000002220446049250313, y: 0.0000000000000000002220446049250313, z: 0.0000000000000000002220446049250313 }`,
 right: `Vector { x: 0.0, y: 0.0, z: 0.0 }`', tests/solution_test.rs:145:5

---- solution_test::test_point_distance stdout ----
Vector { x: -1.0, y: 1.0, z: 0.0 }
thread 'solution_test::test_point_distance' panicked at 'assertion failed: (line.distance(p1) - 2.0f64.sqrt() / 2.0).abs() < EPS * 1000.0', tests/solution_test.rs:183:5


failures:
    solution_test::test_equailty_symmetry
    solution_test::test_equality_floating
    solution_test::test_line_equality_by_points
    solution_test::test_line_equality_by_points_and_vectors
    solution_test::test_line_equality_by_vectors
    solution_test::test_number_vector_multiplication_with_precision
    solution_test::test_point_distance

test result: FAILED. 8 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--test solution_test'

История (2 версии и 6 коментара)

Александър качи първо решение на 25.11.2018 10:57 (преди почти 7 години)

Александър качи решение на 25.11.2018 19:16 (преди почти 7 години)

use std::f64;
use std::ops::Add;
use std::ops::BitXor;
use std::ops::Mul;
use std::ops::Sub;
#[derive(Debug, Clone, Copy)]
pub struct Point {
x: f64,
y: f64,
z: f64,
}
impl Point {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Point { x, y, z }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Vector {
x: f64,
y: f64,
z: f64,
}
impl Vector {
pub fn new(x: f64, y: f64, z: f64) -> Self {
Vector { x, y, z }
}
pub fn len(self) -> f64 {
(self * self).sqrt()
}
}
impl Add<Vector> for Point {
type Output = Point;
fn add(self, vector: Vector) -> Point {
Point {
x: self.x + vector.x,
y: self.y + vector.y,
z: self.z + vector.z,
}
}
}
impl Add for Vector {
type Output = Vector;
fn add(self, vector: Vector) -> Vector {
Vector {
x: self.x + vector.x,
y: self.y + vector.y,
z: self.z + vector.z,
}
}
}
pub fn nearly_equal(a: f64, b: f64) -> bool {
let abs_a = a.abs();
let abs_b = b.abs();
let diff = (a - b).abs();
if a == b {
// Handle infinities.
true
} else if a == 0.0 || b == 0.0 || diff < f64::MIN_POSITIVE {
// One of a or b is zero (or both are extremely close to it,) use absolute error.
diff < (f64::EPSILON * f64::MIN_POSITIVE)
} else {
// Use relative error.
(diff / f64::min(abs_a + abs_b, f64::MAX)) < f64::EPSILON
}
}

Ще ти дам бонус точка, задето си се опитал да имплементираш относително сравнение, но това не сработва съвсем. С останалите поправки, премахването на * f64::MIN_POSITIVE от втория клон минава тестовете. Реално, умножението на две числа по-малки от 1 намалява абсолютната стойност на числата, така че тази проверка е доста по-стриктна от просто сравнение с EPSILON.

Забавното е, че diff < f64::MIN_POSITIVE не мисля че би върнало истина, освен ако diff не е точно 0. Иначе как ще имаш float, който е по-малък от минималното положително число, което се репрезентира в f64? :)

impl PartialEq for Point {
fn eq(&self, other: &Point) -> bool {
nearly_equal(self.x, other.x)
&& nearly_equal(self.y, other.y)
&& nearly_equal(self.z, other.z)
}
}
impl PartialEq for Vector {
fn eq(&self, other: &Vector) -> bool {
nearly_equal(self.x, other.x)
&& nearly_equal(self.y, other.y)
&& nearly_equal(self.z, other.z)
}
}
impl Sub for Point {
type Output = Vector;
fn sub(self, other: Point) -> Vector {
Vector {
x: self.x - other.x,
y: self.y - other.y,
z: self.z - other.z,
}
}
}
impl Mul<Vector> for f64 {
type Output = Vector;
fn mul(self, other: Vector) -> Vector {
Vector {
x: self * other.x,
y: self * other.y,
z: self * other.z,
}
}
}
impl Mul for Vector {
type Output = f64;
fn mul(self, other: Vector) -> Self::Output {
self.x * other.x + self.y * other.y + self.z * other.z
}
}
impl BitXor for Vector {
type Output = Vector;
fn bitxor(self, other: Vector) -> Self::Output {
Vector {
x: self.y * other.z - self.z * other.y,
y: self.z * other.x - self.x * other.z,
z: self.x * other.y - self.y * other.x,
}
}
}
#[derive(Debug)]
pub struct Line {
vector: Vector,
point: Point,
}
impl Line {
/// Конструиране на линия през две точки, които минават през нея. Две различни точки са
/// достатъчни, за да дефинират еднозначно линия.
///
/// Можете да получите точка и вектор, като извадите едната от другата точка.
///
/// Ако точките са една и съща, очакваме да върнете None.
///
pub fn from_pp(p1: Point, p2: Point) -> Option<Self> {
if p1 == p2 {
None
} else {
Some(Line {
vector: Vector::new(p2.x - p1.x, p2.y - p1.y, p2.z - p2.z),

Още един малшанс, p2.z - p2.z вероятно е трябвало да бъде p2.z - p1.z :). Още по-жалко е, че просто можеше да напишеш p2 - p1, понеже имаш имплементирано вадене на точки (затова го имаше в условието).

point: p1,
})
}
}
/// Конструиране на линия през точка за начало, и вектор, който определя посоката. Стига
/// вектора да е ненулев, това е достатъчно, за да дефинира еднозначно линия.
///
/// Може да получите две точки, като съберете дадената с вектора.
///
/// Ако вектора е нулев, очакваме да върнете None.
///
pub fn from_pv(p: Point, v: Vector) -> Option<Self> {
if v.x == 0f64 && v.y == 0f64 && v.z == 0f64 {
None
} else {
Some(Line {
vector: v,
point: p,
})
}
}
}
impl Line {
- pub fn distance(&self, point: &Point) -> f64 {
+ pub fn distance(&self, target: Point) -> f64 {
let v = Vector::new(
- self.point.x - point.x,
- self.point.y - point.y,
- self.point.z - point.z,
+ self.point.x - target.x,
+ self.point.y - target.y,
+ self.point.z - target.z,
);
let cross_product = v ^ self.vector;
println!("{:?}", self.vector);
cross_product.len() / cross_product.len()
}
}
impl PartialEq for Line {
fn eq(&self, other: &Line) -> bool {
if self.point == other.point && self.vector ^ other.vector == Vector::new(0.0, 0.0, 0.0) {
true
} else {
- if self.distance(&other.point) != 0.0 || other.distance(&self.point) != 0.0 {
+ if self.distance(other.point) != 0.0 || other.distance(self.point) != 0.0 {
false
} else {
self.vector ^ other.vector == Vector::new(0.0, 0.0, 0.0)
}
}
}

Първото условие има някакъв смисъл, но е доста специфично -- Двете точки не е нужно да бъдат една и съща, така че рядко би се случило то да върне истина.

Второто условие е малко странно. Разстоянието на self до other.point и това на other до self.point може да са равни на 0, но това да е просто защото point е едно и също. Тук има и проблема, че сравняваш директно с 0, вместо да използваш nearly_equal. Ей това би ти свършило работа:

fn eq(&self, other: &Line) -> bool {
    nearly_equal(self.distance(other.point), 0.0) && nearly_equal(self.distance(other.point + other.vector), 0.0)
}
}
fn main() {
let v1 = Vector::new(1.0, 1.0, 1.0);
let v2 = Vector::new(2.0, 2.0, 2.0);
let p1 = Point::new(0.1, 0.1, 0.1);
let p2 = Point::new(2.0, 2.0, 2.0);
println!(
"{}",
nearly_equal((p1 + v2).x, Vector::new(2.1, 2.1, 2.1).x)
);
assert!(v1 == v1);
assert!(p1 == p1);
assert!(v1 != v2);
assert!(p1 != p2);
assert!(p1.x == 1.0);
assert_eq!(p1 + v1, p2);
assert_eq!(p2 - p1, v1);
assert_eq!(v1 + v1, v2);
assert_eq!(2.0 * v1, v2);
assert_eq!(v1 * v2, 6.0);
assert_eq!(v1 ^ v2, Vector::new(0.0, 0.0, 0.0));
assert_eq!(
Line::from_pv(Point::new(0.0, 0.0, 0.0), Vector::new(1.0, 1.0, 1.0)),
Line::from_pv(Point::new(0.0, 0.0, 0.0), Vector::new(2.0, 2.0, 2.0))
);
}