rust-java-functional-progra.../src/stream_algos.rs

146 lines
4.5 KiB
Rust

use crate::geometry::{Point, Rectangle};
use std::collections::HashMap;
use std::ops::{Add, Mul};
impl Add<Point> for Rectangle {
type Output = Self;
fn add(self, rhs: Point) -> Self {
Rectangle::from_corner_width_height(self.top_left() + rhs, self.width(), self.height())
}
}
impl Mul<u32> for Rectangle {
type Output = Self;
fn mul(self, rhs: u32) -> Self {
Rectangle::from_corner_width_height(
self.top_left(),
self.width() * rhs,
self.height() * rhs,
)
}
}
/// Returns a stream of rectangles by translating (moving) each rectangle according to the given
/// distance vector.
pub fn translate<T>(rects: T, distance_vector: Point) -> impl Iterator<Item = Rectangle>
where
T: Iterator<Item = Rectangle>,
{
rects.map(move |r| r + distance_vector)
}
/// Returns a stream of rectangles by scaling each rectangle by a given amount.
pub fn scale<T>(rects: T, scale_factor: u32) -> impl Iterator<Item = Rectangle>
where
T: Iterator<Item = Rectangle>,
{
rects.map(move |r| r * scale_factor)
}
/// Returns a stream containing, in order, the bottom-left point of each input rectangle.
pub fn bottom_left_points<T>(rects: T) -> impl Iterator<Item = Point>
where
T: Iterator<Item = Rectangle>,
{
rects.map(|r| r.bottom_left())
}
/// Returns a stream containing all rectangles that intersect with the given rectangle.
pub fn get_all_intersecting<T>(rects: T, rectangle: &Rectangle) -> impl Iterator<Item = Rectangle>
where
T: Iterator<Item = Rectangle>,
{
// We need to satisfy the borrow checker here, make a copy.
let rectangle = *rectangle;
rects.filter(move |r| r.intersects(&rectangle))
}
/// Returns the largest area among the given rectangles
pub fn get_largest_area(rects: impl Iterator<Item = Rectangle>) -> Option<u32> {
rects.map(|r| r.area()).max()
}
/// Returns the largest height among the given rectangles
pub fn get_largest_height(rects: impl Iterator<Item = Rectangle>) -> Option<u32> {
rects.map(|r| r.height()).max()
}
/// Returns the sum of the areas of the rectangles
pub fn get_area_sum(rects: impl Iterator<Item = Rectangle>) -> u32 {
rects.map(|r| r.area()).sum()
}
/// Computes the sum of areas of all rectangles that intersect with the given rectangle.
pub fn get_area_sum_of_intersecting<T>(rects: T, rectangle: &Rectangle) -> u32
where
T: Iterator<Item = Rectangle>,
{
get_area_sum(get_all_intersecting(rects, rectangle))
}
/// Returns map from rectangle to computed area
pub fn get_area_map<T>(rects: T) -> HashMap<Rectangle, u32>
where
T: Iterator<Item = Rectangle>,
{
// The type of the collect() function is chosen based on the return type that it's meant to fit
rects.map(|r| (r, r.area())).collect()
}
pub fn intersect_all<T>(mut rects: T) -> Option<Rectangle>
where
T: Iterator<Item = Rectangle>,
{
// The question mark operator is equivalent to:
// let first = match rects.next() {
// None => return None,
// Some(x) => x,
// };
let first = rects.next()?;
fn intersect_helper(acc: Option<Rectangle>, rect: Rectangle) -> Option<Rectangle> {
match acc {
None => None,
Some(a) => a.intersection(&rect),
}
}
rects.fold(Some(first), intersect_helper)
}
#[cfg(test)]
mod tests {
use super::*;
const INCREASING_ENCLOSING: [Rectangle; 5] = [
Rectangle::at_origin_with_size(1, 1),
Rectangle::at_origin_with_size(2, 2),
Rectangle::at_origin_with_size(3, 3),
Rectangle::at_origin_with_size(4, 4),
Rectangle::at_origin_with_size(5, 5),
];
#[test]
fn test_intersect_all_1() {
let mut v = Vec::new();
v.extend(INCREASING_ENCLOSING.iter());
// Drain is a bit different to iter in that it releases ownership of each object,
// instead of just giving a reference.
assert_eq!(Some(INCREASING_ENCLOSING[0]), intersect_all(v.drain(0..)));
}
#[test]
fn test_intersect_all_2() {
// Deliberately inconsistent with above code to show a different way of doing things.
let mut various_rects = vec![
Rectangle::from_corner_width_height(Point::new(20, 110), 10, 10),
Rectangle::from_corner_width_height(Point::new(25, 115), 10, 10),
Rectangle::from_corner_width_height(Point::new(10, 100), 1000, 1000),
Rectangle::from_corner_width_height(Point::new(2010, 2100), 1, 1),
];
assert_eq!(None, intersect_all(various_rects.drain(0..)));
}
}