Create most naive starting solution (#22)

This commit is contained in:
Patrick Stevens
2023-06-04 16:19:07 +01:00
committed by GitHub
parent 9ce3a98890
commit 8b5213e262
11 changed files with 274 additions and 159 deletions

102
Cargo.lock generated
View File

@@ -126,7 +126,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.14",
"syn",
]
[[package]]
@@ -143,22 +143,22 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "const_panic"
version = "0.2.7"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58baae561b85ca19b3122a9ddd35c8ec40c3bcd14fe89921824eae73f7baffbf"
checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b"
dependencies = [
"konst_kernel",
"typewit",
]
[[package]]
name = "derive_arbitrary"
version = "1.3.0"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cdeb9ec472d588e539a818b2dee436825730da08ad0017c4b1a17676bdc8b7"
checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"syn",
]
[[package]]
@@ -212,6 +212,17 @@ dependencies = [
"serde",
]
[[package]]
name = "haversine-app"
version = "0.1.0"
dependencies = [
"byteorder",
"clap",
"haversine",
"json",
"utf8-read",
]
[[package]]
name = "heck"
version = "0.4.1"
@@ -226,9 +237,9 @@ checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "io-lifetimes"
version = "1.0.10"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
@@ -257,23 +268,17 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
name = "json"
version = "0.1.0"
[[package]]
name = "konst_kernel"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7771682454392dfe62a909aba2c6efc6674e2ad0b678fbc33b154e2e1bd59b89"
[[package]]
name = "libc"
version = "0.2.141"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "linux-raw-sys"
version = "0.3.1"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "memchr"
@@ -299,9 +304,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.17.1"
version = "1.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
[[package]]
name = "ppv-lite86"
@@ -311,18 +316,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.56"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
@@ -359,9 +364,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.37.11"
version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77"
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [
"bitflags",
"errno",
@@ -394,7 +399,7 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.14",
"syn",
]
[[package]]
@@ -408,6 +413,14 @@ dependencies = [
"serde",
]
[[package]]
name = "sim-wrapper"
version = "0.1.0"
dependencies = [
"clap",
"sim_8086",
]
[[package]]
name = "sim_8086"
version = "0.1.0"
@@ -426,9 +439,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.109"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
dependencies = [
"proc-macro2",
"quote",
@@ -436,21 +449,22 @@ dependencies = [
]
[[package]]
name = "syn"
version = "2.0.14"
name = "typewit"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
checksum = "7c52b4cb7830f995903b2fcff3f523d21efc1c11f6c1596dd544b7925a64ff56"
[[package]]
name = "unicode-ident"
version = "1.0.8"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "utf8-read"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb566ac06e11c3f13872ebcb0e72ea5f97f0f65a16f45048d0adea21edc28018"
[[package]]
name = "utf8parse"
@@ -529,11 +543,3 @@ name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "wrapper"
version = "0.1.0"
dependencies = [
"clap",
"sim_8086",
]

View File

@@ -1,10 +1,11 @@
[workspace]
members = [
"sim_8086",
"wrapper",
"sim-wrapper",
"generator",
"haversine",
"json"
"json",
"haversine-app"
]
[profile.release]

View File

@@ -1,7 +1,8 @@
# Performance-Aware Programming coursework
Me running through [Performance-Aware Programming](https://www.computerenhance.com) in Rust.
There are *certainly* bugs in the code; I mostly just bashed it out without thinking very hard, and without testing it except by using Casey's provided tests.
There are *certainly* bugs in the 8086 simulator code; I mostly just bashed it out without thinking very hard, and without testing it except by using Casey's provided tests.
It does, however, conform to all Casey's provided test cases except the "completionist decode" one.
## Licensing

View File

@@ -23,7 +23,7 @@
crate2nix,
...
}: let
name = "wrapper";
name = "sim-wrapper";
in
utils.lib.eachDefaultSystem
(

View File

@@ -7,7 +7,7 @@ use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use std::fmt::{Display, Formatter};
use std::fs::File;
use std::io::{BufWriter, Read, Write};
use std::io::{BufWriter, Write};
use std::str::FromStr;
#[derive(Clone, Debug)]
@@ -71,10 +71,10 @@ fn write_answer(data: &HaversineData, binary_filename: &str) {
let output_file = File::create(binary_filename).unwrap();
let mut writer = BufWriter::new(output_file);
let mut expected_sum: f64 = 0.0;
let mut expected_average: f64 = 0.0;
let mut buf = [0u8; 8];
for point in &data.pairs {
for (count, point) in data.pairs.iter().enumerate() {
let distance = distance::naive(point, earth::RADIUS);
LittleEndian::write_f64(&mut buf, distance);
@@ -83,12 +83,14 @@ fn write_answer(data: &HaversineData, binary_filename: &str) {
panic!("Failed to write everything")
}
expected_sum += distance;
expected_average = ((1.0 - (1.0 / (count as f64 + 1.0))) * expected_average)
+ (distance / (count as f64 + 1.0));
}
println!("Expected sum: {}", expected_sum);
// sic!
println!("Expected sum: {}", expected_average);
LittleEndian::write_f64(&mut buf, expected_sum);
LittleEndian::write_f64(&mut buf, expected_average);
let written = writer.write(&buf).unwrap();
if written < buf.len() {
panic!("Failed to write everything")
@@ -97,64 +99,38 @@ fn write_answer(data: &HaversineData, binary_filename: &str) {
writer.flush().unwrap();
}
#[allow(dead_code)]
fn read_answer(binary_filename: &str) -> (Vec<f64>, f64) {
let mut file = File::create(binary_filename).unwrap();
let file_size = file.metadata().unwrap().len();
if file_size % 8 != 0 {
panic!(
"Malformed input file of size {} is not a multiple of 8",
file_size
)
}
let num_floats = file_size / 8;
assert_ne!(num_floats, 0, "Empty file");
let num_bytes = num_floats - 1;
let mut data = Vec::with_capacity(num_bytes as usize);
let mut buf = [0u8, 8];
for _ in 0..num_floats {
let bytes_read = file.read(&mut buf).unwrap();
if bytes_read < 8 {
panic!("Not enough bytes read")
}
data.push(LittleEndian::read_f64(&buf));
}
let bytes_read = file.read(&mut buf).unwrap();
if bytes_read < 8 {
panic!("Not enough bytes read")
}
(data, LittleEndian::read_f64(&buf))
}
fn point_within(
point: &CoordinatePair,
cluster_centre_x: f64,
cluster_centre_y: f64,
cluster_radius: f64,
) -> bool {
let mut distance = CoordinatePair {
x0: point.x0,
y0: point.y0,
x1: cluster_centre_x,
y1: cluster_centre_y,
};
if distance::naive(&distance, 1.0) > cluster_radius {
return false;
}
distance.x0 = point.x1;
distance.y0 = point.y1;
distance::naive(&distance, 1.0) <= cluster_radius
}
const CLUSTER_COUNT: usize = 20;
/// Returns the smaller one then the larger one.
fn sample_two<R, D>(range: D, rng: &mut R) -> (f64, f64)
where
R: Rng,
D: Distribution<f64>,
{
let x1 = range.sample(rng);
let x2 = range.sample(rng);
if x1 < x2 {
(x1, x2)
} else {
(x2, x1)
}
}
fn sample_point<R, D1, D2>(rng: &mut R, x_range: D1, y_range: D2) -> CoordinatePair
where
R: Rng,
D1: Distribution<f64>,
D2: Distribution<f64>,
{
CoordinatePair {
x0: x_range.sample(rng),
y0: y_range.sample(rng),
x1: x_range.sample(rng),
y1: y_range.sample(rng),
}
}
fn main() {
let args = Args::parse();
println!("Method: {}", args.algorithm);
@@ -170,13 +146,7 @@ fn main() {
match args.algorithm {
SelectionAlgorithm::Uniform => {
for _ in 0usize..args.count {
let point = CoordinatePair {
x0: x_range.sample(&mut rng),
y0: y_range.sample(&mut rng),
x1: x_range.sample(&mut rng),
y1: y_range.sample(&mut rng),
};
let point = sample_point(&mut rng, x_range, y_range);
v.push(point);
}
}
@@ -189,29 +159,14 @@ fn main() {
}
for _ in 0..CLUSTER_COUNT {
// Uniformly sampling over lat/long pairs is dumb and biased towards the poles,
// but :shrug: we'll just bias it away a bit by hand.
let cluster_centre_x = x_range.sample(&mut rng);
// Bias a bit away from the pole by shrinking it
let cluster_centre_y = y_range.sample(&mut rng) * 0.9;
let cluster_radius = rng.gen_range(0.1..0.4);
let (min_x, max_x) = sample_two(x_range, &mut rng);
let (min_y, max_y) = sample_two(y_range, &mut rng);
let x_range = rand::distributions::Uniform::from(min_x..max_x);
let y_range = rand::distributions::Uniform::from(min_y..max_y);
for _ in 0..args.count / CLUSTER_COUNT {
let mut point = CoordinatePair {
x0: 0.0,
y0: 0.0,
x1: 0.0,
// too big to ever be in the right place, so a suitable uninitialised value
y1: 1000.0,
};
while !point_within(&point, cluster_centre_x, cluster_centre_y, cluster_radius)
{
point.x0 = x_range.sample(&mut rng);
point.y0 = y_range.sample(&mut rng);
point.x1 = x_range.sample(&mut rng);
point.y1 = y_range.sample(&mut rng);
}
let point = sample_point(&mut rng, x_range, y_range);
v.push(point);
}
}

13
haversine-app/Cargo.toml Normal file
View File

@@ -0,0 +1,13 @@
[package]
name = "haversine-app"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.3.1", features = [ "derive" ] }
haversine = { path = "../haversine" }
json = { path = "../json" }
byteorder = "1.4.3"
utf8-read = "0.4.0"

99
haversine-app/src/main.rs Normal file
View File

@@ -0,0 +1,99 @@
use byteorder::{ByteOrder, LittleEndian};
use clap::Parser;
use haversine::haversine::CoordinatePair;
use haversine::{distance, earth};
use json::json_object::JsonValue;
use std::fs::File;
use std::io::{BufReader, Read};
use utf8_read::Reader;
#[derive(Parser, Debug)]
struct Args {
#[arg(value_name = "INPUT_JSON", required = true)]
input_json: String,
#[arg(value_name = "EXPECTED_F64", required = true)]
expected: String,
}
fn read_answer(binary_filename: &str) -> (Vec<f64>, f64) {
let mut file = File::open(binary_filename).unwrap();
let file_size = file.metadata().unwrap().len();
if file_size % 8 != 0 {
panic!(
"Malformed input file of size {} is not a multiple of 8",
file_size
)
}
let num_floats = file_size / 8;
assert_ne!(num_floats, 0, "Empty file");
let num_floats = num_floats - 1;
let mut data = Vec::with_capacity(num_floats as usize);
let mut buf = [0u8; 8];
for _ in 0..num_floats {
let mut bytes_read = 0;
while bytes_read < 8 {
let new_bytes = file.read(&mut buf[bytes_read..]).unwrap();
if new_bytes == 0 {
panic!("Reached end of file");
}
bytes_read += new_bytes;
}
data.push(LittleEndian::read_f64(&buf));
}
let bytes_read = file.read(&mut buf).unwrap();
if bytes_read < 8 {
panic!("Not enough bytes read")
}
(data, LittleEndian::read_f64(&buf))
}
fn read_json(json_filename: &str) -> Vec<CoordinatePair> {
let file = File::open(json_filename).unwrap();
let reader = BufReader::new(file);
let mut decoder = Reader::new(reader);
match JsonValue::parse(&mut decoder.into_iter().map(|x| x.unwrap())).unwrap() {
(JsonValue::Object(o), None) => {
let a = o.values.get("pairs").unwrap().as_array();
a.iter()
.map(|o| {
let o = o.as_object();
CoordinatePair {
x0: o.get("x0").unwrap().as_number(),
y0: o.get("y0").unwrap().as_number(),
x1: o.get("x1").unwrap().as_number(),
y1: o.get("y1").unwrap().as_number(),
}
})
.collect()
}
other => {
panic!("Expected a JSON object, got: {:?}", other)
}
}
}
fn haversine_sum(v: &[CoordinatePair]) -> f64 {
let mut answer = 0.0_f64;
for pair in v {
answer += distance::naive(pair, earth::RADIUS);
}
answer / (v.len() as f64)
}
fn main() {
let args = Args::parse();
let input = read_json(&args.input_json);
let (_expected_values, expected_sum) = read_answer(&args.expected);
let actual_sum = haversine_sum(&input);
println!("Pair count: {}", input.len());
println!("Haversine sum: {}", actual_sum);
println!("Validation:");
println!("Reference sum: {}", expected_sum);
println!("Difference: {}", f64::abs(expected_sum - actual_sum));
}

1
json/src/example.json Normal file
View File

@@ -0,0 +1 @@
{"pairs":[{"x0":22.477076634891404,"y0":1.96712597438826,"x1":64.3045066154957,"y1":-11.011501834694641},{"x0":-29.95327542455132,"y0":-16.17420677740482,"x1":-95.33727035262879,"y1":5.442724293569228},{"x0":176.9535218941732,"y0":-23.62633427891349,"x1":98.49393113007599,"y1":66.11720571650596},{"x0":84.10721569912283,"y0":-31.531857354540108,"x1":19.118449375424888,"y1":28.47444316116797},{"x0":-104.74547266971662,"y0":-44.6905221231317,"x1":-41.98223217669326,"y1":-0.8361930004999607},{"x0":-97.57786242745013,"y0":-7.555696001498205,"x1":-110.18578839345321,"y1":-45.82142186105271},{"x0":8.489029425696025,"y0":-19.661609891086545,"x1":32.6916569844426,"y1":-19.23122886972898},{"x0":-82.73889716232972,"y0":-3.4950910477402566,"x1":-61.08583995567845,"y1":-7.395573056242222},{"x0":-2.348717663153586,"y0":-18.777604624531843,"x1":145.1231894370418,"y1":-16.272245920530942},{"x0":-118.95284692848952,"y0":-38.240441628627764,"x1":-95.75644279084551,"y1":38.35221318654381},{"x0":143.2114792465152,"y0":7.237874212954941,"x1":143.10890111372015,"y1":3.0510375574127986},{"x0":-117.16427058103187,"y0":-19.328188969376868,"x1":-109.1457976040175,"y1":-42.77835599605936},{"x0":-76.39830609710457,"y0":58.00061923926861,"x1":-46.41076441053505,"y1":54.73539164184136},{"x0":-99.69233221681043,"y0":-16.03252235163597,"x1":-95.66539569227095,"y1":-18.54085071711185},{"x0":4.005958252988464,"y0":-15.08359144353581,"x1":2.868307430731619,"y1":35.954144546196765},{"x0":-105.22171195417796,"y0":44.664012522319325,"x1":-115.66595736862281,"y1":5.8518107376345085},{"x0":-45.56180924277177,"y0":5.185604480193316,"x1":-37.5672842051612,"y1":-6.723875513314969},{"x0":-81.211983455444,"y0":82.43176574614236,"x1":-89.23338448851845,"y1":82.57791072309365},{"x0":9.362158900743665,"y0":19.55481006439635,"x1":-43.13010547577467,"y1":8.122399345136628},{"x0":-7.901960753291205,"y0":-12.360772954348086,"x1":-7.911976757629197,"y1":-21.793505300605254}]}

View File

@@ -143,23 +143,26 @@ where
}
};
if current != '0' {
if ('1'..='9').contains(&current) {
integer_part = (current as u8 - b'0') as f64;
loop {
current = match iter.next() {
None => return Ok((negative * integer_part, None)),
Some(v) => v,
};
if current.is_ascii_digit() {
integer_part = integer_part * 10.0 + ((current as u8 - b'0') as f64)
} else {
break;
}
}
} else {
return Err(JsonNumberParseError::NonDigit(current));
if current == '0' {
current = match iter.next() {
Some(v) => v,
None => return Ok((negative * 0.0, None)),
}
} else if ('1'..='9').contains(&current) {
integer_part = (current as u8 - b'0') as f64;
loop {
current = match iter.next() {
None => return Ok((negative * integer_part, None)),
Some(v) => v,
};
if current.is_ascii_digit() {
integer_part = integer_part * 10.0 + ((current as u8 - b'0') as f64)
} else {
break;
}
}
} else {
return Err(JsonNumberParseError::NonDigit(current));
}
// Parse fraction
@@ -347,6 +350,27 @@ where
}
impl JsonValue {
pub fn as_number(&self) -> f64 {
match self {
JsonValue::Number(f) => *f,
_ => panic!("Expected a number, got: {:?}", self),
}
}
pub fn as_object(&self) -> &HashMap<String, JsonValue> {
match self {
JsonValue::Object(o) => &o.values,
_ => panic!("Expected an object, got: {:?}", self),
}
}
pub fn as_array(&self) -> &Vec<JsonValue> {
match self {
JsonValue::Array(a) => a,
_ => panic!("Expected an array, got: {:?}", self),
}
}
/// Consumes the JSON value and leaves the iterator sitting on the first non-whitespace
/// character after the JSON value. Returns that non-whitespace character in the Ok case.
pub(crate) fn parse_iter<I>(
@@ -861,4 +885,19 @@ mod test {
}
}
}
#[test]
fn negative_number() {
let (parsed, remaining) = JsonValue::parse(&mut "-0.8".chars()).unwrap();
assert_eq!(remaining, None);
assert_eq!(parsed.as_number(), -0.8);
}
#[test]
fn haversine_example() {
let s = include_str!("example.json");
let parsed = parse_object(&s);
let o = parsed.values.get("pairs").unwrap().as_array();
assert_eq!(o.len(), 20);
}
}

View File

@@ -1,5 +1,5 @@
[package]
name = "wrapper"
name = "sim-wrapper"
version = "0.1.0"
edition = "2021"