<commit_before>//	MIT License
//
//  Copyright © 2017 Michael J Simms. All rights reserved.
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in all
//	copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//	SOFTWARE.

use std::collections::HashSet;
use std::collections::HashMap;

/// Each feature has a name and value.
pub struct Feature {
    name: String,
    value: u64,
}

impl Feature {
    pub fn new (name: &str, value: u64) -> Feature {
        Feature { name: name.to_string(), value: value }
    }

    pub fn set_name(&mut self, name: String) {
        self.name = name;
    }

    pub fn get_name(&self) -> String {
        self.name
    }

    pub fn set_value(&mut self, value: u64) {
        self.value = value;
    }

    pub fn get_value(&self) -> u64 {
        self.value
    }
}

pub type FeatureList = Vec<Feature>;
pub type Uint64Set = HashSet<u64>;
pub type FeatureNameToValuesMap<'a> = HashMap<&'a str, Uint64Set>;

/// This class represents a sample.
/// Each sample has a name and list of features.
pub struct Sample {
    name: String,
    features: FeatureList,
}

impl Sample {
    pub fn new (name: &str) -> Sample {
        Sample { name: name.to_string(), features: Sample::create_feature_list() }
    }

    fn create_feature_list() -> FeatureList {
        let mut v: FeatureList = vec![];
        v
    }

    pub fn add_features(&mut self, features: &mut FeatureList) {
        self.features.append(features);
    }

    pub fn add_feature(&mut self, feature: Feature) {
        self.features.push(feature);
    }

    pub fn features(&self) -> FeatureList {
        self.features
    }
}

pub type SampleList = Vec<Sample>;

/// Tree node, used internally.
struct Node {
    feature_name: String,
    left: NodeLink,
    right: NodeLink,
}

impl Node {
    pub fn new (feature_name: &str) -> Node {
        Node { feature_name: feature_name.to_string(), left: None, right: None }
    }

    fn get_feature_name(&self) -> String {
        self.feature_name
    }

	fn set_left_subtree(&mut self, subtree: NodeLink) {
		self.left = subtree;
	}

    fn get_left_subtree(&self) -> NodeLink {
        self.left
    }

	fn set_right_subtree(&mut self, subtree: NodeLink) {
		self.right = subtree;
	}

    fn get_right_subtree(&self) -> NodeLink {
        self.right
    }
}

type NodeLink = Option<Box<Node>>;
pub type NodeList = Vec<Node>;

/// Isolation Forest implementation.
pub struct Forest<'a> {
    feature_values: FeatureNameToValuesMap<'a>, // Lists each feature and maps it to all unique values in the training set
    trees: NodeList, // The decision trees that comprise the forest
    num_trees_to_create: u32, // The maximum number of trees to create
    sub_sampling_size: u32, // The maximum depth of a tree
}

impl<'a> Forest<'a> {
    pub fn new (num_trees_to_create: u32, sub_sampling_size: u32) -> Forest<'a> {
        Forest { num_trees_to_create: num_trees_to_create, sub_sampling_size: sub_sampling_size, trees: Forest::initialize_trees(), feature_values: Forest::create_feature_name_to_values_map() }
    }

    fn initialize_trees() -> NodeList {
        let mut v: NodeList = vec![];
        v
    }

    fn create_feature_name_to_values_map() -> FeatureNameToValuesMap<'a> {
        let mut m = HashMap::new();
        m
    }

    pub fn add_sample(&self, sample: Sample) {
		// Add each of this sample's features to the list of known features
		// with the corresponding set of unique values.
		let mut features = sample.features();
        for feature in &features {
            let mut found_feature = false;
        }
    }

    pub fn create_tree(&self, feature_values: FeatureNameToValuesMap<'a>, depth: u32) -> NodeList {
        let mut tree = NodeList::new();

		// Sanity check.
		if feature_values.len() <= 1 {
			return tree;
		}

		// If we've exceeded the maximum desired depth, then stop.
		if (self.sub_sampling_size > 0) && (depth >= self.sub_sampling_size) {
			return tree;
		}

		// Randomly select a feature.
		let mut selected_feature_index = 0;
		if feature_values.len() > 1 {
			return tree;
		}

		// Get the value list to split on.

		// Randomly select a split value.
		let mut split_value_index = 0;

		// Create a tree node to hold the split value.

        tree
    }

    pub fn create(&self) {
    	for _i in 0..self.num_trees_to_create {
            let tree = self.create_tree(self.feature_values, 0);
            if tree.len() > 0 {
            }
        }
    }

    fn score_tree(&self, sample: Sample, tree: NodeLink) -> f64 {
        let mut depth = 0.0;
        let mut current_node = tree;
        let mut done = false;

        while !done {
            let mut found_feature = false;
            let node_feature_name = current_node.get_feature_name();
            let features = sample.features();

            for feature in features {
                let feature_name = feature.get_name();

                if feature_name == node_feature_name {
					if feature.get_value() < current_node.split_value() {
						current_node = current_node.get_left_subtree();
                    } else {
						current_node = current_node.get_right_subtree();
                    }
                    found_feature = false;
                }
            }
        }
        depth
    }

    pub fn score(&self, sample: Sample) -> f64 {
        let mut score = 0.0;

        if self.trees.len() > 0 {
            for tree in self.trees {
                score += self.score_tree(sample, tree);
            }
            score /= self.trees.len() as f64;
        }
        score
    }
}
<commit_msg>Backup commit.<commit_after>//	MIT License
//
//  Copyright © 2017 Michael J Simms. All rights reserved.
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in all
//	copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//	SOFTWARE.

use std::collections::HashSet;
use std::collections::HashMap;

/// Each feature has a name and value.
pub struct Feature {
    name: String,
    value: u64,
}

impl Feature {
    pub fn new (name: &str, value: u64) -> Feature {
        Feature { name: name.to_string(), value: value }
    }

    pub fn set_name(&mut self, name: String) {
        self.name = name;
    }

    pub fn get_name(&self) -> String {
        self.name
    }

    pub fn set_value(&mut self, value: u64) {
        self.value = value;
    }

    pub fn get_value(&self) -> u64 {
        self.value
    }
}

pub type FeatureList = Vec<Feature>;
pub type Uint64Set = HashSet<u64>;
pub type FeatureNameToValuesMap<'a> = HashMap<&'a str, Uint64Set>;

/// This class represents a sample.
/// Each sample has a name and list of features.
pub struct Sample {
    name: String,
    features: FeatureList,
}

impl Sample {
    pub fn new (name: &str) -> Sample {
        Sample { name: name.to_string(), features: Sample::create_feature_list() }
    }

    fn create_feature_list() -> FeatureList {
        let mut v: FeatureList = vec![];
        v
    }

    pub fn add_features(&mut self, features: &mut FeatureList) {
        self.features.append(features);
    }

    pub fn add_feature(&mut self, feature: Feature) {
        self.features.push(feature);
    }

    pub fn features(&self) -> FeatureList {
        self.features
    }
}

pub type SampleList = Vec<Sample>;

/// Tree node, used internally.
struct Node {
    feature_name: String,
    left: NodeLink,
    right: NodeLink,
}

impl Node {
    pub fn new (feature_name: &str) -> Node {
        Node { feature_name: feature_name.to_string(), left: None, right: None }
    }

    fn get_feature_name(&self) -> String {
        self.feature_name
    }

	fn set_left_subtree(&mut self, subtree: NodeLink) {
		self.left = subtree;
	}

    fn get_left_subtree(&self) -> NodeLink {
        self.left
    }

	fn set_right_subtree(&mut self, subtree: NodeLink) {
		self.right = subtree;
	}

    fn get_right_subtree(&self) -> NodeLink {
        self.right
    }
}

type NodeLink = Option<Box<Node>>;
pub type NodeList = Vec<Node>;

/// Isolation Forest implementation.
pub struct Forest<'a> {
    feature_values: FeatureNameToValuesMap<'a>, // Lists each feature and maps it to all unique values in the training set
    trees: NodeList, // The decision trees that comprise the forest
    num_trees_to_create: u32, // The maximum number of trees to create
    sub_sampling_size: u32, // The maximum depth of a tree
}

impl<'a> Forest<'a> {
    pub fn new (num_trees_to_create: u32, sub_sampling_size: u32) -> Forest<'a> {
        Forest { num_trees_to_create: num_trees_to_create, sub_sampling_size: sub_sampling_size, trees: Forest::initialize_trees(), feature_values: Forest::create_feature_name_to_values_map() }
    }

    fn initialize_trees() -> NodeList {
        let mut v: NodeList = vec![];
        v
    }

    fn create_feature_name_to_values_map() -> FeatureNameToValuesMap<'a> {
        let mut m = HashMap::new();
        m
    }

    pub fn add_sample(&self, sample: Sample) {
		// Add each of this sample's features to the list of known features
		// with the corresponding set of unique values.
		let mut features = sample.features();
        for feature in &features {
            let mut found_feature = false;
        }
    }

    pub fn create_tree(&self, feature_values: FeatureNameToValuesMap<'a>, depth: u32) -> NodeList {
        let mut tree = NodeList::new();

		// Sanity check.
		if feature_values.len() <= 1 {
			return tree;
		}

		// If we've exceeded the maximum desired depth, then stop.
		if (self.sub_sampling_size > 0) && (depth >= self.sub_sampling_size) {
			return tree;
		}

		// Randomly select a feature.
		let mut selected_feature_index = 0;
		if feature_values.len() > 1 {
			return tree;
		}

		// Get the value list to split on.

		// Randomly select a split value.
		let mut split_value_index = 0;

		// Create a tree node to hold the split value.

        tree
    }

    pub fn create(&self) {
    	for _i in 0..self.num_trees_to_create {
            let tree = self.create_tree(self.feature_values, 0);
            if tree.len() > 0 {
            }
        }
    }

    fn score_tree(&self, sample: Sample, tree: NodeLink) -> f64 {
        let mut depth = 0.0;
        let mut current_node = tree;
        let mut done = false;

        while !done {
            let mut found_feature = false;
            let current_node_deref = current_node.deref();
            let node_feature_name = current_node_deref.get_feature_name();
            let features = sample.features();

            for feature in features {
                let feature_name = feature.get_name();

                if feature_name == node_feature_name {
					if feature.get_value() < current_node_deref.split_value() {
						current_node = current_node_deref.get_left_subtree();
                    } else {
						current_node = current_node_deref.get_right_subtree();
                    }
                    found_feature = false;
                }
            }
        }
        depth
    }

    pub fn score(&self, sample: Sample) -> f64 {
        let mut score = 0.0;

        if self.trees.len() > 0 {
            for tree in self.trees {
                score += self.score_tree(sample, tree);
            }
            score /= self.trees.len() as f64;
        }
        score
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::io::Read;
use std::ops::Add;
use std::ops::Sub;

use nom::bytes::complete::tag;
use nom::character::complete::newline;
use nom::combinator::map;
use nom::multi::many1;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use nom::sequence::preceded;
use nom::sequence::terminated;
use nom::sequence::tuple;
use nom::IResult;

use crate::common::read_input;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
struct Point3(pub [i32; 3]);

impl Sub for Point3 {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        Point3([
            self.0[0] - rhs.0[0],
            self.0[1] - rhs.0[1],
            self.0[2] - rhs.0[2],
        ])
    }
}

impl Add for Point3 {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Point3([
            self.0[0] + rhs.0[0],
            self.0[1] + rhs.0[1],
            self.0[2] + rhs.0[2],
        ])
    }
}

struct Rotations<'a> {
    points: &'a [Point3],
    axes: [usize; 3],
    rotation_index: usize,
}

impl<'a> Rotations<'a> {
    const ROTATIONS: [[i32; 3]; 8] = [
        [1, 1, 1],
        [1, 1, -1],
        [1, -1, 1],
        [1, -1, -1],
        [-1, 1, 1],
        [-1, 1, -1],
        [-1, -1, 1],
        [-1, -1, -1],
    ];

    pub fn new(points: &'a [Point3]) -> Self {
        Self {
            points,
            axes: [0, 1, 2],
            rotation_index: 0,
        }
    }
}

impl Iterator for Rotations<'_> {
    type Item = Vec<Point3>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rotation_index >= Self::ROTATIONS.len() {
            if !next_permutation(&mut self.axes) {
                return None;
            }

            self.rotation_index = 0;
        }

        let axes = &self.axes;
        let rot = &Self::ROTATIONS[self.rotation_index];

        let result = self
            .points
            .iter()
            .map(|Point3(coords)| {
                Point3([
                    coords[axes[0]] * rot[0],
                    coords[axes[1]] * rot[1],
                    coords[axes[2]] * rot[2],
                ])
            })
            .collect();

        self.rotation_index += 1;

        Some(result)
    }
}

fn parse_point(input: &[u8]) -> IResult<&[u8], Point3> {
    use nom::character::complete::char;
    use nom::character::complete::i32;

    map(
        tuple((i32, preceded(char(','), i32), preceded(char(','), i32))),
        |(x, y, z)| Point3([x, y, z]),
    )(input)
}

fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<Vec<Point3>>> {
    use nom::character::complete::i32;
    let parse_header = delimited(tag("--- scanner "), i32, tag(" ---\n"));

    let parse_scanner = preceded(parse_header, many1(terminated(parse_point, newline)));
    separated_list1(newline, parse_scanner)(input)
}

fn try_overlap(correct: &[(Point3, HashSet<Point3>)], candidate: &[Point3]) -> Option<Vec<Point3>> {
    let mut relative = HashSet::new();
    for rot in Rotations::new(candidate) {
        for &start in &rot {
            relative.clear();

            relative.extend(rot.iter().map(|&other| other - start));

            if let Some((base, _)) = correct.iter().find(|(_, correct_relative)| {
                correct_relative.intersection(&relative).count() >= 12
            }) {
                // Found a solution, build the correct output
                let translated = relative.drain().map(|point| point + *base).collect();

                return Some(translated);
            }
        }
    }

    None
}

pub fn part1(input: &mut dyn Read) -> String {
    let mut scanners = read_input(input, parse_input);

    let mut points: HashSet<_> = scanners[0].iter().copied().collect();

    let mut todo = vec![std::mem::take(&mut scanners[0])];
    println!("Scanners: {}", scanners.len());

    while let Some(matched) = todo.pop() {
        if scanners.iter().all(Vec::is_empty) {
            break;
        }

        let relative: Vec<(Point3, HashSet<Point3>)> = matched
            .iter()
            .map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
            .collect();

        for candidate in &mut scanners {
            if candidate.is_empty() {
                continue;
            }

            if let Some(result) = try_overlap(&relative, candidate) {
                points.extend(result.iter().copied());
                todo.push(result);
                candidate.clear();
            }
        }
    }

    scanners.retain(|s| !s.is_empty());

    points.len().to_string()
}

pub fn part2(_input: &mut dyn Read) -> String {
    todo!()
}

pub fn next_permutation<T: Ord>(list: &mut [T]) -> bool {
    // Based on: https://en.cppreference.com/w/cpp/algorithm/next_permutation
    if list.len() <= 1 {
        return false;
    }

    if let Some((i, val1)) = list
        .windows(2)
        .enumerate()
        .rev()
        .find_map(|(i, window)| (window[0] < window[1]).then(|| (i, &window[0])))
    {
        let it2 = list
            .iter()
            .enumerate()
            .skip(i + 1)
            .rev()
            .find_map(|(idx, val2)| (val1 < val2).then(|| idx))
            .expect("Unreachable, ascending pair exists");

        list.swap(i, it2);
        list[(i + 1)..].reverse();
        true
    } else {
        list.reverse();
        false
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/19.txt");

    #[test]
    fn test_next_permutation() {
        let mut list = [1, 2, 3];

        assert!(next_permutation(&mut list));
        assert_eq!(list, [1, 3, 2]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [2, 1, 3]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [2, 3, 1]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [3, 1, 2]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [3, 2, 1]);

        // Note the negation!
        assert!(!next_permutation(&mut list));
        assert_eq!(list, [1, 2, 3]);

        // 24 is a bit too much to write out, but we can check the number
        let mut list2 = [1, 2, 3, 4];
        for _ in 1..24 {
            assert!(next_permutation(&mut list2));
        }

        // Should be back to the start
        assert!(!next_permutation(&mut list2));
        assert_eq!(list2, [1, 2, 3, 4]);
    }

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 79);
    }
}
<commit_msg>Implement part 2 with the same brute force<commit_after>use std::collections::HashSet;
use std::io::Read;
use std::ops::Add;
use std::ops::Sub;

use nom::bytes::complete::tag;
use nom::character::complete::newline;
use nom::combinator::map;
use nom::multi::many1;
use nom::multi::separated_list1;
use nom::sequence::delimited;
use nom::sequence::preceded;
use nom::sequence::terminated;
use nom::sequence::tuple;
use nom::IResult;

use crate::common::read_input;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
struct Point3(pub [i32; 3]);

impl Point3 {
    pub fn manhattan(&self) -> i32 {
        self.0.into_iter().map(i32::abs).sum()
    }
}

impl Sub for Point3 {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        Point3([
            self.0[0] - rhs.0[0],
            self.0[1] - rhs.0[1],
            self.0[2] - rhs.0[2],
        ])
    }
}

impl Add for Point3 {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        Point3([
            self.0[0] + rhs.0[0],
            self.0[1] + rhs.0[1],
            self.0[2] + rhs.0[2],
        ])
    }
}

struct Rotations<'a> {
    points: &'a [Point3],
    axes: [usize; 3],
    rotation_index: usize,
}

impl<'a> Rotations<'a> {
    const ROTATIONS: [[i32; 3]; 8] = [
        [1, 1, 1],
        [1, 1, -1],
        [1, -1, 1],
        [1, -1, -1],
        [-1, 1, 1],
        [-1, 1, -1],
        [-1, -1, 1],
        [-1, -1, -1],
    ];

    pub fn new(points: &'a [Point3]) -> Self {
        Self {
            points,
            axes: [0, 1, 2],
            rotation_index: 0,
        }
    }
}

impl Iterator for Rotations<'_> {
    type Item = Vec<Point3>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.rotation_index >= Self::ROTATIONS.len() {
            if !next_permutation(&mut self.axes) {
                return None;
            }

            self.rotation_index = 0;
        }

        let axes = &self.axes;
        let rot = &Self::ROTATIONS[self.rotation_index];

        let result = self
            .points
            .iter()
            .map(|Point3(coords)| {
                Point3([
                    coords[axes[0]] * rot[0],
                    coords[axes[1]] * rot[1],
                    coords[axes[2]] * rot[2],
                ])
            })
            .collect();

        self.rotation_index += 1;

        Some(result)
    }
}

fn parse_point(input: &[u8]) -> IResult<&[u8], Point3> {
    use nom::character::complete::char;
    use nom::character::complete::i32;

    map(
        tuple((i32, preceded(char(','), i32), preceded(char(','), i32))),
        |(x, y, z)| Point3([x, y, z]),
    )(input)
}

fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<Vec<Point3>>> {
    use nom::character::complete::i32;
    let parse_header = delimited(tag("--- scanner "), i32, tag(" ---\n"));

    let parse_scanner = preceded(parse_header, many1(terminated(parse_point, newline)));
    separated_list1(newline, parse_scanner)(input)
}

fn try_overlap(
    correct: &[(Point3, HashSet<Point3>)],
    candidate: &[Point3],
) -> Option<(Point3, Vec<Point3>)> {
    let mut relative = HashSet::new();
    for rot in Rotations::new(candidate) {
        for &start in &rot {
            relative.clear();

            relative.extend(rot.iter().map(|&other| other - start));

            if let Some((base, _)) = correct.iter().find(|(_, correct_relative)| {
                correct_relative.intersection(&relative).count() >= 12
            }) {
                // Found a solution, build the correct output
                let translated = relative.drain().map(|point| point + *base).collect();

                return Some((start - *base, translated));
            }
        }
    }

    None
}

pub fn part1(input: &mut dyn Read) -> String {
    let mut scanners = read_input(input, parse_input);

    let mut points: HashSet<_> = scanners[0].iter().copied().collect();

    let mut todo = vec![std::mem::take(&mut scanners[0])];

    while let Some(matched) = todo.pop() {
        if scanners.iter().all(Vec::is_empty) {
            break;
        }

        let relative: Vec<(Point3, HashSet<Point3>)> = matched
            .iter()
            .map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
            .collect();

        for candidate in &mut scanners {
            if candidate.is_empty() {
                continue;
            }

            if let Some((_, result)) = try_overlap(&relative, candidate) {
                points.extend(result.iter().copied());
                todo.push(result);
                candidate.clear();
            }
        }
    }

    points.len().to_string()
}

pub fn part2(input: &mut dyn Read) -> String {
    let mut scanners = read_input(input, parse_input);

    let mut found_scanners = vec![Point3::default()];
    let mut todo = vec![std::mem::take(&mut scanners[0])];

    while let Some(matched) = todo.pop() {
        if scanners.iter().all(Vec::is_empty) {
            break;
        }

        let relative: Vec<(Point3, HashSet<Point3>)> = matched
            .iter()
            .map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
            .collect();

        for candidate in &mut scanners {
            if candidate.is_empty() {
                continue;
            }

            if let Some((new_offset, result)) = try_overlap(&relative, candidate) {
                todo.push(result);
                candidate.clear();

                found_scanners.push(new_offset);
            }
        }
    }

    found_scanners
        .iter()
        .flat_map(|&first| {
            found_scanners
                .iter()
                .map(move |&second| (first - second).manhattan())
        })
        .max()
        .unwrap()
        .to_string()
}

pub fn next_permutation<T: Ord>(list: &mut [T]) -> bool {
    // Based on: https://en.cppreference.com/w/cpp/algorithm/next_permutation
    if list.len() <= 1 {
        return false;
    }

    if let Some((i, val1)) = list
        .windows(2)
        .enumerate()
        .rev()
        .find_map(|(i, window)| (window[0] < window[1]).then(|| (i, &window[0])))
    {
        let it2 = list
            .iter()
            .enumerate()
            .skip(i + 1)
            .rev()
            .find_map(|(idx, val2)| (val1 < val2).then(|| idx))
            .expect("Unreachable, ascending pair exists");

        list.swap(i, it2);
        list[(i + 1)..].reverse();
        true
    } else {
        list.reverse();
        false
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::test_implementation;

    const SAMPLE: &[u8] = include_bytes!("samples/19.txt");

    #[test]
    fn test_next_permutation() {
        let mut list = [1, 2, 3];

        assert!(next_permutation(&mut list));
        assert_eq!(list, [1, 3, 2]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [2, 1, 3]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [2, 3, 1]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [3, 1, 2]);

        assert!(next_permutation(&mut list));
        assert_eq!(list, [3, 2, 1]);

        // Note the negation!
        assert!(!next_permutation(&mut list));
        assert_eq!(list, [1, 2, 3]);

        // 24 is a bit too much to write out, but we can check the number
        let mut list2 = [1, 2, 3, 4];
        for _ in 1..24 {
            assert!(next_permutation(&mut list2));
        }

        // Should be back to the start
        assert!(!next_permutation(&mut list2));
        assert_eq!(list2, [1, 2, 3, 4]);
    }

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE, 79);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE, 3621);
    }
}
<|endoftext|>


<commit_before>use crate::*;
use ssz::TreeHash;

/// Builds an `AttesterSlashing`.
///
/// This struct should **never be used for production purposes.**
pub struct TestingAttesterSlashingBuilder();

impl TestingAttesterSlashingBuilder {
    /// Builds an `AttesterSlashing` that is a double vote.
    ///
    /// The `signer` function is used to sign the double-vote and accepts:
    ///
    /// - `validator_index: u64`
    /// - `message: &[u8]`
    /// - `epoch: Epoch`
    /// - `domain: Domain`
    ///
    /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`).
    pub fn double_vote<F>(validator_indices: &[u64], signer: F) -> AttesterSlashing
    where
        F: Fn(u64, &[u8], Epoch, Domain) -> Signature,
    {
        let double_voted_slot = Slot::new(0);
        let shard = 0;
        let justified_epoch = Epoch::new(0);
        let epoch = Epoch::new(0);
        let hash_1 = Hash256::from_low_u64_le(1);
        let hash_2 = Hash256::from_low_u64_le(2);

        let mut slashable_attestation_1 = SlashableAttestation {
            validator_indices: validator_indices.to_vec(),
            data: AttestationData {
                slot: double_voted_slot,
                shard,
                beacon_block_root: hash_1,
                epoch_boundary_root: hash_1,
                crosslink_data_root: hash_1,
                latest_crosslink: Crosslink {
                    epoch,
                    crosslink_data_root: hash_1,
                },
                justified_epoch,
                justified_block_root: hash_1,
            },
            custody_bitfield: Bitfield::new(),
            aggregate_signature: AggregateSignature::new(),
        };

        let mut slashable_attestation_2 = SlashableAttestation {
            validator_indices: validator_indices.to_vec(),
            data: AttestationData {
                slot: double_voted_slot,
                shard,
                beacon_block_root: hash_2,
                epoch_boundary_root: hash_2,
                crosslink_data_root: hash_2,
                latest_crosslink: Crosslink {
                    epoch,
                    crosslink_data_root: hash_2,
                },
                justified_epoch,
                justified_block_root: hash_2,
            },
            custody_bitfield: Bitfield::new(),
            aggregate_signature: AggregateSignature::new(),
        };

        let add_signatures = |attestation: &mut SlashableAttestation| {
            for (i, validator_index) in validator_indices.iter().enumerate() {
                attestation.custody_bitfield.set(i, false);
                let attestation_data_and_custody_bit = AttestationDataAndCustodyBit {
                    data: attestation.data.clone(),
                    custody_bit: attestation.custody_bitfield.get(i).unwrap(),
                };
                let message = attestation_data_and_custody_bit.hash_tree_root();
                let signature = signer(*validator_index, &message[..], epoch, Domain::Attestation);
                attestation.aggregate_signature.add(&signature);
            }
        };

        add_signatures(&mut slashable_attestation_1);
        add_signatures(&mut slashable_attestation_2);

        AttesterSlashing {
            slashable_attestation_1,
            slashable_attestation_2,
        }
    }
}
<commit_msg>Optimize TestingAttesterSlashingBuilder<commit_after>use crate::*;
use ssz::TreeHash;

/// Builds an `AttesterSlashing`.
///
/// This struct should **never be used for production purposes.**
pub struct TestingAttesterSlashingBuilder();

impl TestingAttesterSlashingBuilder {
    /// Builds an `AttesterSlashing` that is a double vote.
    ///
    /// The `signer` function is used to sign the double-vote and accepts:
    ///
    /// - `validator_index: u64`
    /// - `message: &[u8]`
    /// - `epoch: Epoch`
    /// - `domain: Domain`
    ///
    /// Where domain is a domain "constant" (e.g., `spec.domain_attestation`).
    pub fn double_vote<F>(validator_indices: &[u64], signer: F) -> AttesterSlashing
    where
        F: Fn(u64, &[u8], Epoch, Domain) -> Signature,
    {
        let double_voted_slot = Slot::new(0);
        let shard = 0;
        let justified_epoch = Epoch::new(0);
        let epoch = Epoch::new(0);
        let hash_1 = Hash256::from_low_u64_le(1);
        let hash_2 = Hash256::from_low_u64_le(2);

        let mut slashable_attestation_1 = SlashableAttestation {
            validator_indices: validator_indices.to_vec(),
            data: AttestationData {
                slot: double_voted_slot,
                shard,
                beacon_block_root: hash_1,
                epoch_boundary_root: hash_1,
                crosslink_data_root: hash_1,
                latest_crosslink: Crosslink {
                    epoch,
                    crosslink_data_root: hash_1,
                },
                justified_epoch,
                justified_block_root: hash_1,
            },
            custody_bitfield: Bitfield::new(),
            aggregate_signature: AggregateSignature::new(),
        };

        let mut slashable_attestation_2 = SlashableAttestation {
            validator_indices: validator_indices.to_vec(),
            data: AttestationData {
                slot: double_voted_slot,
                shard,
                beacon_block_root: hash_2,
                epoch_boundary_root: hash_2,
                crosslink_data_root: hash_2,
                latest_crosslink: Crosslink {
                    epoch,
                    crosslink_data_root: hash_2,
                },
                justified_epoch,
                justified_block_root: hash_2,
            },
            custody_bitfield: Bitfield::new(),
            aggregate_signature: AggregateSignature::new(),
        };

        let add_signatures = |attestation: &mut SlashableAttestation| {
            // All validators sign with a `false` custody bit.
            let attestation_data_and_custody_bit = AttestationDataAndCustodyBit {
                data: attestation.data.clone(),
                custody_bit: false,
            };
            let message = attestation_data_and_custody_bit.hash_tree_root();

            for (i, validator_index) in validator_indices.iter().enumerate() {
                attestation.custody_bitfield.set(i, false);
                let signature = signer(*validator_index, &message[..], epoch, Domain::Attestation);
                attestation.aggregate_signature.add(&signature);
            }
        };

        add_signatures(&mut slashable_attestation_1);
        add_signatures(&mut slashable_attestation_2);

        AttesterSlashing {
            slashable_attestation_1,
            slashable_attestation_2,
        }
    }
}
<|endoftext|>


<commit_before>#[allow(unused)]
macro_rules! features {
    (
      @TARGET: $target:ident;
      @MACRO_NAME: $macro_name:ident;
      @MACRO_ATTRS: $(#[$macro_attrs:meta])*
      $(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; )*
      $(@NO_RUNTIME_DETECTION: $nort_feature:tt; )*
      $(@FEATURE: #[$stability_attr:meta] $feature:ident: $feature_lit:tt; $(#[$feature_comment:meta])*)*
    ) => {
        #[macro_export]
        $(#[$macro_attrs])*
        #[allow_internal_unstable(stdsimd_internal)]
        macro_rules! $macro_name {
            $(
                ($feature_lit) => {
                    $crate::detect::__is_feature_detected::$feature()
                };
            )*
            $(
                ($bind_feature) => { $macro_name!($feature_impl) };
            )*
            $(
                ($nort_feature) => {
                    compile_error!(
                        concat!(
                            stringify!(nort_feature),
                            " feature cannot be detected at run-time"
                        )
                    )
                };
            )*
            ($t:tt,) => {
                    $macro_name!($t);
            };
            ($t:tt) => {
                compile_error!(
                    concat!(
                        concat!("unknown ", stringify!($target)),
                        concat!(" target feature: ", $t)
                    )
                )
            };
        }

        /// Each variant denotes a position in a bitset for a particular feature.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        #[allow(non_camel_case_types)]
        #[derive(Copy, Clone)]
        #[repr(u8)]
        #[unstable(feature = "stdsimd_internal", issue = "none")]
        pub(crate) enum Feature {
            $(
                $(#[$feature_comment])*
                $feature,
            )*

            // Do not add variants after last:
            _last
        }

        impl Feature {
            pub(crate) fn to_str(self) -> &'static str {
                match self {
                    $(Feature::$feature => $feature_lit,)*
                    Feature::_last => unreachable!(),
                }
            }
            #[cfg(feature = "std_detect_env_override")]
            pub(crate) fn from_str(s: &str) -> Result<Feature, ()> {
                match s {
                    $($feature_lit => Ok(Feature::$feature),)*
                    _ => Err(())
                }
            }
        }

        /// Each function performs run-time feature detection for a single
        /// feature. This allow us to use stability attributes on a per feature
        /// basis.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        pub mod __is_feature_detected {
            $(

                /// PLEASE: do not use this, it is an implementation detail
                /// subject to change.
                #[inline]
                #[doc(hidden)]
                #[$stability_attr]
                pub fn $feature() -> bool {
                    cfg!(target_feature = $feature_lit) ||
                        $crate::detect::check_for($crate::detect::Feature::$feature)
                }
            )*
        }
    };
}
<commit_msg>Move cfg!(target_feature) directly into is_*_feature_detected!() (#1141)<commit_after>#[allow(unused)]
macro_rules! features {
    (
      @TARGET: $target:ident;
      @MACRO_NAME: $macro_name:ident;
      @MACRO_ATTRS: $(#[$macro_attrs:meta])*
      $(@BIND_FEATURE_NAME: $bind_feature:tt; $feature_impl:tt; )*
      $(@NO_RUNTIME_DETECTION: $nort_feature:tt; )*
      $(@FEATURE: #[$stability_attr:meta] $feature:ident: $feature_lit:tt; $(#[$feature_comment:meta])*)*
    ) => {
        #[macro_export]
        $(#[$macro_attrs])*
        #[allow_internal_unstable(stdsimd_internal)]
        macro_rules! $macro_name {
            $(
                ($feature_lit) => {
                    cfg!(target_feature = $feature_lit) ||
                        $crate::detect::__is_feature_detected::$feature()
                };
            )*
            $(
                ($bind_feature) => { $macro_name!($feature_impl) };
            )*
            $(
                ($nort_feature) => {
                    compile_error!(
                        concat!(
                            stringify!(nort_feature),
                            " feature cannot be detected at run-time"
                        )
                    )
                };
            )*
            ($t:tt,) => {
                    $macro_name!($t);
            };
            ($t:tt) => {
                compile_error!(
                    concat!(
                        concat!("unknown ", stringify!($target)),
                        concat!(" target feature: ", $t)
                    )
                )
            };
        }

        /// Each variant denotes a position in a bitset for a particular feature.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        #[allow(non_camel_case_types)]
        #[derive(Copy, Clone)]
        #[repr(u8)]
        #[unstable(feature = "stdsimd_internal", issue = "none")]
        pub(crate) enum Feature {
            $(
                $(#[$feature_comment])*
                $feature,
            )*

            // Do not add variants after last:
            _last
        }

        impl Feature {
            pub(crate) fn to_str(self) -> &'static str {
                match self {
                    $(Feature::$feature => $feature_lit,)*
                    Feature::_last => unreachable!(),
                }
            }
            #[cfg(feature = "std_detect_env_override")]
            pub(crate) fn from_str(s: &str) -> Result<Feature, ()> {
                match s {
                    $($feature_lit => Ok(Feature::$feature),)*
                    _ => Err(())
                }
            }
        }

        /// Each function performs run-time feature detection for a single
        /// feature. This allow us to use stability attributes on a per feature
        /// basis.
        ///
        /// PLEASE: do not use this, it is an implementation detail subject
        /// to change.
        #[doc(hidden)]
        pub mod __is_feature_detected {
            $(

                /// PLEASE: do not use this, it is an implementation detail
                /// subject to change.
                #[inline]
                #[doc(hidden)]
                #[$stability_attr]
                pub fn $feature() -> bool {
                    $crate::detect::check_for($crate::detect::Feature::$feature)
                }
            )*
        }
    };
}
<|endoftext|>


<commit_before>extern crate llvm_tools;
extern crate target_build_utils;
extern crate walkdir;

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-hermit-kernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	#[cfg(feature = "instrument")]
	cmd.env("RUSTFLAGS", "-Z instrument-mcount");
	// if instrument is not set, ensure that instrument is not in environment variables!
	#[cfg(not(feature = "instrument"))]
	cmd.env(
		"RUSTFLAGS",
		env::var("RUSTFLAGS")
			.unwrap_or_else(|_| "".into())
			.replace("-Z instrument-mcount", ""),
	);

	let output = cmd.output().expect("Unable to build kernel");
	let stdout = std::string::String::from_utf8(output.stdout);
	let stderr = std::string::String::from_utf8(output.stderr);

	println!("Build libhermit-rs output-status: {}", output.status);
	println!("Build libhermit-rs output-stdout: {}", stdout.unwrap());
	println!("Build libhermit-rs output-stderr: {}", stderr.unwrap());
	assert!(output.status.success());

	let lib_location = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-hermit-kernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

	// in case of a debug version of the library, we have to avoid a symbol collision.
	// Kernel and user space has its own versions of memcpy, memset, etc,
	// Consequently, we rename the functions in the libos to avoid collisions.
	// In addition, it provides us the offer to create a optimized version of memcpy
	// in user space.
	if profile == "debug" {
		// get access to llvm tools shipped in the llvm-tools-preview rustup component
		let llvm_tools = match llvm_tools::LlvmTools::new() {
			Ok(tools) => tools,
			Err(llvm_tools::Error::NotFound) => {
				eprintln!("Error: llvm-tools not found");
				eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?");
				eprintln!("  Install it through: `rustup component add llvm-tools-preview`");
				process::exit(1);
			}
			Err(err) => {
				eprintln!("Failed to retrieve llvm-tools component: {:?}", err);
				process::exit(1);
			}
		};

		let lib = lib_location.join("libhermit.a");

		// determine llvm_objcopy
		let llvm_objcopy = llvm_tools
			.tool(&llvm_tools::exe("llvm-objcopy"))
			.expect("llvm_objcopy not found in llvm-tools");

		// rename symbols
		let mut cmd = Command::new(llvm_objcopy);
		cmd.arg("--redefine-sym")
			.arg("memcpy=kernel_memcpy")
			.arg("--redefine-sym")
			.arg("memmove=kernel_memmove")
			.arg("--redefine-sym")
			.arg("memset=kernel_memset")
			.arg("--redefine-sym")
			.arg("memcmp=kernel_memcmp")
			.arg("--redefine-sym")
			.arg("bcmp=kernel_bcmp")
			.arg(lib.display().to_string());

		println!("cmd {:?}", cmd);
		let output = cmd.output().expect("Unable to rename symbols");
		let stdout = std::string::String::from_utf8(output.stdout);
		let stderr = std::string::String::from_utf8(output.stderr);
		println!("Rename symbols output-status: {}", output.status);
		println!("Rename symbols output-stdout: {}", stdout.unwrap());
		println!("Rename symbols output-stderr: {}", stderr.unwrap());
		assert!(output.status.success());
	}

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	//HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	// Doesn't actually rebuild atm - see: https://github.com/rust-lang/cargo/issues/8306
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	let _output = Command::new("cargo")
		.current_dir(out_dir)
		.arg("download")
		.arg("--output")
		.arg(src_dir.clone().into_os_string())
		.arg("--extract")
		.arg("rusty-hermit")
		.output()
		.expect("Unable to download rusty-hermit. Please install `cargo-download`.");

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.clone().join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(|e| is_not_ignored(e))
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<commit_msg>try to rename symbols also in the release version<commit_after>extern crate llvm_tools;
extern crate target_build_utils;
extern crate walkdir;

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

use target_build_utils::TargetInfo;
use walkdir::{DirEntry, WalkDir};

fn build_hermit(src_dir: &Path, target_dir_opt: Option<&Path>) {
	let target = TargetInfo::new().expect("Could not get target info");
	let profile = env::var("PROFILE").expect("PROFILE was not set");
	let mut cmd = Command::new("cargo");

	if target.target_arch() == "x86_64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("x86_64-unknown-hermit-kernel");
	} else if target.target_arch() == "aarch64" {
		cmd.current_dir(src_dir)
			.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg("aarch64-unknown-hermit");
	} else {
		panic!("Try to build for an unsupported platform");
	}

	if let Some(target_dir) = target_dir_opt {
		cmd.arg("--target-dir").arg(target_dir);
	}
	let target_dir = match target_dir_opt {
		Some(target_dir) => target_dir.to_path_buf(),
		None => src_dir.join("target"),
	};

	if profile == "release" {
		cmd.arg("--release");
	}

	#[cfg(feature = "instrument")]
	cmd.env("RUSTFLAGS", "-Z instrument-mcount");
	// if instrument is not set, ensure that instrument is not in environment variables!
	#[cfg(not(feature = "instrument"))]
	cmd.env(
		"RUSTFLAGS",
		env::var("RUSTFLAGS")
			.unwrap_or_else(|_| "".into())
			.replace("-Z instrument-mcount", ""),
	);

	let output = cmd.output().expect("Unable to build kernel");
	let stdout = std::string::String::from_utf8(output.stdout);
	let stderr = std::string::String::from_utf8(output.stderr);

	println!("Build libhermit-rs output-status: {}", output.status);
	println!("Build libhermit-rs output-stdout: {}", stdout.unwrap());
	println!("Build libhermit-rs output-stderr: {}", stderr.unwrap());
	assert!(output.status.success());

	let lib_location = if target.target_arch() == "x86_64" {
		target_dir
			.join("x86_64-unknown-hermit-kernel")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else if target.target_arch() == "aarch64" {
		target_dir
			.join("aarch64-unknown-hermit")
			.join(&profile)
			.canonicalize()
			.unwrap() // Must exist after building
	} else {
		panic!("Try to build for an unsupported platform");
	};
	println!("Lib location: {}", lib_location.display());

	// Kernel and user space has its own versions of memcpy, memset, etc,
	// Consequently, we rename the functions in the libos to avoid collisions.
	// In addition, it provides us the offer to create a optimized version of memcpy
	// in user space.

	// get access to llvm tools shipped in the llvm-tools-preview rustup component
	let llvm_tools = match llvm_tools::LlvmTools::new() {
		Ok(tools) => tools,
		Err(llvm_tools::Error::NotFound) => {
			eprintln!("Error: llvm-tools not found");
			eprintln!("Maybe the rustup component `llvm-tools-preview` is missing?");
			eprintln!("  Install it through: `rustup component add llvm-tools-preview`");
			process::exit(1);
		}
		Err(err) => {
			eprintln!("Failed to retrieve llvm-tools component: {:?}", err);
			process::exit(1);
		}
	};

	let lib = lib_location.join("libhermit.a");

	// determine llvm_objcopy
	let llvm_objcopy = llvm_tools
		.tool(&llvm_tools::exe("llvm-objcopy"))
		.expect("llvm_objcopy not found in llvm-tools");

	// rename symbols
	let mut cmd = Command::new(llvm_objcopy);
	cmd.arg("--redefine-sym")
		.arg("memcpy=kernel_memcpy")
		.arg("--redefine-sym")
		.arg("memmove=kernel_memmove")
		.arg("--redefine-sym")
		.arg("memset=kernel_memset")
		.arg("--redefine-sym")
		.arg("memcmp=kernel_memcmp")
		.arg("--redefine-sym")
		.arg("bcmp=kernel_bcmp")
		.arg(lib.display().to_string());

	println!("cmd {:?}", cmd);
	let output = cmd.output().expect("Unable to rename symbols");
	let stdout = std::string::String::from_utf8(output.stdout);
	let stderr = std::string::String::from_utf8(output.stderr);
	println!("Rename symbols output-status: {}", output.status);
	println!("Rename symbols output-stdout: {}", stdout.unwrap());
	println!("Rename symbols output-stderr: {}", stderr.unwrap());

	println!("cargo:rustc-link-search=native={}", lib_location.display());
	println!("cargo:rustc-link-lib=static=hermit");

	//HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
	// Doesn't actually rebuild atm - see: https://github.com/rust-lang/cargo/issues/8306
	println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
}

#[cfg(all(not(feature = "rustc-dep-of-std"), not(feature = "with_submodule")))]
fn build() {
	#[cfg(windows)]
	let out_dir = env::temp_dir();
	#[cfg(not(windows))]
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let src_dir = out_dir.join("rusty-hermit");

	let _output = Command::new("cargo")
		.current_dir(out_dir)
		.arg("download")
		.arg("--output")
		.arg(src_dir.clone().into_os_string())
		.arg("--extract")
		.arg("rusty-hermit")
		.output()
		.expect("Unable to download rusty-hermit. Please install `cargo-download`.");

	build_hermit(src_dir.as_ref(), None);
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn build() {
	let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
	let target_dir = out_dir.clone().join("target");
	let src_dir = env::current_dir()
		.unwrap()
		.parent()
		.unwrap()
		.join("libhermit-rs");

	build_hermit(src_dir.as_ref(), Some(target_dir.as_ref()));
	configure_cargo_rerun_if_changed(src_dir.as_ref());
}

#[cfg(all(not(feature = "rustc-dep-of-std"), feature = "with_submodule"))]
fn configure_cargo_rerun_if_changed(src_dir: &Path) {
	fn is_not_ignored(entry: &DirEntry) -> bool {
		// Ignore .git .vscode and target directories, but not .cargo or .github
		if entry.depth() == 1
			&& entry.path().is_dir()
			&& (entry.path().ends_with("target")
				|| entry.path().ends_with(".git")
				|| entry.path().ends_with(".vscode"))
		{
			return false;
		}
		true
	}

	WalkDir::new(src_dir)
		.into_iter()
		.filter_entry(|e| is_not_ignored(e))
		.filter_map(|v| v.ok())
		.filter_map(|v| v.path().canonicalize().ok())
		.for_each(|x| println!("cargo:rerun-if-changed={}", x.display()));
}

#[cfg(not(feature = "rustc-dep-of-std"))]
fn create_constants() {
	let out_dir = env::var("OUT_DIR").expect("No out dir");
	let dest_path = Path::new(&out_dir).join("constants.rs");
	let mut f = File::create(&dest_path).expect("Could not create file");

	let ip = option_env!("HERMIT_IP");
	let ip = ip.map_or("10.0.5.3", |v| v);

	let gateway = option_env!("HERMIT_GATEWAY");
	let gateway = gateway.map_or("10.0.5.1", |v| v);

	let mask = option_env!("HERMIT_MASK");
	let mask = mask.map_or("255.255.255.0", |v| v);

	writeln!(&mut f, "const HERMIT_IP: &str = \"{}\";", ip).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_IP");

	writeln!(&mut f, "const HERMIT_GATEWAY: &str = \"{}\";", gateway)
		.expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_GATEWAY");

	writeln!(&mut f, "const HERMIT_MASK: &str = \"{}\";", mask).expect("Could not write file");
	println!("cargo:rerun-if-env-changed=HERMIT_MASK");
}

fn main() {
	#[cfg(not(feature = "rustc-dep-of-std"))]
	create_constants();
	#[cfg(not(feature = "rustc-dep-of-std"))]
	build();
}
<|endoftext|>


<commit_before>//! Generator configurations specific for each Qt module.

use crate::detect_signals_and_slots::detect_signals_and_slots;
use crate::doc_parser::{parse_docs, set_crate_root_doc};
use crate::slot_wrappers::add_slot_wrappers;
use log::info;
use qt_ritual_common::{all_crate_names, get_full_build_config, lib_dependencies, lib_folder_name};
use ritual::config::{Config, CrateDependencyKind, GlobalConfig};
use ritual::config::{CrateDependencySource, CrateProperties};
use ritual_common::cpp_build_config::CppLibraryType;
use ritual_common::cpp_build_config::{CppBuildConfigData, CppBuildPaths};
use ritual_common::errors::{bail, format_err, Result, ResultExt};
use ritual_common::file_utils::repo_dir_path;
use ritual_common::target;
use ritual_common::toml;
use std::path::{Path, PathBuf};

mod _3d;
mod charts;
mod core;
mod gui;
mod widgets;

use self::_3d::{
    core_3d_config, extras_3d_config, input_3d_config, logic_3d_config, render_3d_config,
};
use self::{charts::charts_config, core::core_config, gui::gui_config, widgets::widgets_config};
use std::env;

pub const MOQT_INSTALL_DIR_ENV_VAR_NAME: &str = "MOQT_INSTALL_DIR";
pub const MOQT_TEMPLATE_DIR_ENV_VAR_NAME: &str = "MOQT_TEMPLATE_DIR";

/// Version of all generated Qt crates
const QT_OUTPUT_CRATES_VERSION: &str = "0.4.0";

fn empty_config(_config: &mut Config) -> Result<()> {
    Ok(())
}

/// Executes the generator for a single Qt module with given configuration.
pub fn create_config(crate_name: &str, qmake_path: Option<&str>) -> Result<Config> {
    info!("Preparing generator config for crate: {}", crate_name);
    let mut crate_properties = CrateProperties::new(crate_name, QT_OUTPUT_CRATES_VERSION);
    let mut custom_fields = toml::value::Table::new();
    let mut package_data = toml::value::Table::new();
    package_data.insert(
        "authors".to_string(),
        toml::Value::Array(vec![toml::Value::String(
            "Pavel Strakhov <ri@idzaaus.org>".to_string(),
        )]),
    );
    let description = format!("Bindings for {} C++ library", lib_folder_name(crate_name));
    package_data.insert("description".to_string(), toml::Value::String(description));
    let doc_url = format!("https://rust-qt.github.io/rustdoc/qt/{}", &crate_name);
    package_data.insert("documentation".to_string(), toml::Value::String(doc_url));
    package_data.insert(
        "repository".to_string(),
        toml::Value::String("https://github.com/rust-qt/ritual".to_string()),
    );
    package_data.insert(
        "license".to_string(),
        toml::Value::String("MIT OR Apache-2.0".to_string()),
    );
    package_data.insert(
        "keywords".to_string(),
        toml::Value::Array(vec![
            toml::Value::String("gui".to_string()),
            toml::Value::String("ffi".to_string()),
            toml::Value::String("qt".to_string()),
            toml::Value::String("ritual".to_string()),
        ]),
    );
    package_data.insert(
        "categories".to_string(),
        toml::Value::Array(vec![
            toml::Value::String("external-ffi-bindings".to_string()),
            toml::Value::String("gui".to_string()),
        ]),
    );

    custom_fields.insert("package".to_string(), toml::Value::Table(package_data));
    crate_properties.set_custom_fields(custom_fields);

    for &dependency in lib_dependencies(crate_name)? {
        crate_properties.add_dependency(
            dependency,
            CrateDependencyKind::Ritual,
            CrateDependencySource::CurrentWorkspace,
        )?;
    }

    let mut config = if crate_name.starts_with("moqt_") {
        let mut config = Config::new(crate_properties);
        let moqt_path =
            PathBuf::from(env::var(MOQT_INSTALL_DIR_ENV_VAR_NAME).with_context(|_| {
                format_err!("{} env var is missing", MOQT_INSTALL_DIR_ENV_VAR_NAME)
            })?);

        config.add_include_directive(format!("{}.h", crate_name));
        let include_path = moqt_path.join("include");
        if !include_path.exists() {
            bail!("Path does not exist: {}", include_path.display());
        }
        let lib_path = moqt_path.join("lib");
        if !lib_path.exists() {
            bail!("Path does not exist: {}", lib_path.display());
        }
        let sublib_include_path = include_path.join(crate_name);
        if !sublib_include_path.exists() {
            bail!("Path does not exist: {}", sublib_include_path.display());
        }
        {
            let mut paths = CppBuildPaths::new();
            paths.add_include_path(&sublib_include_path);
            paths.add_lib_path(&lib_path);

            for &lib in lib_dependencies(crate_name)? {
                let dep_include_path = include_path.join(lib);
                if !dep_include_path.exists() {
                    bail!("Path does not exist: {}", dep_include_path.display());
                }
                paths.add_include_path(&dep_include_path);
            }
            config.set_cpp_build_paths(paths);
        }
        config.add_target_include_path(&sublib_include_path);

        {
            let mut data = CppBuildConfigData::new();
            data.add_linked_lib(crate_name);
            for &lib in lib_dependencies(crate_name)? {
                data.add_linked_lib(lib);
            }
            // TODO: why shared for moqt?
            data.set_library_type(CppLibraryType::Shared);
            config
                .cpp_build_config_mut()
                .add(target::Condition::True, data);
        }
        {
            let mut data = CppBuildConfigData::new();
            data.add_compiler_flag("-fPIC");
            data.add_compiler_flag("-std=gnu++11");
            config
                .cpp_build_config_mut()
                .add(target::Condition::Env(target::Env::Msvc).negate(), data);
        }
        if target::current_env() == target::Env::Msvc {
            config.add_cpp_parser_argument("-std=c++14");
        } else {
            config.add_cpp_parser_argument("-std=gnu++11");
        }

        let template_path =
            PathBuf::from(env::var(MOQT_TEMPLATE_DIR_ENV_VAR_NAME).with_context(|_| {
                format_err!("{} env var is missing", MOQT_TEMPLATE_DIR_ENV_VAR_NAME)
            })?);
        config.set_crate_template_path(template_path.join(&crate_name));

        let steps = config.processing_steps_mut();
        let crate_name_clone = crate_name.to_string();
        steps.add_after(&["cpp_parser"], "qt_doc_parser", move |data| {
            parse_docs(data, &crate_name_clone, &Path::new("."))
        })?;

        config
    } else {
        crate_properties.remove_default_build_dependencies();
        crate_properties.add_build_dependency(
            "qt_ritual_build",
            CrateDependencySource::Local {
                path: repo_dir_path("qt_ritual_build")?,
            },
        )?;

        let mut config = Config::new(crate_properties);

        let qt_config = get_full_build_config(crate_name, qmake_path)?;
        config.set_cpp_build_config(qt_config.cpp_build_config);
        config.set_cpp_build_paths(qt_config.cpp_build_paths);

        config.add_target_include_path(&qt_config.installation_data.lib_include_path);
        config.set_cpp_lib_version(qt_config.installation_data.qt_version.as_str());
        // TODO: does parsing work on MacOS without adding "-F"?

        config.add_include_directive(&lib_folder_name(crate_name));

        // TODO: allow to override parser flags
        config.add_cpp_parser_arguments(vec!["-fPIC", "-fcxx-exceptions"]);

        if target::current_env() == target::Env::Msvc {
            config.add_cpp_parser_argument("-std=c++14");
        } else {
            config.add_cpp_parser_argument("-std=gnu++11");
        }
        //config.add_cpp_parser_blocked_name(CppName::from_one_part("qt_check_for_QGADGET_macro"));

        let steps = config.processing_steps_mut();
        let crate_name_clone = crate_name.to_string();
        let docs_path = qt_config.installation_data.docs_path.clone();

        steps.add_after(&["cpp_parser"], "qt_doc_parser", move |data| {
            parse_docs(data, &crate_name_clone, &docs_path)
        })?;

        config
            .set_crate_template_path(repo_dir_path("qt_ritual/crate_templates")?.join(&crate_name));

        config
    };

    config.add_after_cpp_parser_hook(detect_signals_and_slots);

    let steps = config.processing_steps_mut();
    for cpp_parser_stage in &["cpp_parser", "cpp_parser_stage2"] {
        steps.add_after(&[cpp_parser_stage], "add_slot_wrappers", add_slot_wrappers)?;
    }

    steps.add_after(
        &["rust_generator"],
        "set_crate_root_doc",
        set_crate_root_doc,
    )?;

    let lib_config = match crate_name {
        "qt_core" => core_config,
        "qt_gui" => gui_config,
        "qt_widgets" => widgets_config,
        "qt_3d_core" => core_3d_config,
        "qt_3d_render" => render_3d_config,
        "qt_3d_input" => input_3d_config,
        "qt_3d_logic" => logic_3d_config,
        "qt_3d_extras" => extras_3d_config,
        "qt_ui_tools" => empty_config,
        "qt_charts" => charts_config,
        "qt_qml" => empty_config,
        "moqt_core" => core_config,
        "moqt_gui" => gui_config,
        _ => bail!("Unknown crate name: {}", crate_name),
    };
    lib_config(&mut config)?;

    Ok(config)
}

pub fn global_config() -> GlobalConfig {
    let mut config = GlobalConfig::new();
    config.set_all_crate_names(all_crate_names().iter().map(|s| s.to_string()).collect());
    config.set_create_config_hook(|crate_name| create_config(crate_name, None));
    config
}
<commit_msg>fix fPIC parser argument<commit_after>//! Generator configurations specific for each Qt module.

use crate::detect_signals_and_slots::detect_signals_and_slots;
use crate::doc_parser::{parse_docs, set_crate_root_doc};
use crate::slot_wrappers::add_slot_wrappers;
use log::info;
use qt_ritual_common::{all_crate_names, get_full_build_config, lib_dependencies, lib_folder_name};
use ritual::config::{Config, CrateDependencyKind, GlobalConfig};
use ritual::config::{CrateDependencySource, CrateProperties};
use ritual_common::cpp_build_config::CppLibraryType;
use ritual_common::cpp_build_config::{CppBuildConfigData, CppBuildPaths};
use ritual_common::errors::{bail, format_err, Result, ResultExt};
use ritual_common::file_utils::repo_dir_path;
use ritual_common::target;
use ritual_common::toml;
use std::path::{Path, PathBuf};

mod _3d;
mod charts;
mod core;
mod gui;
mod widgets;

use self::_3d::{
    core_3d_config, extras_3d_config, input_3d_config, logic_3d_config, render_3d_config,
};
use self::{charts::charts_config, core::core_config, gui::gui_config, widgets::widgets_config};
use std::env;

pub const MOQT_INSTALL_DIR_ENV_VAR_NAME: &str = "MOQT_INSTALL_DIR";
pub const MOQT_TEMPLATE_DIR_ENV_VAR_NAME: &str = "MOQT_TEMPLATE_DIR";

/// Version of all generated Qt crates
const QT_OUTPUT_CRATES_VERSION: &str = "0.4.0";

fn empty_config(_config: &mut Config) -> Result<()> {
    Ok(())
}

/// Executes the generator for a single Qt module with given configuration.
pub fn create_config(crate_name: &str, qmake_path: Option<&str>) -> Result<Config> {
    info!("Preparing generator config for crate: {}", crate_name);
    let mut crate_properties = CrateProperties::new(crate_name, QT_OUTPUT_CRATES_VERSION);
    let mut custom_fields = toml::value::Table::new();
    let mut package_data = toml::value::Table::new();
    package_data.insert(
        "authors".to_string(),
        toml::Value::Array(vec![toml::Value::String(
            "Pavel Strakhov <ri@idzaaus.org>".to_string(),
        )]),
    );
    let description = format!("Bindings for {} C++ library", lib_folder_name(crate_name));
    package_data.insert("description".to_string(), toml::Value::String(description));
    let doc_url = format!("https://rust-qt.github.io/rustdoc/qt/{}", &crate_name);
    package_data.insert("documentation".to_string(), toml::Value::String(doc_url));
    package_data.insert(
        "repository".to_string(),
        toml::Value::String("https://github.com/rust-qt/ritual".to_string()),
    );
    package_data.insert(
        "license".to_string(),
        toml::Value::String("MIT OR Apache-2.0".to_string()),
    );
    package_data.insert(
        "keywords".to_string(),
        toml::Value::Array(vec![
            toml::Value::String("gui".to_string()),
            toml::Value::String("ffi".to_string()),
            toml::Value::String("qt".to_string()),
            toml::Value::String("ritual".to_string()),
        ]),
    );
    package_data.insert(
        "categories".to_string(),
        toml::Value::Array(vec![
            toml::Value::String("external-ffi-bindings".to_string()),
            toml::Value::String("gui".to_string()),
        ]),
    );

    custom_fields.insert("package".to_string(), toml::Value::Table(package_data));
    crate_properties.set_custom_fields(custom_fields);

    for &dependency in lib_dependencies(crate_name)? {
        crate_properties.add_dependency(
            dependency,
            CrateDependencyKind::Ritual,
            CrateDependencySource::CurrentWorkspace,
        )?;
    }

    let mut config = if crate_name.starts_with("moqt_") {
        let mut config = Config::new(crate_properties);
        let moqt_path =
            PathBuf::from(env::var(MOQT_INSTALL_DIR_ENV_VAR_NAME).with_context(|_| {
                format_err!("{} env var is missing", MOQT_INSTALL_DIR_ENV_VAR_NAME)
            })?);

        config.add_include_directive(format!("{}.h", crate_name));
        let include_path = moqt_path.join("include");
        if !include_path.exists() {
            bail!("Path does not exist: {}", include_path.display());
        }
        let lib_path = moqt_path.join("lib");
        if !lib_path.exists() {
            bail!("Path does not exist: {}", lib_path.display());
        }
        let sublib_include_path = include_path.join(crate_name);
        if !sublib_include_path.exists() {
            bail!("Path does not exist: {}", sublib_include_path.display());
        }
        {
            let mut paths = CppBuildPaths::new();
            paths.add_include_path(&sublib_include_path);
            paths.add_lib_path(&lib_path);

            for &lib in lib_dependencies(crate_name)? {
                let dep_include_path = include_path.join(lib);
                if !dep_include_path.exists() {
                    bail!("Path does not exist: {}", dep_include_path.display());
                }
                paths.add_include_path(&dep_include_path);
            }
            config.set_cpp_build_paths(paths);
        }
        config.add_target_include_path(&sublib_include_path);

        {
            let mut data = CppBuildConfigData::new();
            data.add_linked_lib(crate_name);
            for &lib in lib_dependencies(crate_name)? {
                data.add_linked_lib(lib);
            }
            // TODO: why shared for moqt?
            data.set_library_type(CppLibraryType::Shared);
            config
                .cpp_build_config_mut()
                .add(target::Condition::True, data);
        }
        {
            let mut data = CppBuildConfigData::new();
            data.add_compiler_flag("-fPIC");
            data.add_compiler_flag("-std=gnu++11");
            config
                .cpp_build_config_mut()
                .add(target::Condition::Env(target::Env::Msvc).negate(), data);
        }

        let template_path =
            PathBuf::from(env::var(MOQT_TEMPLATE_DIR_ENV_VAR_NAME).with_context(|_| {
                format_err!("{} env var is missing", MOQT_TEMPLATE_DIR_ENV_VAR_NAME)
            })?);
        config.set_crate_template_path(template_path.join(&crate_name));

        let steps = config.processing_steps_mut();
        let crate_name_clone = crate_name.to_string();
        steps.add_after(&["cpp_parser"], "qt_doc_parser", move |data| {
            parse_docs(data, &crate_name_clone, &Path::new("."))
        })?;

        config
    } else {
        crate_properties.remove_default_build_dependencies();
        crate_properties.add_build_dependency(
            "qt_ritual_build",
            CrateDependencySource::Local {
                path: repo_dir_path("qt_ritual_build")?,
            },
        )?;

        let mut config = Config::new(crate_properties);

        let qt_config = get_full_build_config(crate_name, qmake_path)?;
        config.set_cpp_build_config(qt_config.cpp_build_config);
        config.set_cpp_build_paths(qt_config.cpp_build_paths);

        config.add_target_include_path(&qt_config.installation_data.lib_include_path);
        config.set_cpp_lib_version(qt_config.installation_data.qt_version.as_str());
        // TODO: does parsing work on MacOS without adding "-F"?

        config.add_include_directive(&lib_folder_name(crate_name));

        // TODO: allow to override parser flags
        if target::current_os() != target::OS::Windows {
            config.add_cpp_parser_argument("-fPIC");
        }
        config.add_cpp_parser_argument("-fcxx-exceptions");

        let steps = config.processing_steps_mut();
        let crate_name_clone = crate_name.to_string();
        let docs_path = qt_config.installation_data.docs_path.clone();

        steps.add_after(&["cpp_parser"], "qt_doc_parser", move |data| {
            parse_docs(data, &crate_name_clone, &docs_path)
        })?;

        config
            .set_crate_template_path(repo_dir_path("qt_ritual/crate_templates")?.join(&crate_name));

        config
    };

    if target::current_env() == target::Env::Msvc {
        config.add_cpp_parser_argument("-std=c++14");
    } else {
        config.add_cpp_parser_argument("-std=gnu++11");
    }

    config.add_after_cpp_parser_hook(detect_signals_and_slots);

    let steps = config.processing_steps_mut();
    for cpp_parser_stage in &["cpp_parser", "cpp_parser_stage2"] {
        steps.add_after(&[cpp_parser_stage], "add_slot_wrappers", add_slot_wrappers)?;
    }

    steps.add_after(
        &["rust_generator"],
        "set_crate_root_doc",
        set_crate_root_doc,
    )?;

    let lib_config = match crate_name {
        "qt_core" => core_config,
        "qt_gui" => gui_config,
        "qt_widgets" => widgets_config,
        "qt_3d_core" => core_3d_config,
        "qt_3d_render" => render_3d_config,
        "qt_3d_input" => input_3d_config,
        "qt_3d_logic" => logic_3d_config,
        "qt_3d_extras" => extras_3d_config,
        "qt_ui_tools" => empty_config,
        "qt_charts" => charts_config,
        "qt_qml" => empty_config,
        "moqt_core" => core_config,
        "moqt_gui" => gui_config,
        _ => bail!("Unknown crate name: {}", crate_name),
    };
    lib_config(&mut config)?;

    Ok(config)
}

pub fn global_config() -> GlobalConfig {
    let mut config = GlobalConfig::new();
    config.set_all_crate_names(all_crate_names().iter().map(|s| s.to_string()).collect());
    config.set_create_config_hook(|crate_name| create_config(crate_name, None));
    config
}
<|endoftext|>


<commit_before>extern crate xml;

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::BufReader;

use xml::reader::{EventReader, XmlEvent};
use xml::attribute::OwnedAttribute;

#[derive(Debug, Clone, Default)]
struct LineAttributes {
    top: i32,
    left: i32,
    height: i32,
}

fn read_attributes(attr_list: &Vec<OwnedAttribute>) -> LineAttributes {
    let mut attributes : LineAttributes = Default::default();

    for attr in attr_list {
        match attr.name.local_name.as_ref() {
            "top" => attributes.top = attr.value.parse().unwrap(),
            "left" => attributes.left = attr.value.parse().unwrap(),
            "height" => attributes.height = attr.value.parse().unwrap(),
            _ => {}
        }
    }

    attributes
}

#[derive(Debug, Clone, Default)]
struct ScriptProperties {
    direction_position: i32,
    dialog_position: i32,
    speaker_direction_position: i32,
    speaker_position: i32,
    intra_paragraph_line_height: i32,
}

fn read_and_analyze_script(reader: Box<Read>) -> (ScriptProperties, Vec<(LineAttributes, String)>) {
    let mut script_properties: ScriptProperties = Default::default();
    let mut lines: Vec<(LineAttributes, String)> = Vec::new();

    // these maps are used to heuristically determine the ScriptProperties
    let mut position_uses: HashMap<i32, u32> = HashMap::new();
    let mut line_height_uses: HashMap<i32, u32> = HashMap::new();

    // states for the streaming xml parsing
    let mut current_line_attributes: LineAttributes = Default::default();
    let mut current_text_buffer = String::new();
    let mut last_line_height = 0;

    let parser = EventReader::new(reader);
    for event in parser {
        match event {
            Ok(XmlEvent::StartElement { name, attributes, .. }) => {
                match name.local_name.as_ref() {
                    "text" => {
                        current_line_attributes = read_attributes(&attributes);

                        current_text_buffer.clear();

                        // increase the count for the current left margin
                        *position_uses.entry(current_line_attributes.left)
                                      .or_insert(0) += 1;

                        // increase the count for the current top position diff
                        *line_height_uses.entry(current_line_attributes.top - last_line_height)
                                         .or_insert(0) += 1;
                        last_line_height = current_line_attributes.top;
                    }
                    "page" => {
                        last_line_height = 0;
                    }
                    _ => {}
                }
            }
            Ok(XmlEvent::Characters(text)) => {
                if current_text_buffer.len() > 0 {
                    current_text_buffer.push(' ');
                }
                current_text_buffer.push_str(text.trim());
            }
            Ok(XmlEvent::EndElement { name, .. }) => {
                if name.local_name == "text" {
                    lines.push((current_line_attributes.clone(), current_text_buffer.clone()));
                }
            }
            Err(e) => panic!(e),
            _ => {}
        }
    }

    // the position_uses map should at least have 3 different entries
    if position_uses.len() < 3 {
        panic!("Script uses strange layout");
    }

    // copy the position uses map into a vector and sort by value desc
    let mut position_uses_vec = position_uses.into_iter().collect::<Vec<_>>();
    position_uses_vec.sort_by(|&a, &b| b.1.cmp(&a.1));

    // sort the top 3 entries by position again (right-exclusive range)
    position_uses_vec[0..3].sort_by(|&a, &b| b.0.cmp(&a.0));

    script_properties.speaker_position = position_uses_vec[0].0;
    script_properties.dialog_position = position_uses_vec[1].0;
    script_properties.direction_position = position_uses_vec[2].0;

    // take the next value between speaker and dialog as speaker_direction
    for &value in position_uses_vec.iter().skip(3) {
        let (position, _) = value;
        if position > script_properties.dialog_position &&
           position < script_properties.speaker_position {
            script_properties.speaker_direction_position = position;
            break;
        }
    }

    // use to most used line diff value to determine the specific sections
    let mut last_uses: u32 = 0;
    for (&line_height, &uses) in line_height_uses.iter() {
        if uses > last_uses {
            script_properties.intra_paragraph_line_height = line_height;
            last_uses = uses;
        }
    }

    (script_properties, lines)
}

fn condense_script_demo(properties: ScriptProperties, lines: &Vec<(LineAttributes, String)>) {
    let mut last_left_position = 0;
    let mut last_top_position = 0;

    for line in lines.iter() {
        let &(ref attributes, ref line) = line;

        if line.len() == 0 {
            continue;
        }

        // start new section
        if last_left_position != attributes.left ||
           attributes.top - last_top_position > 18 ||
           attributes.top - last_top_position < 0 {
            println!("");

            match attributes.left {
                k if (k == properties.direction_position)
                    => print!("Regie:     "),
                k if (k == properties.dialog_position)
                    => print!("Dialog:         "),
                k if (k == properties.speaker_direction_position)
                    => print!("Dialoganweisung:  "),
                k if (k == properties.speaker_position)
                    => print!("Sprecher:            "),
                _   => print!("------- "),
            }
        }

        last_left_position = attributes.left;
        last_top_position = attributes.top;

        print!("{} ", line);
    }
}

fn main() {
    let xml_file_name = env::args().skip(1).next().expect("No input file given");

    let file_reader = File::open(xml_file_name).expect("Could not open file");
    let buffered_file_reader = Box::new(BufReader::new(file_reader));

    let (properties, lines) = read_and_analyze_script(buffered_file_reader);

    condense_script_demo(properties, &lines);
}
<commit_msg>Make it explicit that we ignore other events<commit_after>extern crate xml;

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::BufReader;

use xml::reader::{EventReader, XmlEvent};
use xml::attribute::OwnedAttribute;

#[derive(Debug, Clone, Default)]
struct LineAttributes {
    top: i32,
    left: i32,
    height: i32,
}

fn read_attributes(attr_list: &Vec<OwnedAttribute>) -> LineAttributes {
    let mut attributes : LineAttributes = Default::default();

    for attr in attr_list {
        match attr.name.local_name.as_ref() {
            "top" => attributes.top = attr.value.parse().unwrap(),
            "left" => attributes.left = attr.value.parse().unwrap(),
            "height" => attributes.height = attr.value.parse().unwrap(),
            _ => {}
        }
    }

    attributes
}

#[derive(Debug, Clone, Default)]
struct ScriptProperties {
    direction_position: i32,
    dialog_position: i32,
    speaker_direction_position: i32,
    speaker_position: i32,
    intra_paragraph_line_height: i32,
}

fn read_and_analyze_script(reader: Box<Read>) -> (ScriptProperties, Vec<(LineAttributes, String)>) {
    let mut script_properties: ScriptProperties = Default::default();
    let mut lines: Vec<(LineAttributes, String)> = Vec::new();

    // these maps are used to heuristically determine the ScriptProperties
    let mut position_uses: HashMap<i32, u32> = HashMap::new();
    let mut line_height_uses: HashMap<i32, u32> = HashMap::new();

    // states for the streaming xml parsing
    let mut current_line_attributes: LineAttributes = Default::default();
    let mut current_text_buffer = String::new();
    let mut last_line_height = 0;

    let parser = EventReader::new(reader);
    for event in parser {
        match event {
            Ok(XmlEvent::StartElement { name, attributes, .. }) => {
                match name.local_name.as_ref() {
                    "text" => {
                        current_line_attributes = read_attributes(&attributes);

                        current_text_buffer.clear();

                        // increase the count for the current left margin
                        *position_uses.entry(current_line_attributes.left)
                                      .or_insert(0) += 1;

                        // increase the count for the current top position diff
                        *line_height_uses.entry(current_line_attributes.top - last_line_height)
                                         .or_insert(0) += 1;
                        last_line_height = current_line_attributes.top;
                    }
                    "page" => {
                        last_line_height = 0;
                    }
                    _ => {}
                }
            }
            Ok(XmlEvent::Characters(text)) => {
                if current_text_buffer.len() > 0 {
                    current_text_buffer.push(' ');
                }
                current_text_buffer.push_str(text.trim());
            }
            Ok(XmlEvent::EndElement { name, .. }) => {
                if name.local_name == "text" {
                    lines.push((current_line_attributes.clone(), current_text_buffer.clone()));
                }
            }
            Ok(_) => {},
            Err(e) => panic!(e),
        }
    }

    // the position_uses map should at least have 3 different entries
    if position_uses.len() < 3 {
        panic!("Script uses strange layout");
    }

    // copy the position uses map into a vector and sort by value desc
    let mut position_uses_vec = position_uses.into_iter().collect::<Vec<_>>();
    position_uses_vec.sort_by(|&a, &b| b.1.cmp(&a.1));

    // sort the top 3 entries by position again (right-exclusive range)
    position_uses_vec[0..3].sort_by(|&a, &b| b.0.cmp(&a.0));

    script_properties.speaker_position = position_uses_vec[0].0;
    script_properties.dialog_position = position_uses_vec[1].0;
    script_properties.direction_position = position_uses_vec[2].0;

    // take the next value between speaker and dialog as speaker_direction
    for &value in position_uses_vec.iter().skip(3) {
        let (position, _) = value;
        if position > script_properties.dialog_position &&
           position < script_properties.speaker_position {
            script_properties.speaker_direction_position = position;
            break;
        }
    }

    // use to most used line diff value to determine the specific sections
    let mut last_uses: u32 = 0;
    for (&line_height, &uses) in line_height_uses.iter() {
        if uses > last_uses {
            script_properties.intra_paragraph_line_height = line_height;
            last_uses = uses;
        }
    }

    (script_properties, lines)
}

fn condense_script_demo(properties: ScriptProperties, lines: &Vec<(LineAttributes, String)>) {
    let mut last_left_position = 0;
    let mut last_top_position = 0;

    for line in lines.iter() {
        let &(ref attributes, ref line) = line;

        if line.len() == 0 {
            continue;
        }

        // start new section
        if last_left_position != attributes.left ||
           attributes.top - last_top_position > 18 ||
           attributes.top - last_top_position < 0 {
            println!("");

            match attributes.left {
                k if (k == properties.direction_position)
                    => print!("Regie:     "),
                k if (k == properties.dialog_position)
                    => print!("Dialog:         "),
                k if (k == properties.speaker_direction_position)
                    => print!("Dialoganweisung:  "),
                k if (k == properties.speaker_position)
                    => print!("Sprecher:            "),
                _   => print!("------- "),
            }
        }

        last_left_position = attributes.left;
        last_top_position = attributes.top;

        print!("{} ", line);
    }
}

fn main() {
    let xml_file_name = env::args().skip(1).next().expect("No input file given");

    let file_reader = File::open(xml_file_name).expect("Could not open file");
    let buffered_file_reader = Box::new(BufReader::new(file_reader));

    let (properties, lines) = read_and_analyze_script(buffered_file_reader);

    condense_script_demo(properties, &lines);
}
<|endoftext|>