initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "scott"
|
||||
version = "0.1.0"
|
||||
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "scott"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
39
data
Normal file
39
data
Normal file
@@ -0,0 +1,39 @@
|
||||
RAx = 0
|
||||
RAy + REy = L
|
||||
REy = 1/2 L
|
||||
RAx - AB - AK cos a = 0
|
||||
RAy - AK sin a = 0
|
||||
AB + BK cos d - BH cos d - BC = 0
|
||||
BK sin d + BH sin d = -1/3 L
|
||||
BC + CJ cos d - CG cos d - CD = 0
|
||||
CJ sin d + CG sin d = -1/3 L
|
||||
CD + ID cos d - DF cos d - DE = 0
|
||||
ID sin d + DF sin d = -1/3 L
|
||||
DE + FE cos a = 0
|
||||
REy - FE sin a = 0
|
||||
DF cos d + GF cos b - EF cos a = 0
|
||||
DF sin d - GF sin b + EF sin a = 0
|
||||
CG cos d + GH cos c - FG cos b = 0
|
||||
CG sin d - GH sin c + FG sin b = 0
|
||||
IH + BH cos d - HG cos c = 0
|
||||
# BH sin d + HG sin c = 0
|
||||
# IJ cos c - ID cos d - IH = 0
|
||||
JI sin c + ID sin d = 0
|
||||
# JK cos b - CJ cos d - IJ cos c = 0
|
||||
AK cos a - BK cos d - JK cos b = 0
|
||||
|
||||
order: RAx RAy REy AB BC CD DE EF FG GH HI IJ JK KA BK CJ DI BH CG DF
|
||||
|
||||
map: cos a = Ca
|
||||
map: cos b = Cb
|
||||
map: cos c = Cc
|
||||
map: cos d = Cd
|
||||
map: sin a = Sa
|
||||
map: sin b = Sb
|
||||
map: sin c = Sc
|
||||
map: sin d = Sd
|
||||
|
||||
map: -1/3 L = -(1/3)*load
|
||||
map: 1/2 L = (1/2)*load
|
||||
map: L = load
|
||||
map: matrix = Coef
|
||||
74
src/main.rs
Normal file
74
src/main.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use crate::parse::System;
|
||||
|
||||
mod parse;
|
||||
|
||||
fn main() {
|
||||
let stdin = std::io::stdin();
|
||||
let system = System::parse(stdin.lines());
|
||||
print_matlab(&system);
|
||||
}
|
||||
|
||||
fn print_matlab(system: &System) {
|
||||
let mut matrix = Vec::new();
|
||||
let mut col_len = vec![0; system.order.len()];
|
||||
for eq in &system.equations {
|
||||
for term in &eq.terms {
|
||||
if !system.order.contains(&term.var) {
|
||||
panic!("unknown var {}", term.var);
|
||||
}
|
||||
}
|
||||
let coeffs: Vec<_> = system
|
||||
.order
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, var)| {
|
||||
let res = eq
|
||||
.terms
|
||||
.iter()
|
||||
.find_map(|t| if t.var == *var { Some(t.str()) } else { None })
|
||||
.unwrap_or("0".to_string());
|
||||
col_len[i] = col_len[i].max(res.len());
|
||||
res
|
||||
})
|
||||
.collect();
|
||||
matrix.push(coeffs);
|
||||
}
|
||||
|
||||
let col_len = col_len.into_iter().max().unwrap();
|
||||
|
||||
print!("% ");
|
||||
print_row(&system.order, col_len);
|
||||
println!();
|
||||
|
||||
println!("{} = [", system.name("matrix"));
|
||||
for row in &matrix {
|
||||
print!(" ");
|
||||
print_row(row, col_len);
|
||||
println!(";");
|
||||
}
|
||||
println!("];");
|
||||
println!();
|
||||
|
||||
print!("{} = [", system.name("constants"));
|
||||
for (i, eq) in system.equations.iter().enumerate() {
|
||||
print!("{};", eq.result);
|
||||
if i != system.equations.len() - 1 {
|
||||
print!(" ");
|
||||
}
|
||||
}
|
||||
println!("];");
|
||||
}
|
||||
|
||||
fn print_row(row: &[String], col_len: usize) {
|
||||
let print_col = |str| {
|
||||
print!("{str:>0$}", col_len);
|
||||
};
|
||||
let mut iter = row.iter();
|
||||
if let Some(first) = iter.next() {
|
||||
print_col(first);
|
||||
}
|
||||
for col in iter {
|
||||
print!(" ");
|
||||
print_col(col);
|
||||
}
|
||||
}
|
||||
221
src/parse.rs
Normal file
221
src/parse.rs
Normal file
@@ -0,0 +1,221 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
iter::Peekable,
|
||||
str::Chars,
|
||||
};
|
||||
|
||||
const TRIG_FNS: [&str; 3] = ["cos", "sin", "tan"];
|
||||
const ORDER_PREFIX: &str = "order: ";
|
||||
const MAP_PREFIX: &str = "map: ";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Term {
|
||||
pub sign: Sign,
|
||||
pub var: String,
|
||||
pub coeff: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Sign {
|
||||
Pos,
|
||||
Neg,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Equation {
|
||||
pub terms: Vec<Term>,
|
||||
pub result: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct System {
|
||||
pub equations: Vec<Equation>,
|
||||
pub order: Vec<String>,
|
||||
pub map: Map,
|
||||
}
|
||||
|
||||
enum Token {
|
||||
Sign(Sign),
|
||||
Var(String),
|
||||
Coeff(String),
|
||||
Equals,
|
||||
}
|
||||
|
||||
impl System {
|
||||
pub fn parse(lines: impl Iterator<Item = Result<String, std::io::Error>>) -> Self {
|
||||
let mut equations = Vec::new();
|
||||
let mut order = Vec::new();
|
||||
let mut map = HashMap::new();
|
||||
for line in lines {
|
||||
let line = line.unwrap();
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
if let Some(line) = line.strip_prefix(ORDER_PREFIX) {
|
||||
order = parse_order(line);
|
||||
continue;
|
||||
}
|
||||
if let Some(line) = line.strip_prefix(MAP_PREFIX) {
|
||||
let (from, to) = line.split_once('=').expect("bad map");
|
||||
map.insert(from.trim().to_string(), to.trim().to_string());
|
||||
continue;
|
||||
}
|
||||
let eq = parse_equation(line);
|
||||
equations.push(eq);
|
||||
}
|
||||
for eq in &mut equations {
|
||||
for term in &mut eq.terms {
|
||||
if let Some(to) = map.get(&term.coeff) {
|
||||
term.coeff = to.clone();
|
||||
}
|
||||
}
|
||||
if let Some(to) = map.get(&eq.result) {
|
||||
eq.result = to.clone();
|
||||
}
|
||||
}
|
||||
if order.is_empty() {
|
||||
let mut vars = HashSet::new();
|
||||
for eq in &equations {
|
||||
for term in &eq.terms {
|
||||
vars.insert(term.var.clone());
|
||||
}
|
||||
}
|
||||
order = vars.into_iter().collect();
|
||||
order.sort();
|
||||
}
|
||||
Self {
|
||||
equations,
|
||||
order,
|
||||
map,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self, name: &'static str) -> &str {
|
||||
self.map.get(name).map_or(name, |v| v)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_order(line: &str) -> Vec<String> {
|
||||
line.split(' ')
|
||||
.map(|s| {
|
||||
let mut s = s.to_string();
|
||||
reorder(&mut s);
|
||||
s
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub type Map = HashMap<String, String>;
|
||||
|
||||
pub fn parse_equation(line: &str) -> Equation {
|
||||
let mut iter = line.chars().peekable();
|
||||
let mut terms = Vec::new();
|
||||
let mut next = next_token(&mut iter).unwrap();
|
||||
loop {
|
||||
let term = parse_term(&mut next, &mut iter).expect("no equals");
|
||||
terms.push(term);
|
||||
if let Token::Equals = next {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let mut result: String = iter.collect();
|
||||
result = result.trim().to_string();
|
||||
Equation { terms, result }
|
||||
}
|
||||
|
||||
fn parse_term(next: &mut Token, iter: &mut Peekable<Chars>) -> Option<Term> {
|
||||
let mut sign = Sign::Pos;
|
||||
if let Token::Equals = next {
|
||||
panic!("unexpected equals");
|
||||
}
|
||||
if let &mut Token::Sign(s) = next {
|
||||
sign = s;
|
||||
*next = next_token(iter)?;
|
||||
}
|
||||
let mut coeff: Option<String> = None;
|
||||
let mut var = None;
|
||||
loop {
|
||||
match next {
|
||||
Token::Sign(..) => break,
|
||||
Token::Equals => break,
|
||||
Token::Var(v) => {
|
||||
if var.is_some() {
|
||||
panic!("two vars in term")
|
||||
} else {
|
||||
var = Some(v.clone());
|
||||
}
|
||||
}
|
||||
Token::Coeff(c) => {
|
||||
if let Some(coeff) = &mut coeff {
|
||||
*coeff += c;
|
||||
} else {
|
||||
coeff = Some(c.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
*next = next_token(iter)?;
|
||||
}
|
||||
Some(Term {
|
||||
sign,
|
||||
var: var.expect("no var in term"),
|
||||
coeff: coeff.unwrap_or("1".to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
fn next_token(iter: &mut Peekable<Chars>) -> Option<Token> {
|
||||
let mut next = *iter.peek()?;
|
||||
while next.is_whitespace() {
|
||||
iter.next();
|
||||
next = *iter.peek()?;
|
||||
}
|
||||
'outer: {
|
||||
let token = match next {
|
||||
'+' => Token::Sign(Sign::Pos),
|
||||
'-' => Token::Sign(Sign::Neg),
|
||||
'=' => Token::Equals,
|
||||
_ => break 'outer,
|
||||
};
|
||||
iter.next();
|
||||
return Some(token);
|
||||
}
|
||||
let mut word = next_word(iter);
|
||||
if TRIG_FNS.iter().any(|f| word == *f) {
|
||||
word.push(' ');
|
||||
word += &next_word(iter);
|
||||
}
|
||||
Some(if word.chars().next()?.is_ascii_uppercase() {
|
||||
Token::Var(word)
|
||||
} else {
|
||||
Token::Coeff(word)
|
||||
})
|
||||
}
|
||||
|
||||
fn next_word(iter: &mut Peekable<Chars>) -> String {
|
||||
let mut word = String::new();
|
||||
while let Some(&c) = iter.peek()
|
||||
&& !(c.is_whitespace() || c == '-' || c == '+')
|
||||
{
|
||||
word.push(c);
|
||||
iter.next();
|
||||
}
|
||||
reorder(&mut word);
|
||||
word
|
||||
}
|
||||
|
||||
pub fn reorder(word: &mut String) {
|
||||
if word.chars().all(|c| c.is_ascii_uppercase()) {
|
||||
let mut chars: Vec<char> = word.chars().collect();
|
||||
chars.sort();
|
||||
*word = chars.into_iter().collect();
|
||||
}
|
||||
}
|
||||
|
||||
impl Term {
|
||||
pub fn str(&self) -> String {
|
||||
match self.sign {
|
||||
Sign::Pos => self.coeff.clone(),
|
||||
Sign::Neg => "-".to_string() + &self.coeff,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user