Last active
August 29, 2015 14:06
-
-
Save abonander/902235199a3770806ccd to your computer and use it in GitHub Desktop.
Triangle Solver in Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::num::FloatMath; | |
struct Tri { | |
sides: [Option<f32>, ..3], | |
angles: [Option<f32>, ..3], | |
ambiguous: Option<Box<Tri>>, | |
} | |
impl Tri { | |
fn from_vec(vals: &[Option<f32>]) -> Tri { | |
Tri { | |
sides: [vals[0], vals[1], vals[2]], | |
angles: [vals[3], vals[4], vals[5]], | |
ambiguous: None, | |
} | |
} | |
fn side(&mut self, i: int) -> &mut Option<f32> { | |
let len = self.sides.len(); | |
self.sides.get_mut(wrap(i, len)).unwrap() | |
} | |
fn ang(&mut self, i: int) -> &mut Option<f32> { | |
let len = self.angles.len(); | |
self.angles.get_mut(wrap(i, len)).unwrap() | |
} | |
fn solved(&self) -> bool { | |
count_knowns(&self.angles) == 3 && count_knowns(&self.sides) == 3 | |
} | |
fn has_all_angles(&mut self) -> bool { | |
match self.angles { | |
[Some(_), Some(_), Some(_)] => return true, | |
[Some(a), Some(b), None] => self.angles[2] = Some(180f32 - (a + b)), | |
[Some(a), None, Some(c)] => self.angles[1] = Some(180f32 - (a + c)), | |
[None, Some(b), Some(c)] => self.angles[0] = Some(180f32 - (b + c)), | |
_ => return false, | |
} | |
self.has_all_angles() | |
} | |
fn solve_angles(&mut self) -> bool { | |
self.has_all_angles() || | |
(match self.angles { | |
[None, _, _] => self.solve_angle(0), | |
[_, None, _] => self.solve_angle(1), | |
[_, _, None] => self.solve_angle(2), | |
_ => false, | |
}) && self.solve_angles() | |
} | |
fn solve_angle(&mut self, i: int) -> bool { | |
match (*self.side(i), *self.side(i - 1), *self.side(i + 1), *self.ang(i - 1), *self.ang(i + 1)) { | |
// Side - Side - Side | |
(Some(opp), Some(left), Some(right), _, _) => *self.ang(i) = Some(sss(opp, left, right)), | |
// Side - Side - Angle with the given angle opposite the short side (ambiguous case) | |
(Some(opp), Some(left), _, Some(_), _) if opp < left => self.ambiguous_case(i, -1), | |
// Ambiguous SSA right variant | |
(Some(opp) , _, Some(right), _, Some(_)) if opp < right => self.ambiguous_case(i, 1), | |
// Side - Side - Angle with the given angle opposite the long side (not ambiguous) | |
(Some(opp), Some(left), _, Some(ang_left), _) => *self.ang(i) = Some(no_ambi_ssa(opp, left, ang_left)), | |
// Non-ambiguous SSA right variant | |
(Some(opp), _, Some(right), _, Some(ang_right)) => *self.ang(i) = Some(no_ambi_ssa(opp, right, ang_right)), | |
// Cannot solve angles with info given | |
_ => return false, | |
} | |
true | |
} | |
fn has_all_sides(&self) -> bool { | |
match self.sides { | |
[Some(_), Some(_), Some(_)] => true, | |
_ => false, | |
} | |
} | |
fn solve_sides(&mut self) -> bool { | |
self.has_all_sides() || | |
(match self.sides { | |
[None, _, _] => self.solve_side(0), | |
[_, None, _] => self.solve_side(1), | |
[_, _, None] => self.solve_side(2), | |
_ => false, | |
}) && self.solve_sides() | |
} | |
fn solve_side(&mut self, i: int) -> bool { | |
match (*self.side(i - 1), *self.side(i + 1), *self.ang(i), *self.ang(i - 1), *self.ang(i + 1)) { | |
// Side - Angle - Side | |
(Some(left), Some(right), Some(opp), _, _) => | |
*self.side(i) = Some(sas(left, right, opp)), | |
// Angle - Angle - Side left | |
(Some(left), _, _, Some(ang_left), Some(ang_right)) => | |
*self.side(i) = Some(asa(left, ang_left, ang_right)), | |
// Angle -Angle - Side right | |
(_, Some(right), _, Some(ang_left), Some(ang_right)) => | |
*self.side(i) = Some(asa(right, ang_right, ang_left)), | |
// Angle - Side - Angle would have been worked out with solve_angles() | |
// All other cases are unsolvable | |
_ => return false, | |
} | |
true | |
} | |
fn ambiguous_case(&mut self, i: int, which: int) { | |
let ambi_ang = no_ambi_ssa( | |
self.side(i + which).unwrap(), | |
self.side(i).unwrap(), | |
self.ang(i + which).unwrap() | |
); | |
let other_ambi_ang = 180.0 - ambi_ang - self.ang(i + which).unwrap(); | |
let mut other = Tri { | |
sides: [self.sides[0], self.sides[1], self.sides[2]], | |
angles: [self.angles[0], self.sides[1], self.sides[2]], | |
ambiguous: None, | |
}; | |
*self.ang(i) = Some(ambi_ang); | |
*other.ang(i) = Some(other_ambi_ang); | |
self.ambiguous = Some(box other); | |
} | |
fn solve(&mut self) -> bool { | |
if self.solved() { return true; } | |
let solved = if !self.solve_angles() { | |
self.solve_sides() && self.solve_angles() | |
} else { | |
self.solve_sides() | |
}; | |
let ambi_solved = self.ambiguous.as_mut().map(|ambi| ambi.solve()).unwrap_or(true); | |
solved && ambi_solved | |
} | |
fn print(&self) { | |
println!("{:.2f} {:.2f} {:.2f} {:.2f} {:.2f} {:.2f}", | |
self.sides[0].unwrap_or(0.0), | |
self.sides[1].unwrap_or(0.0), | |
self.sides[2].unwrap_or(0.0), | |
self.angles[0].unwrap_or(0.0), | |
self.angles[1].unwrap_or(0.0), | |
self.angles[2].unwrap_or(0.0), | |
); | |
self.ambiguous.as_ref().map(|tri| { println!("Ambiguous case:"); tri.print() }); | |
} | |
} | |
#[inline] | |
fn deg_sin(x: f32) -> f32 { x.to_radians().sin() } | |
#[inline] | |
fn deg_cos(x: f32) -> f32 { x.to_radians().cos() } | |
#[inline] | |
fn deg_asin(x: f32) -> f32 { x.asin().to_degrees() } | |
#[inline] | |
fn deg_acos(x: f32) -> f32 { x.acos().to_degrees() } | |
fn sss(opp: f32, adj_1: f32, adj_2: f32) -> f32 { | |
// A = acos((b^2 + c^2 - a^2) / 2bc) | |
deg_acos((sq(adj_1) + sq(adj_2) - sq(opp)) / (adj_1 * adj_2 * 2.0)) | |
} | |
fn sas(left: f32, right: f32, opp: f32) -> f32 { | |
// a = sqrt(b^2 + c^2 - 2*b*c*cos(A)) | |
(sq(left) + sq(right) - left * right * deg_cos(opp) * 2.0).sqrt() | |
} | |
fn asa(s1: f32, a1: f32, a2: f32) -> f32 { | |
s1 * deg_sin(a2) / deg_sin(a1) | |
} | |
fn no_ambi_ssa(side_1: f32, side_2: f32, ang_side_1: f32) -> f32 { | |
// asin(c / b * sin(A)) | |
deg_asin((side_2 / side_1) * deg_sin(ang_side_1)) | |
} | |
#[inline] | |
fn sq(x: f32) -> f32 { x.powi(2) } | |
#[inline] | |
fn wrap(i: int, max: uint) -> uint { (max as int + i) as uint % max } | |
fn count_knowns(vec: &[Option<f32>]) -> uint { | |
vec.iter().filter(|x| x.is_some()).count() | |
} | |
fn main() { | |
println!("Enter triangles in the following format: a b c A B C Using underscores for unknowns."); | |
print!("> "); | |
for line in std::io::stdio::stdin().lines() { | |
let mut tri = parse_tri(line.unwrap().as_slice()); | |
if !tri.solve() { | |
println!("Could not solve triangle!"); | |
} else { | |
tri.print() | |
} | |
print!("> "); | |
} | |
} | |
fn parse_tri(line: &str) -> Tri { | |
let vals: Vec<Option<f32>> = line.words().map(|st| from_str::<f32>(st)).collect(); | |
if vals.len() != 6{ | |
println!("Missing or extra values in triangle!"); | |
} | |
Tri::from_vec(vals.as_slice()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment