programming rust: finish chapter 2.
This commit is contained in:
parent
1f90323879
commit
c1d6c41510
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "gcd"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Kyle Isom <kyle@imap.cc>"]
|
||||||
|
|
||||||
|
[dependencies]
|
|
@ -0,0 +1,43 @@
|
||||||
|
use std::io::Write;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
fn gcd(mut n: u64, mut m: u64) -> u64 {
|
||||||
|
assert!(n != 0 && m != 0);
|
||||||
|
while m != 0 {
|
||||||
|
if m < n {
|
||||||
|
let t = m;
|
||||||
|
m = n;
|
||||||
|
n = t;
|
||||||
|
}
|
||||||
|
m = m % n;
|
||||||
|
}
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gcd() {
|
||||||
|
assert_eq!(gcd(14, 15), 1);
|
||||||
|
assert_eq!(gcd(2 * 3 * 5 * 11 * 17,
|
||||||
|
3 * 7 * 11 * 13 * 19),
|
||||||
|
3 * 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut numbers = Vec::new();
|
||||||
|
|
||||||
|
for arg in std::env::args().skip(1) {
|
||||||
|
numbers.push(u64::from_str(&arg).expect("error parsing argument as u64"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if numbers.len() == 0 {
|
||||||
|
writeln!(std::io::stderr(), "Usage: gcd NUMBER...").unwrap();
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut d = numbers[0];
|
||||||
|
for m in &numbers[1..] {
|
||||||
|
d = gcd(d, *m);
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("The greatest common divisor of {:?} is {}", numbers, d);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "iron-gcd"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Kyle Isom <kyle@imap.cc>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
iron = "0.5.1"
|
||||||
|
mime = "0.2.3"
|
||||||
|
router = "0.5.1"
|
||||||
|
urlencoded = "0.5.0"
|
|
@ -0,0 +1,96 @@
|
||||||
|
extern crate iron;
|
||||||
|
extern crate router;
|
||||||
|
extern crate urlencoded;
|
||||||
|
#[macro_use] extern crate mime;
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
use urlencoded::UrlEncodedBody;
|
||||||
|
|
||||||
|
use iron::prelude::*;
|
||||||
|
use iron::status;
|
||||||
|
use router::Router;
|
||||||
|
|
||||||
|
fn gcd(mut n: u64, mut m: u64) -> u64 {
|
||||||
|
assert!(n != 0 && m != 0);
|
||||||
|
while m != 0 {
|
||||||
|
if m < n {
|
||||||
|
let t = m;
|
||||||
|
m = n;
|
||||||
|
n = t;
|
||||||
|
}
|
||||||
|
m = m % n;
|
||||||
|
}
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut router = Router::new();
|
||||||
|
|
||||||
|
router.get("/", get_form, "root");
|
||||||
|
router.post("/gcd", post_gcd, "gcd");
|
||||||
|
|
||||||
|
println!("Serving on http://localhost:3000...");
|
||||||
|
Iron::new(router).http("localhost:3000").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_form(_request: &mut Request) -> IronResult<Response> {
|
||||||
|
let mut response = Response::new();
|
||||||
|
|
||||||
|
response.set_mut(status::Ok);
|
||||||
|
response.set_mut(mime!(Text/Html; Charset=Utf8));
|
||||||
|
response.set_mut(r#"
|
||||||
|
<title>GCD calculator</title>
|
||||||
|
<form action="/gcd" method="post">
|
||||||
|
<input type="text" name="n" />
|
||||||
|
<input type="text" name="n" />
|
||||||
|
<button type="submit">Compute GCD</button>
|
||||||
|
</form>
|
||||||
|
"#);
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_gcd(request: &mut Request) -> IronResult<Response> {
|
||||||
|
let mut response = Response::new();
|
||||||
|
|
||||||
|
let form_data = match request.get_ref::<UrlEncodedBody>() {
|
||||||
|
Err(e) => {
|
||||||
|
response.set_mut(status::BadRequest);
|
||||||
|
response.set_mut(format!("Error parsing form data: {:?}\n", e));
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
Ok(map) => map
|
||||||
|
};
|
||||||
|
|
||||||
|
let unparsed_numbers = match form_data.get("n") {
|
||||||
|
None => {
|
||||||
|
response.set_mut(status::BadRequest);
|
||||||
|
response.set_mut(format!("Form has no 'n' parameters!\n"));
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
Some(nums) => nums
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut numbers = Vec::new();
|
||||||
|
for unparsed in unparsed_numbers {
|
||||||
|
match u64::from_str(&unparsed) {
|
||||||
|
Err(_) => {
|
||||||
|
response.set_mut(status::BadRequest);
|
||||||
|
response.set_mut(format!("Value for 'n' parameter isn't a number {:?}\n", unparsed));
|
||||||
|
return Ok(response);
|
||||||
|
}
|
||||||
|
Ok(n) => { numbers.push(n); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut d = numbers[0];
|
||||||
|
for m in &numbers[1..] {
|
||||||
|
d = gcd(d, *m);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.set_mut(status::Ok);
|
||||||
|
response.set_mut(mime!(Text/Html; Charset=Utf8));
|
||||||
|
response.set_mut(format!("The GCD of {:?} is {}.\n", numbers, d));
|
||||||
|
|
||||||
|
Ok(response)
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "mandelbrot"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Kyle Isom <kyle@imap.cc>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
crossbeam = "0.2.8"
|
||||||
|
image = "0.13.0"
|
||||||
|
num = "0.1.27"
|
|
@ -0,0 +1,165 @@
|
||||||
|
extern crate crossbeam;
|
||||||
|
extern crate image;
|
||||||
|
extern crate num;
|
||||||
|
|
||||||
|
use image::png::PNGEncoder;
|
||||||
|
use image::ColorType;
|
||||||
|
use num::Complex;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
|
||||||
|
fn parse_pair<T: FromStr>(s: &str, separator: char) -> Option<(T, T)> {
|
||||||
|
match s.find(separator) {
|
||||||
|
None => None,
|
||||||
|
Some(index) => match (T::from_str(&s[..index]), T::from_str(&s[index + 1..])) {
|
||||||
|
(Ok(l), Ok(r)) => Some((l, r)),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_complex(s: &str) -> Option<Complex<f64>> {
|
||||||
|
match parse_pair(s, ',') {
|
||||||
|
Some((re, im)) => Some(Complex { re, im }),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_complex() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_complex("1.25,-0.0625"),
|
||||||
|
Some(Complex {
|
||||||
|
re: 1.25,
|
||||||
|
im: -0.0625
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(parse_complex(",-0.0625", None))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape_time(c: Complex<f64>, limit: u32) -> Option<u32> {
|
||||||
|
let mut z = Complex { re: 0.0, im: 0.0 };
|
||||||
|
for i in 0..limit {
|
||||||
|
z = z * z + c;
|
||||||
|
if z.norm_sqr() > 4.0 {
|
||||||
|
return Some(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pixel_to_point(
|
||||||
|
bounds: (usize, usize),
|
||||||
|
pixel: (usize, usize),
|
||||||
|
upper_left: Complex<f64>,
|
||||||
|
lower_right: Complex<f64>,
|
||||||
|
) -> Complex<f64> {
|
||||||
|
let (width, height) = (
|
||||||
|
lower_right.re - upper_left.re,
|
||||||
|
upper_left.im - lower_right.im,
|
||||||
|
);
|
||||||
|
Complex {
|
||||||
|
re: upper_left.re + pixel.0 as f64 * width / bounds.0 as f64,
|
||||||
|
// Subtraction because pixel.1 increases downward, but the imaginary
|
||||||
|
// component increases upward.
|
||||||
|
im: upper_left.im - pixel.1 as f64 * height / bounds.1 as f64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pixel_to_point() {
|
||||||
|
assert_eq!(
|
||||||
|
pixel_to_point(
|
||||||
|
(100, 100),
|
||||||
|
(25, 75),
|
||||||
|
Complex { re: -1.0, im: 1.0 },
|
||||||
|
Complex { re: 1.0, im: -1.0 }
|
||||||
|
),
|
||||||
|
Complex { re: -0.5, im: -0.5 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(
|
||||||
|
pixels: &mut [u8],
|
||||||
|
bounds: (usize, usize),
|
||||||
|
upper_left: Complex<f64>,
|
||||||
|
lower_right: Complex<f64>,
|
||||||
|
) {
|
||||||
|
assert!(pixels.len() == bounds.0 * bounds.1);
|
||||||
|
|
||||||
|
for row in 0..bounds.1 {
|
||||||
|
for column in 0..bounds.1 {
|
||||||
|
let point = pixel_to_point(bounds, (column, row), upper_left, lower_right);
|
||||||
|
pixels[row * bounds.0 + column] = match escape_time(point, 255) {
|
||||||
|
None => 0,
|
||||||
|
Some(count) => 255 - count as u8,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_image(
|
||||||
|
filename: &str,
|
||||||
|
pixels: &[u8],
|
||||||
|
bounds: (usize, usize),
|
||||||
|
) -> Result<(), std::io::Error> {
|
||||||
|
let output = File::create(filename)?;
|
||||||
|
|
||||||
|
let encoder = PNGEncoder::new(output);
|
||||||
|
encoder.encode(
|
||||||
|
&pixels,
|
||||||
|
bounds.0 as u32,
|
||||||
|
bounds.1 as u32,
|
||||||
|
ColorType::Gray(8),
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
|
||||||
|
if args.len() != 5 {
|
||||||
|
writeln!(
|
||||||
|
std::io::stderr(),
|
||||||
|
"Usage: mandelbrot FILE PIXELS UPPERLEFT LOWERRIGHT"
|
||||||
|
).unwrap();
|
||||||
|
writeln!(
|
||||||
|
std::io::stderr(),
|
||||||
|
"Example: {} mandel.png 1000x750 -1.20,0.35 -1,0.20",
|
||||||
|
args[0]
|
||||||
|
).unwrap();
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bounds: (usize, usize) = parse_pair(&args[2], 'x').expect("error parsing image dimensions");
|
||||||
|
let upper_left = parse_complex(&args[3]).expect("error parsing upper left corner point");
|
||||||
|
let lower_right = parse_complex(&args[4]).expect("error parsing lower right corner point");
|
||||||
|
|
||||||
|
let mut pixels = vec![0; bounds.0 * bounds.1];
|
||||||
|
|
||||||
|
let threads = 8;
|
||||||
|
let rows_per_band = bounds.1 / threads + 1;
|
||||||
|
{
|
||||||
|
let bands: Vec<&mut [u8]> = pixels.chunks_mut(rows_per_band * bounds.0).collect();
|
||||||
|
crossbeam::scope(|spawner| {
|
||||||
|
for (i, band) in bands.into_iter().enumerate() {
|
||||||
|
let top = rows_per_band * i;
|
||||||
|
let height = band.len() / bounds.0;
|
||||||
|
let band_bounds = (bounds.0, height);
|
||||||
|
let band_upper_left = pixel_to_point(bounds, (0, top), upper_left, lower_right);
|
||||||
|
let band_lower_right =
|
||||||
|
pixel_to_point(bounds, (bounds.0, top + height), upper_left, lower_right);
|
||||||
|
|
||||||
|
spawner.spawn(move || {
|
||||||
|
println!("spawned: {}", i);
|
||||||
|
render(band, band_bounds, band_upper_left, band_lower_right);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
write_image(&args[1], &pixels, bounds).expect("error writing PNG file");
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
Homework for chapter 5: Interlude: Process API
|
||||||
|
|
||||||
|
1. Write a program that calls fork(). Before calling fork(), have the main
|
||||||
|
program access a variable and set its value to something. What value is the
|
||||||
|
variable in the child process? What happens to the variable when both the child
|
||||||
|
and the parent change the value of x?
|
||||||
|
|
||||||
|
2. Write a program that opens a new file with open() and then calls fork() to
|
||||||
|
create a new process. Can both the child and the parent access the file
|
||||||
|
descriptor returned by open()? What happens when they are writing to the file
|
||||||
|
concurrently?
|
||||||
|
|
||||||
|
3. Write another program using fork(). The child process should print "hello",
|
||||||
|
the parent process should print "goodbye". You should try to ensure that the
|
||||||
|
child process always prints first; can you do this without calling wait() in
|
||||||
|
the parent?
|
||||||
|
|
||||||
|
4. Write a program that calls fork() and then calls some form of exec() to run
|
||||||
|
/bin/ls. See if you can try all the variants of exec(), including execl(),
|
||||||
|
execle(), execlp(), execv(), execvp(), and execevP(). Why do you think there
|
||||||
|
are so many variants of the same basic call?
|
||||||
|
|
||||||
|
5. Now write a program that uses wait() to wait for the child process to finish
|
||||||
|
in the parent. What does wait() return? What happens if you use wait() in the
|
||||||
|
child()?
|
||||||
|
|
||||||
|
6. Write a slight modification of the previous program, this time using
|
||||||
|
waitpid() instead of wait(). When would waitpid() be useful?
|
||||||
|
|
||||||
|
7. Write a program that creates a child process, and then in the child close
|
||||||
|
standard output. What happens if the child calls printf() to print some output
|
||||||
|
after closing the descriptor?
|
||||||
|
|
||||||
|
8. Write a program that creates two children, and connects the standard output
|
||||||
|
of one to the standard input of the other using the pipe() system call.
|
Loading…
Reference in New Issue