<commit_before>use std::fs::File;
use std::io::prelude::*;

const SIZE: usize = 300;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("11.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

fn coord_to_index((x, y): (usize, usize)) -> usize {
    (x - 1) + (y - 1) * SIZE
}

fn get_largest_fixed_square(partial_sums: &Vec<isize>, square: usize) -> Option<((usize, usize), isize)> {
    (1..SIZE + 2 - square)
    .flat_map(|x| (1..SIZE + 2 - square).map(move |y| (x, y)))
    .map(|(x, y)| (
        (x, y),
        [
            (x + square - 1, y + square - 1),
            (x + square - 1, y - 1),
            (x - 1, y - 1),
            (x - 1, y + square - 1)
        ]
        .iter()
        .cloned()
        .enumerate()
        .filter(|&(_, (x, y))| 1 <= x && x <= SIZE && 1 <= y && y <= SIZE)
        .map(|(i, coord)| (i, coord_to_index(coord)))
        .map(|(i, j)| (-1isize).pow(i as u32) * partial_sums.get(j).cloned().unwrap_or(0))
        .sum::<isize>()
    ))
    .max_by_key(|&(_, power)| power)
}

fn main() {
    let serial_number = get_input().unwrap().trim().parse::<usize>().unwrap();
    let fuel_cells: Vec<isize> = (1..SIZE + 1)
        .flat_map(|y| (1..SIZE + 1).map(move |x| (x, y)))
        .map(|(x, y)| {
            let rack_id = x + 10;
            ((rack_id * y + serial_number) * rack_id % 1000) as isize / 100 - 5
        })
        .collect();

    let partial_sums: Vec<isize> = (1..SIZE + 1)
        .flat_map(|y| (1..SIZE + 1).map(move |x| (x, y)))
        .map(|(x, y)| {
            (1..y + 1)
            .flat_map(|b| (1..x + 1).map(move |a| coord_to_index((a, b))))
            .map(|i| fuel_cells.get(i).cloned().unwrap())
            .sum::<isize>()
        })
        .collect();

    get_largest_fixed_square(&partial_sums, 3)
    .map(|((x, y), _)| {
        println!("Part 1: {},{}", x, y)
    });

    (1..SIZE + 1)
    .filter_map(|square_size| {
        get_largest_fixed_square(&partial_sums, square_size)
        .map(|x| (x, square_size))
    })
    .max_by_key(|&((_, power), _)| power)
    .map(|(((x, y), _), square_size)| {
        println!("Part 2: {},{},{}", x, y, square_size)
    });
}
<commit_msg>O(n^2) algorithm for generating partial sums vector<commit_after>use std::fs::File;
use std::io::prelude::*;

const SIZE: usize = 300;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("11.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

fn coord_to_index((x, y): (usize, usize)) -> usize {
    (x - 1) + (y - 1) * SIZE
}

fn get_largest_fixed_square(partial_sums: &Vec<isize>, square: usize) -> Option<((usize, usize), isize)> {
    (1..SIZE + 2 - square)
    .flat_map(|x| (1..SIZE + 2 - square).map(move |y| (x, y)))
    .map(|(x, y)| (
        (x, y),
        [
            (x + square - 1, y + square - 1),
            (x + square - 1, y - 1),
            (x - 1, y - 1),
            (x - 1, y + square - 1)
        ]
        .iter()
        .cloned()
        .enumerate()
        .filter(|&(_, (x, y))| 1 <= x && x <= SIZE && 1 <= y && y <= SIZE)
        .map(|(i, coord)| (i, coord_to_index(coord)))
        .map(|(i, j)| (-1isize).pow(i as u32) * partial_sums.get(j).cloned().unwrap_or(0))
        .sum::<isize>()
    ))
    .max_by_key(|&(_, power)| power)
}

fn main() {
    let serial_number = get_input().unwrap().trim().parse::<usize>().unwrap();
    let fuel_cells: Vec<isize> = (1..SIZE + 1)
        .flat_map(|y| (1..SIZE + 1).map(move |x| (x, y)))
        .map(|(x, y)| {
            let rack_id = x + 10;
            ((rack_id * y + serial_number) * rack_id % 1000) as isize / 100 - 5
        })
        .collect();

    let mut partial_sums = Vec::new();

    for (i, power) in fuel_cells.iter().cloned().enumerate() {
        let x = i % SIZE;
        let sum = power
            + partial_sums.get(i - SIZE).cloned().unwrap_or(0)
            + if x > 0 {
                partial_sums.get(i - 1).cloned().unwrap_or(0)
                - partial_sums.get(i - SIZE - 1).cloned().unwrap_or(0)
            } else {
                0
            };

        partial_sums.push(sum);
    }

    get_largest_fixed_square(&partial_sums, 3)
    .map(|((x, y), _)| {
        println!("Part 1: {},{}", x, y)
    });

    (1..SIZE + 1)
    .filter_map(|square_size| {
        get_largest_fixed_square(&partial_sums, square_size)
        .map(|x| (x, square_size))
    })
    .max_by_key(|&((_, power), _)| power)
    .map(|(((x, y), _), square_size)| {
        println!("Part 2: {},{},{}", x, y, square_size)
    });
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::hash::Hash;
use std::collections::{HashMap, VecDeque};

#[derive(Debug, Copy, Clone, PartialEq)]
enum PortalType {
  Inner,
  Outer
}

#[derive(Debug, Copy, Clone)]
enum Tile<P> {
  Wall,
  Passage,
  Portal(P, PortalType)
}

type Position = (usize, usize);

#[derive(Debug)]
struct Labyrinth<N: Hash + Eq, P: Hash + Eq> {
  tiles: HashMap<N, Tile<P>>,
  portals: HashMap<P, (N, N)>,
  entrance: Option<N>,
  goal: Option<N>,
}

trait HasNeighbors<N: Hash + Eq, P: Hash + Eq> {
  fn neighbors(&self, labyrinth: &Labyrinth<N, P>) -> Vec<Self> where Self: Sized;
}

impl<P: Hash + Eq + Clone> HasNeighbors<Position, P> for Position {
  fn neighbors(&self, labyrinth: &Labyrinth<Position, P>) -> Vec<Self> {
    let &(x, y) = self;
    let portal = match labyrinth.tiles.get(self) {
      Some(Tile::Portal(p, _)) => Some(p.clone()),
      _ => None
    };

    let mut result = vec![(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
      .filter(|&(x, y)| x > 0 && y > 0)
      .filter(|pos| match labyrinth.tiles.get(pos) {
        Some(Tile::Passage) | Some(Tile::Portal(_, _)) => true,
        _ => false
      })
      .collect::<Vec<_>>();

    if let Some(p) = portal {
      let (pos1, pos2) = labyrinth.portals.get(&p).cloned().unwrap();

      if pos1 == *self {
        result.push(pos2);
      } else {
        result.push(pos1);
      }
    }

    result
  }
}

impl<P: Hash + Eq + Clone> HasNeighbors<Position, P> for (Position, usize) {
  fn neighbors(&self, labyrinth: &Labyrinth<Position, P>) -> Vec<Self> {
    let &((x, y), level) = self;
    let (portal, portal_type) = match labyrinth.tiles.get(&(x, y)) {
      Some(Tile::Portal(p, t)) => (Some(p.clone()), Some(t.clone())),
      _ => (None, None)
    };

    let mut result = vec![(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
      .filter(|&(x, y)| x > 0 && y > 0)
      .filter(|pos| match labyrinth.tiles.get(pos) {
        Some(Tile::Passage) | Some(Tile::Portal(_, _)) => true,
        _ => false
      })
      .map(|pos| (pos, level))
      .collect::<Vec<_>>();

    if let (Some(p), Some(portal_type)) = (portal, portal_type) {
      if level > 0 && portal_type == PortalType::Outer || portal_type == PortalType::Inner {
        let (pos1, pos2) = labyrinth.portals.get(&p).cloned().unwrap();

        result.push((
          if pos1 == (x, y) { pos2 } else { pos1 },
          if portal_type == PortalType::Outer { level - 1 } else { level + 1 }
        ));
      }
    }

    result
  }
}

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("20.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn parse_labyrinth(input: &str) -> Labyrinth<Position, String> {
  let height = input.lines().count();
  let width = input.lines().next().map(|row| row.len()).unwrap_or(0);
  let mut character_map = HashMap::new();
  let mut tiles = HashMap::new();
  let mut portals = HashMap::<String, Vec<Position>>::new();
  let mut entrance = None;
  let mut goal = None;

  for (y, line) in input.lines().enumerate() {
    for (x, c) in line.chars().enumerate() {
      character_map.insert((x, y), c);
    }
  }

  for (&(x, y), &c) in character_map.iter() {
    tiles.insert((x, y), match c {
      '#' => Tile::Wall,
      '.' => {
        let get_label_positions = |(x, y), d| (
          (x - 3 + d, y), // Left
          (x + d, y),     // Right
          (x, y - 3 + d), // Top
          (x, y + d)      // Bottom
        );

        let mut label_char_iter = [1, 2].into_iter().cloned()
          .map(|d| get_label_positions((x as isize, y as isize), d));

        let (portal_type, mut label) = match (label_char_iter.next(), label_char_iter.next()) {
          (Some((l11, l21, l31, l41)), Some((l12, l22, l32, l42))) => {
            [(l11, l12), (l21, l22), (l31, l32), (l41, l42)]
            .into_iter()
            .cloned()
            .map(|((a, b), (c, d))| ((a as usize, b as usize), (c as usize, d as usize)))
            .enumerate()
            .filter_map(|(i, (pos1, pos2))| match (character_map.get(&pos1), character_map.get(&pos2)) {
              (Some(&c1), Some(&c2)) => Some((i, (c1, c2))),
              _ => None
            })
            .filter(|&(_, (c1, c2))| c1.is_ascii_alphabetic() && c2.is_ascii_alphabetic())
            .map(|(i, (c1, c2))| {
              let mut label = String::new();
              label.push(c1);
              label.push(c2);

              let portal_type = match i {
                0 => {
                  // Left
                  if x < width / 2 {
                    PortalType::Outer
                  } else {
                    PortalType::Inner
                  }
                },
                1 => {
                  // Right
                  if x < width / 2 {
                    PortalType::Inner
                  } else {
                    PortalType::Outer
                  }
                },
                2 => {
                  // Top
                  if y < height / 2 {
                    PortalType::Outer
                  } else {
                    PortalType::Inner
                  }
                },
                3 => {
                  // Bottom
                  if y < height / 2 {
                    PortalType::Inner
                  } else {
                    PortalType::Outer
                  }
                },
                _ => panic!()
              };

              (Some(portal_type), Some(label))
            })
            .next()
            .unwrap_or((None, None))
          },
          _ => (None, None)
        };

        match label.as_ref().map(|s| &s[..]) {
          Some("ZZ") => {
            label = None;
            goal = Some((x, y));
          },
          Some("AA") => {
            label = None;
            entrance = Some((x, y));
          },
          _ => {}
        }

        if let (Some(portal_type), Some(label)) = (portal_type, label) {
          match portals.get_mut(&label) {
            Some(positions) => {
              positions.push((x, y));
            },
            None => {
              portals.insert(label.clone(), vec![(x, y)]);
            }
          }

          Tile::Portal(label, portal_type)
        } else {
          Tile::Passage
        }
      }
      _ => continue
    });
  }

  let portals = portals.into_iter()
    .filter(|(_, positions)| positions.len() == 2)
    .map(|(key, positions)| (key, (positions[0], positions[1])))
    .collect();

  Labyrinth {
    tiles,
    portals,
    entrance,
    goal
  }
}

fn shortest_path<P, T>(
  labyrinth: &Labyrinth<Position, P>,
  start: T,
  target: T
) -> Option<Vec<T>>
where
  P: Hash + Eq + Clone,
  T: Hash + Eq + Copy + HasNeighbors<Position, P>
{
  let mut queue = VecDeque::new();
  let mut previous_map = HashMap::new();

  queue.push_back(start);
  previous_map.insert(start, None);

  while let Some(position) = queue.pop_front() {
    if position == target {
      break;
    }

    for neighbor in position.neighbors(labyrinth) {
      if previous_map.contains_key(&neighbor) {
        continue;
      }

      queue.push_back(neighbor);
      previous_map.insert(neighbor, Some(position));
    }
  }

  let mut path = vec![target];

  while let Some(&Some(previous)) = previous_map.get(path.last().unwrap()) {
    path.push(previous);
  }

  path.reverse();

  if path[0] == start {
    Some(path)
  } else {
    None
  }
}

fn main() {
  let input = get_input().unwrap();
  let labyrinth = parse_labyrinth(&input);
  let entrance = labyrinth.entrance.unwrap();
  let goal = labyrinth.goal.unwrap();

  let path = shortest_path(&labyrinth, entrance, goal).unwrap();
  println!("Part 1: {}", path.len() - 1);

  let path = shortest_path(&labyrinth, (entrance, 0), (goal, 0)).unwrap();
  println!("Part 2: {}", path.len() - 1);
}
<commit_msg>Use Graph trait<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::hash::Hash;
use std::collections::HashMap;

mod graph;
use graph::Graph;

#[derive(Debug, Copy, Clone, PartialEq)]
enum PortalType {
  Inner,
  Outer
}

#[derive(Debug, Copy, Clone)]
enum Tile<P> {
  Wall,
  Passage,
  Portal(P, PortalType)
}

type Position = (usize, usize);

#[derive(Debug)]
struct Labyrinth<N: Hash + Eq, P: Hash + Eq> {
  tiles: HashMap<N, Tile<P>>,
  portals: HashMap<P, (N, N)>,
  entrance: Option<N>,
  goal: Option<N>,
}

impl<P: Hash + Eq + Clone> Graph<Position> for Labyrinth<Position, P> {
  fn get_neighbors(&self, (x, y): Position) -> Vec<Position> {
    let portal = match self.tiles.get(&(x, y)) {
      Some(Tile::Portal(p, _)) => Some(p.clone()),
      _ => None
    };

    let mut result = vec![(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
      .filter(|&(x, y)| x > 0 && y > 0)
      .filter(|pos| match self.tiles.get(pos) {
        Some(Tile::Passage) | Some(Tile::Portal(_, _)) => true,
        _ => false
      })
      .collect::<Vec<_>>();

    if let Some(p) = portal {
      let (pos1, pos2) = self.portals.get(&p).cloned().unwrap();

      if pos1 == (x, y) {
        result.push(pos2);
      } else {
        result.push(pos1);
      }
    }

    result
  }
}

impl<P: Hash + Eq + Clone> Graph<(Position, usize)> for Labyrinth<Position, P> {
  fn get_neighbors(&self, ((x, y), level): (Position, usize)) -> Vec<(Position, usize)> {
    let (portal, portal_type) = match self.tiles.get(&(x, y)) {
      Some(Tile::Portal(p, t)) => (Some(p.clone()), Some(t.clone())),
      _ => (None, None)
    };

    let mut result = vec![(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)].into_iter()
      .filter(|&(x, y)| x > 0 && y > 0)
      .filter(|pos| match self.tiles.get(pos) {
        Some(Tile::Passage) | Some(Tile::Portal(_, _)) => true,
        _ => false
      })
      .map(|pos| (pos, level))
      .collect::<Vec<_>>();

    if let (Some(p), Some(portal_type)) = (portal, portal_type) {
      if level > 0 && portal_type == PortalType::Outer || portal_type == PortalType::Inner {
        let (pos1, pos2) = self.portals.get(&p).cloned().unwrap();

        result.push((
          if pos1 == (x, y) { pos2 } else { pos1 },
          if portal_type == PortalType::Outer { level - 1 } else { level + 1 }
        ));
      }
    }

    result
  }
}

fn get_input() -> std::io::Result<String> {
  let mut file = File::open("20.txt")?;
  let mut contents = String::new();
  file.read_to_string(&mut contents)?;

  Ok(contents)
}

fn parse_labyrinth(input: &str) -> Labyrinth<Position, String> {
  let height = input.lines().count();
  let width = input.lines().next().map(|row| row.len()).unwrap_or(0);
  let mut character_map = HashMap::new();
  let mut tiles = HashMap::new();
  let mut portals = HashMap::<String, Vec<Position>>::new();
  let mut entrance = None;
  let mut goal = None;

  for (y, line) in input.lines().enumerate() {
    for (x, c) in line.chars().enumerate() {
      character_map.insert((x, y), c);
    }
  }

  for (&(x, y), &c) in character_map.iter() {
    tiles.insert((x, y), match c {
      '#' => Tile::Wall,
      '.' => {
        let get_label_positions = |(x, y), d| (
          (x - 3 + d, y), // Left
          (x + d, y),     // Right
          (x, y - 3 + d), // Top
          (x, y + d)      // Bottom
        );

        let mut label_char_iter = [1, 2].into_iter().cloned()
          .map(|d| get_label_positions((x as isize, y as isize), d));

        let (portal_type, mut label) = match (label_char_iter.next(), label_char_iter.next()) {
          (Some((l11, l21, l31, l41)), Some((l12, l22, l32, l42))) => {
            [(l11, l12), (l21, l22), (l31, l32), (l41, l42)]
            .into_iter()
            .cloned()
            .map(|((a, b), (c, d))| ((a as usize, b as usize), (c as usize, d as usize)))
            .enumerate()
            .filter_map(|(i, (pos1, pos2))| match (character_map.get(&pos1), character_map.get(&pos2)) {
              (Some(&c1), Some(&c2)) => Some((i, (c1, c2))),
              _ => None
            })
            .filter(|&(_, (c1, c2))| c1.is_ascii_alphabetic() && c2.is_ascii_alphabetic())
            .map(|(i, (c1, c2))| {
              let mut label = String::new();
              label.push(c1);
              label.push(c2);

              let portal_type = match i {
                0 => {
                  // Left
                  if x < width / 2 {
                    PortalType::Outer
                  } else {
                    PortalType::Inner
                  }
                },
                1 => {
                  // Right
                  if x < width / 2 {
                    PortalType::Inner
                  } else {
                    PortalType::Outer
                  }
                },
                2 => {
                  // Top
                  if y < height / 2 {
                    PortalType::Outer
                  } else {
                    PortalType::Inner
                  }
                },
                3 => {
                  // Bottom
                  if y < height / 2 {
                    PortalType::Inner
                  } else {
                    PortalType::Outer
                  }
                },
                _ => panic!()
              };

              (Some(portal_type), Some(label))
            })
            .next()
            .unwrap_or((None, None))
          },
          _ => (None, None)
        };

        match label.as_ref().map(|s| &s[..]) {
          Some("ZZ") => {
            label = None;
            goal = Some((x, y));
          },
          Some("AA") => {
            label = None;
            entrance = Some((x, y));
          },
          _ => {}
        }

        if let (Some(portal_type), Some(label)) = (portal_type, label) {
          match portals.get_mut(&label) {
            Some(positions) => {
              positions.push((x, y));
            },
            None => {
              portals.insert(label.clone(), vec![(x, y)]);
            }
          }

          Tile::Portal(label, portal_type)
        } else {
          Tile::Passage
        }
      }
      _ => continue
    });
  }

  let portals = portals.into_iter()
    .filter(|(_, positions)| positions.len() == 2)
    .map(|(key, positions)| (key, (positions[0], positions[1])))
    .collect();

  Labyrinth {
    tiles,
    portals,
    entrance,
    goal
  }
}

fn main() {
  let input = get_input().unwrap();
  let labyrinth = parse_labyrinth(&input);
  let entrance = labyrinth.entrance.unwrap();
  let goal = labyrinth.goal.unwrap();

  let path = labyrinth.bfs(entrance).construct_path(goal).unwrap();
  println!("Part 1: {}", path.len() - 1);

  let path = labyrinth.bfs((entrance, 0)).construct_path((goal, 0)).unwrap();
  println!("Part 2: {}", path.len() - 1);
}
<|endoftext|>


<commit_before>use std::{env, marker::PhantomData};

use super::Module;
use crate::{powerline::Segment, terminal::Color, R};

pub struct ExitCode<S: ExitCodeScheme> {
	scheme: PhantomData<S>,
}

pub trait ExitCodeScheme {
	const EXIT_CODE_BG: Color;
	const EXIT_CODE_FG: Color;
}

impl<S: ExitCodeScheme> ExitCode<S> {
	pub fn new() -> ExitCode<S> {
		ExitCode { scheme: PhantomData }
	}
}

impl<S: ExitCodeScheme> Module for ExitCode<S> {
	fn append_segments(&mut self, segments: &mut Vec<Segment>) -> R<()> {
		let exit_code = env::args().nth(1).unwrap_or("1".to_string());
		if exit_code != "0" {
			let (fg, bg) = (S::EXIT_CODE_FG, S::EXIT_CODE_BG);
			segments.push(Segment::simple(format!(" {} ", exit_code), fg, bg));
		}

		Ok(())
	}
}
<commit_msg>remove useless alloc<commit_after>use std::{env, marker::PhantomData};

use super::Module;
use crate::{powerline::Segment, terminal::Color, R};

pub struct ExitCode<S: ExitCodeScheme> {
	scheme: PhantomData<S>,
}

pub trait ExitCodeScheme {
	const EXIT_CODE_BG: Color;
	const EXIT_CODE_FG: Color;
}

impl<S: ExitCodeScheme> ExitCode<S> {
	pub fn new() -> ExitCode<S> {
		ExitCode { scheme: PhantomData }
	}
}

impl<S: ExitCodeScheme> Module for ExitCode<S> {
	fn append_segments(&mut self, segments: &mut Vec<Segment>) -> R<()> {
		let exit_code = env::args().nth(1).unwrap_or_else(|| "1".to_string());

		if exit_code != "0" {
			let (fg, bg) = (S::EXIT_CODE_FG, S::EXIT_CODE_BG);
			segments.push(Segment::simple(format!(" {} ", exit_code), fg, bg));
		}

		Ok(())
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(bad_style)]

macro_rules! cfg_if {
    ( $( if #[cfg( $meta:meta )] { $($it1:item)* } else { $($it2:item)* } )* ) =>
        ( $( $( #[cfg($meta)] $it1)* $( #[cfg(not($meta))] $it2)* )* )
}

use libc::{c_int, c_void, uintptr_t};

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum _Unwind_Reason_Code {
    _URC_NO_REASON = 0,
    _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
    _URC_FATAL_PHASE2_ERROR = 2,
    _URC_FATAL_PHASE1_ERROR = 3,
    _URC_NORMAL_STOP = 4,
    _URC_END_OF_STACK = 5,
    _URC_HANDLER_FOUND = 6,
    _URC_INSTALL_CONTEXT = 7,
    _URC_CONTINUE_UNWIND = 8,
    _URC_FAILURE = 9, // used only by ARM EHABI
}
pub use self::_Unwind_Reason_Code::*;

pub type _Unwind_Exception_Class = u64;
pub type _Unwind_Word = uintptr_t;
pub type _Unwind_Ptr = uintptr_t;
pub type _Unwind_Trace_Fn = extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void)
                                          -> _Unwind_Reason_Code;
#[cfg(target_arch = "x86")]
pub const unwinder_private_data_size: usize = 5;

#[cfg(target_arch = "x86_64")]
pub const unwinder_private_data_size: usize = 6;

#[cfg(all(target_arch = "arm", not(target_os = "ios")))]
pub const unwinder_private_data_size: usize = 20;

#[cfg(all(target_arch = "arm", target_os = "ios"))]
pub const unwinder_private_data_size: usize = 5;

#[cfg(target_arch = "aarch64")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "mips")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "mips64")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "s390x")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "sparc64")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_os = "emscripten")]
pub const unwinder_private_data_size: usize = 20;

#[repr(C)]
pub struct _Unwind_Exception {
    pub exception_class: _Unwind_Exception_Class,
    pub exception_cleanup: _Unwind_Exception_Cleanup_Fn,
    pub private: [_Unwind_Word; unwinder_private_data_size],
}

pub enum _Unwind_Context {}

pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code,
                                                      exception: *mut _Unwind_Exception);
extern "C" {
    #[unwind]
    pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
    pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
    pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void;
    pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
    pub fn _Unwind_GetTextRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
    pub fn _Unwind_GetDataRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
}

cfg_if! {
if #[cfg(not(any(all(target_os = "android", target_arch = "arm"),
                 all(target_os = "linux", target_arch = "arm"))))] {
    // Not ARM EHABI
    #[repr(C)]
    #[derive(Copy, Clone, PartialEq)]
    pub enum _Unwind_Action {
        _UA_SEARCH_PHASE = 1,
        _UA_CLEANUP_PHASE = 2,
        _UA_HANDLER_FRAME = 4,
        _UA_FORCE_UNWIND = 8,
        _UA_END_OF_STACK = 16,
    }
    pub use self::_Unwind_Action::*;

    extern "C" {
        pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word;
        pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word);
        pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> _Unwind_Word;
        pub fn _Unwind_SetIP(ctx: *mut _Unwind_Context, value: _Unwind_Word);
        pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, ip_before_insn: *mut c_int)
                                 -> _Unwind_Word;
        pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
    }

} else {
    // ARM EHABI
    #[repr(C)]
    #[derive(Copy, Clone, PartialEq)]
    pub enum _Unwind_State {
        _US_VIRTUAL_UNWIND_FRAME = 0,
        _US_UNWIND_FRAME_STARTING = 1,
        _US_UNWIND_FRAME_RESUME = 2,
        _US_ACTION_MASK = 3,
        _US_FORCE_UNWIND = 8,
        _US_END_OF_STACK = 16,
    }
    pub use self::_Unwind_State::*;

    #[repr(C)]
    enum _Unwind_VRS_Result {
        _UVRSR_OK = 0,
        _UVRSR_NOT_IMPLEMENTED = 1,
        _UVRSR_FAILED = 2,
    }
    #[repr(C)]
    enum _Unwind_VRS_RegClass {
        _UVRSC_CORE = 0,
        _UVRSC_VFP = 1,
        _UVRSC_FPA = 2,
        _UVRSC_WMMXD = 3,
        _UVRSC_WMMXC = 4,
    }
    use self::_Unwind_VRS_RegClass::*;
    #[repr(C)]
    enum _Unwind_VRS_DataRepresentation {
        _UVRSD_UINT32 = 0,
        _UVRSD_VFPX = 1,
        _UVRSD_FPAX = 2,
        _UVRSD_UINT64 = 3,
        _UVRSD_FLOAT = 4,
        _UVRSD_DOUBLE = 5,
    }
    use self::_Unwind_VRS_DataRepresentation::*;

    pub const UNWIND_POINTER_REG: c_int = 12;
    pub const UNWIND_IP_REG: c_int = 15;

    extern "C" {
        fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
                           regclass: _Unwind_VRS_RegClass,
                           regno: _Unwind_Word,
                           repr: _Unwind_VRS_DataRepresentation,
                           data: *mut c_void)
                           -> _Unwind_VRS_Result;

        fn _Unwind_VRS_Set(ctx: *mut _Unwind_Context,
                           regclass: _Unwind_VRS_RegClass,
                           regno: _Unwind_Word,
                           repr: _Unwind_VRS_DataRepresentation,
                           data: *mut c_void)
                           -> _Unwind_VRS_Result;
    }

    // On Android or ARM/Linux, these are implemented as macros:

    pub unsafe fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word {
        let mut val: _Unwind_Word = 0;
        _Unwind_VRS_Get(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32,
                        &mut val as *mut _ as *mut c_void);
        val
    }

    pub unsafe fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word) {
        let mut value = value;
        _Unwind_VRS_Set(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32,
                        &mut value as *mut _ as *mut c_void);
    }

    pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context)
                                -> _Unwind_Word {
        let val = _Unwind_GetGR(ctx, UNWIND_IP_REG);
        (val & !1) as _Unwind_Word
    }

    pub unsafe fn _Unwind_SetIP(ctx: *mut _Unwind_Context,
                                value: _Unwind_Word) {
        // Propagate thumb bit to instruction pointer
        let thumb_state = _Unwind_GetGR(ctx, UNWIND_IP_REG) & 1;
        let value = value | thumb_state;
        _Unwind_SetGR(ctx, UNWIND_IP_REG, value);
    }

    pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
                                    ip_before_insn: *mut c_int)
                                    -> _Unwind_Word {
        *ip_before_insn = 0;
        _Unwind_GetIP(ctx)
    }

    // This function also doesn't exist on Android or ARM/Linux, so make it a no-op
    pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
        pc
    }
}

if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
    // Not 32-bit iOS
    extern "C" {
        #[unwind]
        pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
        pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
                                 trace_argument: *mut c_void)
                                 -> _Unwind_Reason_Code;
    }
} else {
    // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace()
    extern "C" {
        #[unwind]
        pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
    }

    #[inline]
    pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code {
        _Unwind_SjLj_RaiseException(exc)
    }
}
} // cfg_if!
<commit_msg>Auto merge of #47571 - FenrirWolf:libunwind, r=alexcrichton<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(bad_style)]

macro_rules! cfg_if {
    ( $( if #[cfg( $meta:meta )] { $($it1:item)* } else { $($it2:item)* } )* ) =>
        ( $( $( #[cfg($meta)] $it1)* $( #[cfg(not($meta))] $it2)* )* )
}

use libc::{c_int, c_void, uintptr_t};

#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum _Unwind_Reason_Code {
    _URC_NO_REASON = 0,
    _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
    _URC_FATAL_PHASE2_ERROR = 2,
    _URC_FATAL_PHASE1_ERROR = 3,
    _URC_NORMAL_STOP = 4,
    _URC_END_OF_STACK = 5,
    _URC_HANDLER_FOUND = 6,
    _URC_INSTALL_CONTEXT = 7,
    _URC_CONTINUE_UNWIND = 8,
    _URC_FAILURE = 9, // used only by ARM EHABI
}
pub use self::_Unwind_Reason_Code::*;

pub type _Unwind_Exception_Class = u64;
pub type _Unwind_Word = uintptr_t;
pub type _Unwind_Ptr = uintptr_t;
pub type _Unwind_Trace_Fn = extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void)
                                          -> _Unwind_Reason_Code;
#[cfg(target_arch = "x86")]
pub const unwinder_private_data_size: usize = 5;

#[cfg(target_arch = "x86_64")]
pub const unwinder_private_data_size: usize = 6;

#[cfg(all(target_arch = "arm", not(target_os = "ios")))]
pub const unwinder_private_data_size: usize = 20;

#[cfg(all(target_arch = "arm", target_os = "ios"))]
pub const unwinder_private_data_size: usize = 5;

#[cfg(target_arch = "aarch64")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "mips")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "mips64")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "s390x")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_arch = "sparc64")]
pub const unwinder_private_data_size: usize = 2;

#[cfg(target_os = "emscripten")]
pub const unwinder_private_data_size: usize = 20;

#[repr(C)]
pub struct _Unwind_Exception {
    pub exception_class: _Unwind_Exception_Class,
    pub exception_cleanup: _Unwind_Exception_Cleanup_Fn,
    pub private: [_Unwind_Word; unwinder_private_data_size],
}

pub enum _Unwind_Context {}

pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code,
                                                      exception: *mut _Unwind_Exception);
extern "C" {
    #[unwind]
    pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
    pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
    pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void;
    pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
    pub fn _Unwind_GetTextRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
    pub fn _Unwind_GetDataRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
}

cfg_if! {
if #[cfg(all(any(target_os = "ios", not(target_arch = "arm"))))] {
    // Not ARM EHABI
    #[repr(C)]
    #[derive(Copy, Clone, PartialEq)]
    pub enum _Unwind_Action {
        _UA_SEARCH_PHASE = 1,
        _UA_CLEANUP_PHASE = 2,
        _UA_HANDLER_FRAME = 4,
        _UA_FORCE_UNWIND = 8,
        _UA_END_OF_STACK = 16,
    }
    pub use self::_Unwind_Action::*;

    extern "C" {
        pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word;
        pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word);
        pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> _Unwind_Word;
        pub fn _Unwind_SetIP(ctx: *mut _Unwind_Context, value: _Unwind_Word);
        pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, ip_before_insn: *mut c_int)
                                 -> _Unwind_Word;
        pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void;
    }

} else {
    // ARM EHABI
    #[repr(C)]
    #[derive(Copy, Clone, PartialEq)]
    pub enum _Unwind_State {
        _US_VIRTUAL_UNWIND_FRAME = 0,
        _US_UNWIND_FRAME_STARTING = 1,
        _US_UNWIND_FRAME_RESUME = 2,
        _US_ACTION_MASK = 3,
        _US_FORCE_UNWIND = 8,
        _US_END_OF_STACK = 16,
    }
    pub use self::_Unwind_State::*;

    #[repr(C)]
    enum _Unwind_VRS_Result {
        _UVRSR_OK = 0,
        _UVRSR_NOT_IMPLEMENTED = 1,
        _UVRSR_FAILED = 2,
    }
    #[repr(C)]
    enum _Unwind_VRS_RegClass {
        _UVRSC_CORE = 0,
        _UVRSC_VFP = 1,
        _UVRSC_FPA = 2,
        _UVRSC_WMMXD = 3,
        _UVRSC_WMMXC = 4,
    }
    use self::_Unwind_VRS_RegClass::*;
    #[repr(C)]
    enum _Unwind_VRS_DataRepresentation {
        _UVRSD_UINT32 = 0,
        _UVRSD_VFPX = 1,
        _UVRSD_FPAX = 2,
        _UVRSD_UINT64 = 3,
        _UVRSD_FLOAT = 4,
        _UVRSD_DOUBLE = 5,
    }
    use self::_Unwind_VRS_DataRepresentation::*;

    pub const UNWIND_POINTER_REG: c_int = 12;
    pub const UNWIND_IP_REG: c_int = 15;

    extern "C" {
        fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context,
                           regclass: _Unwind_VRS_RegClass,
                           regno: _Unwind_Word,
                           repr: _Unwind_VRS_DataRepresentation,
                           data: *mut c_void)
                           -> _Unwind_VRS_Result;

        fn _Unwind_VRS_Set(ctx: *mut _Unwind_Context,
                           regclass: _Unwind_VRS_RegClass,
                           regno: _Unwind_Word,
                           repr: _Unwind_VRS_DataRepresentation,
                           data: *mut c_void)
                           -> _Unwind_VRS_Result;
    }

    // On Android or ARM/Linux, these are implemented as macros:

    pub unsafe fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word {
        let mut val: _Unwind_Word = 0;
        _Unwind_VRS_Get(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32,
                        &mut val as *mut _ as *mut c_void);
        val
    }

    pub unsafe fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word) {
        let mut value = value;
        _Unwind_VRS_Set(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32,
                        &mut value as *mut _ as *mut c_void);
    }

    pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context)
                                -> _Unwind_Word {
        let val = _Unwind_GetGR(ctx, UNWIND_IP_REG);
        (val & !1) as _Unwind_Word
    }

    pub unsafe fn _Unwind_SetIP(ctx: *mut _Unwind_Context,
                                value: _Unwind_Word) {
        // Propagate thumb bit to instruction pointer
        let thumb_state = _Unwind_GetGR(ctx, UNWIND_IP_REG) & 1;
        let value = value | thumb_state;
        _Unwind_SetGR(ctx, UNWIND_IP_REG, value);
    }

    pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context,
                                    ip_before_insn: *mut c_int)
                                    -> _Unwind_Word {
        *ip_before_insn = 0;
        _Unwind_GetIP(ctx)
    }

    // This function also doesn't exist on Android or ARM/Linux, so make it a no-op
    pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void {
        pc
    }
}

if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
    // Not 32-bit iOS
    extern "C" {
        #[unwind]
        pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
        pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
                                 trace_argument: *mut c_void)
                                 -> _Unwind_Reason_Code;
    }
} else {
    // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace()
    extern "C" {
        #[unwind]
        pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
    }

    #[inline]
    pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code {
        _Unwind_SjLj_RaiseException(exc)
    }
}
} // cfg_if!
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;

const SIZE: usize = 300;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("11.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

fn coord_to_index((x, y): (usize, usize)) -> usize {
    (x - 1) + (y - 1) * SIZE
}

fn get_largest_fixed_square(partial_sums: &Vec<isize>, square: usize) -> Option<((usize, usize), isize)> {
    (1..SIZE + 2 - square)
    .flat_map(|x| (1..SIZE + 2 - square).map(move |y| (x, y)))
    .map(|(x, y)| (
        (x, y),
        [
            (x + square - 1, y + square - 1),
            (x + square - 1, y - 1),
            (x - 1, y - 1),
            (x - 1, y + square - 1)
        ]
        .iter()
        .cloned()
        .enumerate()
        .filter(|&(_, (x, y))| 1 <= x && x <= SIZE && 1 <= y && y <= SIZE)
        .map(|(i, coord)| (i, coord_to_index(coord)))
        .map(|(i, j)| (-1isize).pow(i as u32) * partial_sums.get(j).cloned().unwrap_or(0))
        .sum::<isize>()
    ))
    .max_by_key(|&(_, power)| power)
}

fn main() {
    let serial_number = get_input().unwrap().trim().parse::<usize>().unwrap();
    let fuel_cells: Vec<isize> = (1..SIZE + 1)
        .flat_map(|y| (1..SIZE + 1).map(move |x| (x, y)))
        .map(|(x, y)| {
            let rack_id = x + 10;
            ((rack_id * y + serial_number) * rack_id % 1000) as isize / 100 - 5
        })
        .collect();

    let mut partial_sums = Vec::new();

    for (i, power) in fuel_cells.iter().cloned().enumerate() {
        let x = i % SIZE;
        let sum = power
            + partial_sums.get(i - SIZE).cloned().unwrap_or(0)
            + if x > 0 {
                partial_sums.get(i - 1).cloned().unwrap_or(0)
                - partial_sums.get(i - SIZE - 1).cloned().unwrap_or(0)
            } else {
                0
            };

        partial_sums.push(sum);
    }

    get_largest_fixed_square(&partial_sums, 3)
    .map(|((x, y), _)| {
        println!("Part 1: {},{}", x, y)
    });

    (1..SIZE + 1)
    .filter_map(|square_size| {
        get_largest_fixed_square(&partial_sums, square_size)
        .map(|x| (x, square_size))
    })
    .max_by_key(|&((_, power), _)| power)
    .map(|(((x, y), _), square_size)| {
        println!("Part 2: {},{},{}", x, y, square_size)
    });
}
<commit_msg>Fix panic<commit_after>use std::fs::File;
use std::io::prelude::*;

const SIZE: usize = 300;

fn get_input() -> std::io::Result<String> {
    let mut file = File::open("11.txt")?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;

    Ok(contents)
}

fn coord_to_index((x, y): (usize, usize)) -> usize {
    (x - 1) + (y - 1) * SIZE
}

fn get_largest_fixed_square(partial_sums: &Vec<isize>, square: usize) -> Option<((usize, usize), isize)> {
    (1..SIZE + 2 - square)
    .flat_map(|x| (1..SIZE + 2 - square).map(move |y| (x, y)))
    .map(|(x, y)| (
        (x, y),
        [
            (x + square - 1, y + square - 1),
            (x + square - 1, y - 1),
            (x - 1, y - 1),
            (x - 1, y + square - 1)
        ]
        .iter()
        .cloned()
        .enumerate()
        .filter(|&(_, (x, y))| 1 <= x && x <= SIZE && 1 <= y && y <= SIZE)
        .map(|(i, coord)| (i, coord_to_index(coord)))
        .map(|(i, j)| (-1isize).pow(i as u32) * partial_sums.get(j).cloned().unwrap_or(0))
        .sum::<isize>()
    ))
    .max_by_key(|&(_, power)| power)
}

fn main() {
    let serial_number = get_input().unwrap().trim().parse::<usize>().unwrap();
    let fuel_cells: Vec<isize> = (1..SIZE + 1)
        .flat_map(|y| (1..SIZE + 1).map(move |x| (x, y)))
        .map(|(x, y)| {
            let rack_id = x + 10;
            ((rack_id * y + serial_number) * rack_id % 1000) as isize / 100 - 5
        })
        .collect();

    let mut partial_sums = Vec::new();

    for (i, power) in fuel_cells.iter().cloned().enumerate() {
        let x = i % SIZE;
        let sum = power
            + if i >= SIZE { partial_sums[i - SIZE] } else { 0 }
            + if x > 0 { partial_sums[i - 1] } else { 0 }
            - if x > 0 && i >= SIZE + 1 { partial_sums[i - SIZE - 1] } else { 0 };

        partial_sums.push(sum);
    }

    get_largest_fixed_square(&partial_sums, 3)
    .map(|((x, y), _)| {
        println!("Part 1: {},{}", x, y)
    });

    (1..SIZE + 1)
    .filter_map(|square_size| {
        get_largest_fixed_square(&partial_sums, square_size)
        .map(|x| (x, square_size))
    })
    .max_by_key(|&((_, power), _)| power)
    .map(|(((x, y), _), square_size)| {
        println!("Part 2: {},{},{}", x, y, square_size)
    });
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::collections::BTreeMap;
use std::process::exit;
use std::io::Write;
use std::io::stdout;
use std::path::PathBuf;
use std::fs::OpenOptions;

use vobject::vcard::Vcard;
use vobject::write_component;
use toml_query::read::TomlValueReadExt;
use toml::Value;
use uuid::Uuid;

use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagerror::trace::trace_error;
use libimagerror::trace::trace_error_exit;
use libimagutil::warn_result::WarnResult;
use libimagentryref::refstore::RefStore;
use libimagentryref::flags::RefFlags;

const TEMPLATE : &'static str = include_str!("../static/new-contact-template.toml");

#[cfg(test)]
mod test {
    use toml::Value;
    use super::TEMPLATE;

    const TEMPLATE_WITH_DATA : &'static str = include_str!("../static/new-contact-template-test.toml");

    #[test]
    fn test_validity_template_toml() {
        let _ : Value = ::toml::de::from_str(TEMPLATE).unwrap();
    }

    #[test]
    fn test_validity_template_toml_without_comments() {
        let _ : Value = ::toml::de::from_str(TEMPLATE_WITH_DATA).unwrap();
    }
}

macro_rules! ask_continue {
    () => {
        if ::libimaginteraction::ask::ask_bool("Edit tempfile", Some(true)) {
            continue;
        } else {
            exit(1);
        }
    };
}

pub fn create(rt: &Runtime) {
    let scmd         = rt.cli().subcommand_matches("create").unwrap();
    let mut template = String::from(TEMPLATE);

    let (mut dest, location) : (Box<Write>, Option<PathBuf>) = {
        if let Some(mut fl) = scmd.value_of("file-location").map(PathBuf::from) {
            if fl.is_file() {
                error!("File does exist, cannot create/override");
                exit(1);
            } else if fl.is_dir() {
                fl.set_file_name(Uuid::new_v4().hyphenated().to_string());
            }

            let file = OpenOptions::new()
                .write(true)
                .create_new(true)
                .open(fl.clone())
                .map_warn_err_str("Cannot create/open destination File. Stopping.")
                .map_err_trace_exit_unwrap(1);

            (Box::new(file), Some(fl))
        } else {
            (Box::new(stdout()), None)
        }
    };

    loop {
        ::libimagentryedit::edit::edit_in_tmpfile(&rt, &mut template)
            .map_warn_err_str("Editing failed.")
            .map_err_trace_exit_unwrap(1);

        if template == TEMPLATE || template.is_empty() {
            error!("No (changed) content in tempfile. Not doing anything.");
            exit(2);
        }

        match ::toml::de::from_str(&template) {
            Err(e) => {
                error!("Error parsing template");
                trace_error(&e);
                ask_continue!();
            },

            Ok(toml) => {
                debug!("");
                let mut vcard = Vcard::default();

                { // parse name
                    debug!("Parsing name");
                    let firstname = read_str_from_toml(&toml, "name.first");
                    trace!("firstname = {:?}", firstname);

                    let lastname  = read_str_from_toml(&toml, "name.last");
                    trace!("lastname = {:?}", lastname);

                    vcard = vcard.with_name(parameters!(),
                        read_str_from_toml(&toml, "name.prefix"),
                        firstname.clone(),
                        read_str_from_toml(&toml, "name.additional"),
                        lastname.clone(),
                        read_str_from_toml(&toml, "name.suffix"));

                    if let (Some(first), Some(last)) = (firstname, lastname) {
                        trace!("Building fullname: '{} {}'", first, last);
                        vcard = vcard.with_fullname(format!("{} {}", first, last));
                    }
                }

                { // parse nicknames
                    debug!("Parsing nicknames");
                    match toml.read("nickname").map_err_trace_exit_unwrap(1) {
                        Some(&Value::Array(ref ary)) => {
                            for (i, element) in ary.iter().enumerate() {
                                let nicktype = match read_str_from_toml(element, "type") {
                                    Some(p) => {
                                        let mut m = BTreeMap::new();
                                        m.insert("TYPE".into(), p);
                                        m
                                    },
                                    None    => BTreeMap::new(),
                                };

                                let name = match read_str_from_toml(element, "name") {
                                    None => {
                                        error!("Key 'nickname.[{}].name' missing", i);
                                        ask_continue!()
                                    },
                                    Some(p) => p,
                                };

                                trace!("nick type = {:?}", nicktype);
                                trace!("name      = {:?}", name);

                                vcard = vcard.with_nickname(nicktype, name);
                            }
                        },

                        Some(&Value::String(ref name)) => {
                            vcard = vcard.with_nickname(parameters!(), name.clone());
                        }

                        Some(_) => {
                            error!("Type Error: Expected Array or String at 'nickname'");
                            ask_continue!();
                        },
                        None => {
                            // nothing
                        },
                    }
                }

                { // parse organisation
                    debug!("Parsing organisation");

                    if let Some(orgs) = read_strary_from_toml(&toml, "organisation.name") {
                        trace!("orgs = {:?}", orgs);
                        vcard = vcard.with_org(orgs);
                    } else {
                        error!("Key 'organisation.name' missing");
                        ask_continue!();
                    }

                    if let Some(title) = read_str_from_toml(&toml, "organisation.title") {
                        trace!("title = {:?}", title);
                        vcard = vcard.with_title(title);
                    }

                    if let Some(role) = read_str_from_toml(&toml, "organisation.role") {
                        trace!("role = {:?}", role);
                        vcard = vcard.with_role(role);
                    }
                }

                { // parse phone
                    debug!("Parse phone");
                    match toml.read("phone").map_err_trace_exit_unwrap(1) {
                        Some(&Value::Array(ref ary)) => {
                            for (i, element) in ary.iter().enumerate() {
                                let phonetype = match read_str_from_toml(element, "type") {
                                    Some(p) => p,
                                    None => {
                                        error!("Key 'phones.[{}].type' missing", i);
                                        ask_continue!()
                                    }
                                };

                                let number = match read_str_from_toml(element, "number") {
                                    Some(p) => p,
                                    None => {
                                        error!("Key 'phones.[{}].number' missing", i);
                                        ask_continue!()
                                    }
                                };

                                trace!("phonetype = {:?}", phonetype);
                                trace!("number    = {:?}", number);

                                vcard = vcard.with_tel(parameters!("TYPE" => phonetype), number);
                            }
                        },

                        Some(_) => {
                            error!("Expected Array at 'phones'.");
                            ask_continue!()
                        },
                        None => {
                            // nothing
                        },
                    }
                }

                { // parse address
                    debug!("Parsing address");
                    match toml.read("addresses").map_err_trace_exit_unwrap(1) {
                        Some(&Value::Array(ref ary)) => {
                            for (i, element) in ary.iter().enumerate() {
                                let adrtype  = match read_str_from_toml(element, "type") {
                                    None => {
                                        error!("Key 'adresses.[{}].type' missing", i);
                                        ask_continue!()
                                    },
                                    Some(p) => p,
                                };
                                trace!("adrtype = {:?}", adrtype);

                                let bx       = read_str_from_toml(element, "box");
                                let extended = read_str_from_toml(element, "extended");
                                let street   = read_str_from_toml(element, "street");
                                let code     = read_str_from_toml(element, "code");
                                let city     = read_str_from_toml(element, "city");
                                let region   = read_str_from_toml(element, "region");
                                let country  = read_str_from_toml(element, "country");

                                trace!("bx       = {:?}", bx);
                                trace!("extended = {:?}", extended);
                                trace!("street   = {:?}", street);
                                trace!("code     = {:?}", code);
                                trace!("city     = {:?}", city);
                                trace!("region   = {:?}", region);
                                trace!("country  = {:?}", country);

                                vcard = vcard.with_adr(
                                    parameters!("TYPE" => adrtype),
                                    bx, extended, street, code, city, region, country
                                );
                            }
                        },

                        Some(_) => {
                            error!("Type Error: Expected Array at 'addresses'");
                            ask_continue!();
                        },
                        None => {
                            // nothing
                        },
                    }
                }

                { // parse email
                    debug!("Parsing email");
                    match toml.read("email").map_err_trace_exit_unwrap(1) {
                        Some(&Value::Array(ref ary)) => {
                            for (i, element) in ary.iter().enumerate() {
                                let mailtype  = match read_str_from_toml(element, "type") {
                                    None => {
                                        error!("Error: 'email.[{}].type' missing", i);
                                        ask_continue!()
                                    },
                                    Some(p) => p,
                                }; // TODO: Unused, because unsupported by vobject

                                let mail = match read_str_from_toml(element, "addr") {
                                    None => {
                                        error!("Error: 'email.[{}].addr' missing", i);
                                        ask_continue!()
                                    },
                                    Some(p) => p,
                                };

                                trace!("mailtype = {:?} (UNUSED)", mailtype);
                                trace!("mail     = {:?}", mail);

                                vcard = vcard.with_email(mail);
                            }
                        },

                        Some(_) => {
                            error!("Type Error: Expected Array at 'email'");
                            ask_continue!();
                        },
                        None => {
                            // nothing
                        },
                    }
                }

                { // parse others
                    debug!("Parsing others");
                    if let Some(categories) = read_strary_from_toml(&toml, "others.categories") {
                        vcard = vcard.with_categories(categories);
                    } else {
                        debug!("No categories");
                    }

                    if let Some(webpage) = read_str_from_toml(&toml, "others.webpage") {
                        vcard = vcard.with_url(webpage);
                    } else {
                        debug!("No webpage");
                    }

                    if let Some(note) = read_str_from_toml(&toml, "others.note") {
                        vcard = vcard.with_note(note);
                    } else {
                        debug!("No note");
                    }

                }

                //println!("{:#?}", vcard);
                if template == TEMPLATE || template.is_empty() {
                    if ::libimaginteraction::ask::ask_bool("Abort contact creating", Some(false)) {
                        exit(1);
                    } else {
                        continue;
                    }
                }

                let vcard_string = write_component(&vcard);
                if let Err(e) = dest.write_all(&vcard_string.as_bytes()) {
                    warn!("Error while writing out vcard content");
                    trace_error_exit(&e, 1);
                }

                break;
            }
        }
    }

    if let Some(location) = location {
        if !scmd.is_present("dont-track") {
            let flags = RefFlags::default()
                .with_content_hashing(true)
                .with_permission_tracking(false);

            RefStore::create(rt.store(), location, flags)
                .map_err_trace_exit_unwrap(1);

            info!("Created entry in store");
        } else {
            info!("Not creating entry in store");
        }
    } else {
        info!("Cannot track stdout-created contact information");
    }

    info!("Ready");
}

fn read_strary_from_toml(toml: &Value, path: &'static str) -> Option<Vec<String>> {
    match toml.read(path).map_warn_err_str(&format!("Failed to read value at '{}'", path)) {
        Ok(Some(&Value::Array(ref vec))) => {
            let mut v = Vec::new();
            for elem in vec {
                match *elem {
                    Value::String(ref s) => v.push(s.clone()),
                    _ => {
                        error!("Type Error: '{}' must be Array<String>", path);
                        return None
                    },
                }
            }

            Some(v)
        }
        Ok(Some(&Value::String(ref s))) => {
            warn!("Having String, wanting Array<String> ... going to auto-fix");
            Some(vec![s.clone()])
        },
        Ok(Some(_)) => {
            error!("Type Error: '{}' must be Array<String>", path);
            None
        },
        Ok(None) => None,
        Err(_) => None,
    }
}

fn read_str_from_toml(toml: &Value, path: &'static str) -> Option<String> {
    let v = toml.read(path)
        .map_warn_err_str(&format!("Failed to read value at '{}'", path));

    match v {
        Ok(Some(&Value::String(ref s))) => Some(s.clone()),
        Ok(Some(_)) => {
            error!("Type Error: '{}' must be String", path);
            None
        },
        Ok(None) => {
            error!("Expected '{}' to be present, but is not.", path);
            None
        },
        Err(e) => {
            trace_error(&e);
            None
        }
    }
}

#[cfg(test)]
mod test_entry {
    // TODO
}

<commit_msg>Outsource functional part for testability<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::collections::BTreeMap;
use std::process::exit;
use std::io::Write;
use std::io::stdout;
use std::path::PathBuf;
use std::fs::OpenOptions;

use vobject::vcard::Vcard;
use vobject::write_component;
use toml_query::read::TomlValueReadExt;
use toml::Value;
use uuid::Uuid;

use libimagrt::runtime::Runtime;
use libimagerror::trace::MapErrTrace;
use libimagerror::trace::trace_error;
use libimagerror::trace::trace_error_exit;
use libimagutil::warn_result::WarnResult;
use libimagentryref::refstore::RefStore;
use libimagentryref::flags::RefFlags;

const TEMPLATE : &'static str = include_str!("../static/new-contact-template.toml");

#[cfg(test)]
mod test {
    use toml::Value;
    use super::TEMPLATE;

    const TEMPLATE_WITH_DATA : &'static str = include_str!("../static/new-contact-template-test.toml");

    #[test]
    fn test_validity_template_toml() {
        let _ : Value = ::toml::de::from_str(TEMPLATE).unwrap();
    }

    #[test]
    fn test_validity_template_toml_without_comments() {
        let _ : Value = ::toml::de::from_str(TEMPLATE_WITH_DATA).unwrap();
    }
}

macro_rules! ask_continue {
    { yes => $yes:expr; no => $no:expr } => {
        if ::libimaginteraction::ask::ask_bool("Edit tempfile", Some(true)) {
            $yes
        } else {
            $no
        }
    };
}

pub fn create(rt: &Runtime) {
    let scmd         = rt.cli().subcommand_matches("create").unwrap();
    let mut template = String::from(TEMPLATE);

    let (mut dest, location) : (Box<Write>, Option<PathBuf>) = {
        if let Some(mut fl) = scmd.value_of("file-location").map(PathBuf::from) {
            if fl.is_file() {
                error!("File does exist, cannot create/override");
                exit(1);
            } else if fl.is_dir() {
                fl.set_file_name(Uuid::new_v4().hyphenated().to_string());
            }

            let file = OpenOptions::new()
                .write(true)
                .create_new(true)
                .open(fl.clone())
                .map_warn_err_str("Cannot create/open destination File. Stopping.")
                .map_err_trace_exit_unwrap(1);

            (Box::new(file), Some(fl))
        } else {
            (Box::new(stdout()), None)
        }
    };

    loop {
        ::libimagentryedit::edit::edit_in_tmpfile(&rt, &mut template)
            .map_warn_err_str("Editing failed.")
            .map_err_trace_exit_unwrap(1);

        if template == TEMPLATE || template.is_empty() {
            error!("No (changed) content in tempfile. Not doing anything.");
            exit(2);
        }

        match ::toml::de::from_str(&template).map(parse_toml_into_vcard) {
            Err(e) => {
                error!("Error parsing template");
                trace_error(&e);
                ask_continue! { yes => continue; no => exit(1) };
            },

            Ok(None)        => continue,
            Ok(Some(vcard)) => {
                debug!("Parsed:\n{:#?}", vcard);

                if template == TEMPLATE || template.is_empty() {
                    if ::libimaginteraction::ask::ask_bool("Abort contact creating", Some(false)) {
                        exit(1);
                    } else {
                        continue;
                    }
                }

                let vcard_string = write_component(&vcard);
                if let Err(e) = dest.write_all(&vcard_string.as_bytes()) {
                    warn!("Error while writing out vcard content");
                    trace_error_exit(&e, 1);
                }

                break;
            }
        }
    }

    if let Some(location) = location {
        if !scmd.is_present("dont-track") {
            let flags = RefFlags::default()
                .with_content_hashing(true)
                .with_permission_tracking(false);

            RefStore::create(rt.store(), location, flags)
                .map_err_trace_exit_unwrap(1);

            info!("Created entry in store");
        } else {
            info!("Not creating entry in store");
        }
    } else {
        info!("Cannot track stdout-created contact information");
    }

    info!("Ready");
}

fn parse_toml_into_vcard(toml: Value) -> Option<Vcard> {
    let mut vcard = Vcard::default();

    { // parse name
        debug!("Parsing name");
        let firstname = read_str_from_toml(&toml, "name.first");
        trace!("firstname = {:?}", firstname);

        let lastname  = read_str_from_toml(&toml, "name.last");
        trace!("lastname = {:?}", lastname);

        vcard = vcard.with_name(parameters!(),
            read_str_from_toml(&toml, "name.prefix"),
            firstname.clone(),
            read_str_from_toml(&toml, "name.additional"),
            lastname.clone(),
            read_str_from_toml(&toml, "name.suffix"));

        if let (Some(first), Some(last)) = (firstname, lastname) {
            trace!("Building fullname: '{} {}'", first, last);
            vcard = vcard.with_fullname(format!("{} {}", first, last));
        }
    }

    { // parse nicknames
        debug!("Parsing nicknames");
        match toml.read("nickname").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let nicktype = match read_str_from_toml(element, "type") {
                        None    => BTreeMap::new(),
                        Some(p) => {
                            let mut m = BTreeMap::new();
                            m.insert("TYPE".into(), p);
                            m
                        },
                    };

                    let name = match read_str_from_toml(element, "name") {
                        Some(p) => p,
                        None    => {
                            error!("Key 'nickname.[{}].name' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                    };

                    trace!("nick type = {:?}", nicktype);
                    trace!("name      = {:?}", name);

                    vcard = vcard.with_nickname(nicktype, name);
                }
            },

            Some(&Value::String(ref name)) => {
                vcard = vcard.with_nickname(parameters!(), name.clone());
            }

            Some(_) => {
                error!("Type Error: Expected Array or String at 'nickname'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse organisation
        debug!("Parsing organisation");

        if let Some(orgs) = read_strary_from_toml(&toml, "organisation.name") {
            trace!("orgs = {:?}", orgs);
            vcard = vcard.with_org(orgs);
        } else {
            error!("Key 'organisation.name' missing");
            ask_continue! { yes => return None; no => exit(1) };
        }

        if let Some(title) = read_str_from_toml(&toml, "organisation.title") {
            trace!("title = {:?}", title);
            vcard = vcard.with_title(title);
        }

        if let Some(role) = read_str_from_toml(&toml, "organisation.role") {
            trace!("role = {:?}", role);
            vcard = vcard.with_role(role);
        }
    }

    { // parse phone
        debug!("Parse phone");
        match toml.read("phone").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let phonetype = match read_str_from_toml(element, "type") {
                        Some(p) => p,
                        None => {
                            error!("Key 'phones.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        }
                    };

                    let number = match read_str_from_toml(element, "number") {
                        Some(p) => p,
                        None => {
                            error!("Key 'phones.[{}].number' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        }
                    };

                    trace!("phonetype = {:?}", phonetype);
                    trace!("number    = {:?}", number);

                    vcard = vcard.with_tel(parameters!("TYPE" => phonetype), number);
                }
            },

            Some(_) => {
                error!("Expected Array at 'phones'.");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse address
        debug!("Parsing address");
        match toml.read("addresses").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let adrtype  = match read_str_from_toml(element, "type") {
                        None => {
                            error!("Key 'adresses.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    };
                    trace!("adrtype = {:?}", adrtype);

                    let bx       = read_str_from_toml(element, "box");
                    let extended = read_str_from_toml(element, "extended");
                    let street   = read_str_from_toml(element, "street");
                    let code     = read_str_from_toml(element, "code");
                    let city     = read_str_from_toml(element, "city");
                    let region   = read_str_from_toml(element, "region");
                    let country  = read_str_from_toml(element, "country");

                    trace!("bx       = {:?}", bx);
                    trace!("extended = {:?}", extended);
                    trace!("street   = {:?}", street);
                    trace!("code     = {:?}", code);
                    trace!("city     = {:?}", city);
                    trace!("region   = {:?}", region);
                    trace!("country  = {:?}", country);

                    vcard = vcard.with_adr(
                        parameters!("TYPE" => adrtype),
                        bx, extended, street, code, city, region, country
                    );
                }
            },

            Some(_) => {
                error!("Type Error: Expected Array at 'addresses'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse email
        debug!("Parsing email");
        match toml.read("email").map_err_trace_exit_unwrap(1) {
            Some(&Value::Array(ref ary)) => {
                for (i, element) in ary.iter().enumerate() {
                    let mailtype  = match read_str_from_toml(element, "type") {
                        None => {
                            error!("Error: 'email.[{}].type' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    }; // TODO: Unused, because unsupported by vobject

                    let mail = match read_str_from_toml(element, "addr") {
                        None => {
                            error!("Error: 'email.[{}].addr' missing", i);
                            ask_continue! { yes => return None; no => exit(1) };
                        },
                        Some(p) => p,
                    };

                    trace!("mailtype = {:?} (UNUSED)", mailtype);
                    trace!("mail     = {:?}", mail);

                    vcard = vcard.with_email(mail);
                }
            },

            Some(_) => {
                error!("Type Error: Expected Array at 'email'");
                ask_continue! { yes => return None; no => exit(1) };
            },
            None => {
                // nothing
            },
        }
    }

    { // parse others
        debug!("Parsing others");
        if let Some(categories) = read_strary_from_toml(&toml, "others.categories") {
            vcard = vcard.with_categories(categories);
        } else {
            debug!("No categories");
        }

        if let Some(webpage) = read_str_from_toml(&toml, "others.webpage") {
            vcard = vcard.with_url(webpage);
        } else {
            debug!("No webpage");
        }

        if let Some(note) = read_str_from_toml(&toml, "others.note") {
            vcard = vcard.with_note(note);
        } else {
            debug!("No note");
        }

    }

    Some(vcard)
}

fn read_strary_from_toml(toml: &Value, path: &'static str) -> Option<Vec<String>> {
    match toml.read(path).map_warn_err_str(&format!("Failed to read value at '{}'", path)) {
        Ok(Some(&Value::Array(ref vec))) => {
            let mut v = Vec::new();
            for elem in vec {
                match *elem {
                    Value::String(ref s) => v.push(s.clone()),
                    _ => {
                        error!("Type Error: '{}' must be Array<String>", path);
                        return None
                    },
                }
            }

            Some(v)
        }
        Ok(Some(&Value::String(ref s))) => {
            warn!("Having String, wanting Array<String> ... going to auto-fix");
            Some(vec![s.clone()])
        },
        Ok(Some(_)) => {
            error!("Type Error: '{}' must be Array<String>", path);
            None
        },
        Ok(None) => None,
        Err(_) => None,
    }
}

fn read_str_from_toml(toml: &Value, path: &'static str) -> Option<String> {
    let v = toml.read(path)
        .map_warn_err_str(&format!("Failed to read value at '{}'", path));

    match v {
        Ok(Some(&Value::String(ref s))) => Some(s.clone()),
        Ok(Some(_)) => {
            error!("Type Error: '{}' must be String", path);
            None
        },
        Ok(None) => {
            error!("Expected '{}' to be present, but is not.", path);
            None
        },
        Err(e) => {
            trace_error(&e);
            None
        }
    }
}

#[cfg(test)]
mod test_entry {
    // TODO
}

<|endoftext|>


<commit_before>//! Internal traits and functions used to implement parallel
//! iteration. These should be considered highly unstable: users of
//! parallel iterators should not need to interact with them directly.
//! See `README.md` for a high-level overview.

use join;
use super::IndexedParallelIterator;
use super::len::*;
use thread_pool::get_registry;

pub trait ProducerCallback<ITEM> {
    type Output;
    fn callback<P>(self, producer: P) -> Self::Output where P: Producer<Item = ITEM>;
}

/// A producer which will produce a fixed number of items N. This is
/// not queryable through the API; the consumer is expected to track
/// it.
pub trait Producer: IntoIterator + Send + Sized {
    /// Reports whether the producer has explicit weights.
    fn weighted(&self) -> bool {
        false
    }

    /// Cost to produce `len` items, where `len` must be `N`.
    fn cost(&mut self, len: usize) -> f64;

    /// Split into two producers; one produces items `0..index`, the
    /// other `index..N`. Index must be less than `N`.
    fn split_at(self, index: usize) -> (Self, Self);
}

/// A consumer which consumes items that are fed to it.
pub trait Consumer<Item>: Send + Sized {
    type Folder: Folder<Item, Result = Self::Result>;
    type Reducer: Reducer<Self::Result>;
    type Result: Send;

    /// Reports whether the consumer has explicit weights.
    fn weighted(&self) -> bool {
        false
    }

    /// If it costs `producer_cost` to produce the items we will
    /// consume, returns cost adjusted to account for consuming them.
    fn cost(&mut self, producer_cost: f64) -> f64;

    /// Divide the consumer into two consumers, one processing items
    /// `0..index` and one processing items from `index..`. Also
    /// produces a reducer that can be used to reduce the results at
    /// the end.
    fn split_at(self, index: usize) -> (Self, Self, Self::Reducer);

    /// Convert the consumer into a folder that can consume items
    /// sequentially, eventually producing a final result.
    fn into_folder(self) -> Self::Folder;

    /// Hint whether this `Consumer` would like to stop processing
    /// further items, e.g. if a search has been completed.
    fn full(&self) -> bool {
        false
    }
}

pub trait Folder<Item> {
    type Result;

    /// Consume next item and return new sequential state.
    fn consume(self, item: Item) -> Self;

    /// Finish consuming items, produce final result.
    fn complete(self) -> Self::Result;

    /// Hint whether this `Folder` would like to stop processing
    /// further items, e.g. if a search has been completed.
    fn full(&self) -> bool {
        false
    }
}

pub trait Reducer<Result> {
    /// Reduce two final results into one; this is executed after a
    /// split.
    fn reduce(self, left: Result, right: Result) -> Result;
}

/// A stateless consumer can be freely copied.
pub trait UnindexedConsumer<ITEM>: Consumer<ITEM> {
    fn split_off(&self) -> Self;
    fn to_reducer(&self) -> Self::Reducer;
}

/// An unindexed producer that doesn't know its exact length.
/// (or can't represent its known length in a `usize`)
pub trait UnindexedProducer: IntoIterator + Send + Sized {
    fn can_split(&self) -> bool;
    fn split(self) -> (Self, Self);
}

/// A splitter controls the policy for splitting into smaller work items.
#[derive(Clone, Copy)]
enum Splitter {
    /// Classic cost-splitting uses weights to split until below a threshold.
    Cost(f64),

    /// Thief-splitting is an adaptive policy that starts by splitting into
    /// enough jobs for every worker thread, and then resets itself whenever a
    /// job is actually stolen into a different thread.
    Thief(usize, usize),
}

impl Splitter {
    #[inline]
    fn thief_id() -> usize {
        // The actual `ID` value is irrelevant.  We're just using its TLS
        // address as a unique thread key, faster than a real thread-id call.
        thread_local!{ static ID: bool = false }
        ID.with(|id| id as *const bool as usize)
    }

    #[inline]
    fn new_thief() -> Splitter {
        Splitter::Thief(Splitter::thief_id(), get_registry().num_threads())
    }

    #[inline]
    fn try(&mut self) -> bool {
        match *self {
            Splitter::Cost(ref mut cost) => {
                if *cost > THRESHOLD {
                    *cost /= 2.0;
                    true
                } else {
                    false
                }
            }

            Splitter::Thief(ref mut origin, ref mut splits) => {
                let id = Splitter::thief_id();
                if *origin != id {
                    *origin = id;
                    *splits = get_registry().num_threads();
                    true
                } else if *splits > 0 {
                    *splits /= 2;
                    true
                } else {
                    false
                }
            }
        }
    }
}

pub fn bridge<PAR_ITER, C>(mut par_iter: PAR_ITER, consumer: C) -> C::Result
    where PAR_ITER: IndexedParallelIterator,
          C: Consumer<PAR_ITER::Item>
{
    let len = par_iter.len();
    return par_iter.with_producer(Callback {
        len: len,
        consumer: consumer,
    });

    struct Callback<C> {
        len: usize,
        consumer: C,
    }

    impl<C, ITEM> ProducerCallback<ITEM> for Callback<C>
        where C: Consumer<ITEM>
    {
        type Output = C::Result;
        fn callback<P>(self, producer: P) -> C::Result
            where P: Producer<Item = ITEM>
        {
            bridge_producer_consumer(self.len, producer, self.consumer)
        }
    }
}

pub fn bridge_producer_consumer<P, C>(len: usize, mut producer: P, mut consumer: C) -> C::Result
    where P: Producer,
          C: Consumer<P::Item>
{
    let splitter = if producer.weighted() || consumer.weighted() {
        let producer_cost = producer.cost(len);
        let cost = consumer.cost(producer_cost);
        Splitter::Cost(cost)
    } else {
        Splitter::new_thief()
    };
    return helper(len, splitter, producer, consumer);

    fn helper<P, C>(len: usize, mut splitter: Splitter, producer: P, consumer: C) -> C::Result
        where P: Producer,
              C: Consumer<P::Item>
    {
        if consumer.full() {
            consumer.into_folder().complete()
        } else if len > 1 && splitter.try() {
            let mid = len / 2;
            let (left_producer, right_producer) = producer.split_at(mid);
            let (left_consumer, right_consumer, reducer) = consumer.split_at(mid);
            let (left_result, right_result) =
                join(|| helper(mid, splitter, left_producer, left_consumer),
                     || helper(len - mid, splitter, right_producer, right_consumer));
            reducer.reduce(left_result, right_result)
        } else {
            let mut folder = consumer.into_folder();
            for item in producer {
                folder = folder.consume(item);
                if folder.full() {
                    break;
                }
            }
            folder.complete()
        }
    }
}

pub fn bridge_unindexed<P, C>(producer: P, consumer: C) -> C::Result
    where P: UnindexedProducer,
          C: UnindexedConsumer<P::Item>
{
    let splitter = Splitter::new_thief();
    bridge_unindexed_producer_consumer(splitter, producer, consumer)
}

fn bridge_unindexed_producer_consumer<P, C>(mut splitter: Splitter,
                                            producer: P,
                                            consumer: C)
                                            -> C::Result
    where P: UnindexedProducer,
          C: UnindexedConsumer<P::Item>
{
    if consumer.full() {
        consumer.into_folder().complete()
    } else if producer.can_split() && splitter.try() {
        let (left_producer, right_producer) = producer.split();
        let (reducer, left_consumer, right_consumer) =
            (consumer.to_reducer(), consumer.split_off(), consumer);
        let (left_result, right_result) =
            join(|| bridge_unindexed_producer_consumer(splitter, left_producer, left_consumer),
                 || bridge_unindexed_producer_consumer(splitter, right_producer, right_consumer));
        reducer.reduce(left_result, right_result)
    } else {
        let mut folder = consumer.into_folder();
        for item in producer {
            folder = folder.consume(item);
            if folder.full() {
                break;
            }
        }
        folder.complete()
    }
}
<commit_msg>Add method fold_with to Producer and UnindexedProducer<commit_after>//! Internal traits and functions used to implement parallel
//! iteration. These should be considered highly unstable: users of
//! parallel iterators should not need to interact with them directly.
//! See `README.md` for a high-level overview.

use join;
use super::IndexedParallelIterator;
use super::len::*;
use thread_pool::get_registry;

pub trait ProducerCallback<ITEM> {
    type Output;
    fn callback<P>(self, producer: P) -> Self::Output where P: Producer<Item = ITEM>;
}

/// A producer which will produce a fixed number of items N. This is
/// not queryable through the API; the consumer is expected to track
/// it.
pub trait Producer: IntoIterator + Send + Sized {
    /// Reports whether the producer has explicit weights.
    fn weighted(&self) -> bool {
        false
    }

    /// Cost to produce `len` items, where `len` must be `N`.
    fn cost(&mut self, len: usize) -> f64;

    /// Split into two producers; one produces items `0..index`, the
    /// other `index..N`. Index must be less than `N`.
    fn split_at(self, index: usize) -> (Self, Self);

    /// Iterate the producer, feeding each element to `folder`, and
    /// stop when the folder is full (or all elements have been consumed).
    ///
    /// The provided implementation is sufficient for most iterables.
    fn fold_with<F>(self, mut folder: F) -> F
        where F: Folder<Self::Item>,
    {
        for item in self {
            folder = folder.consume(item);
            if folder.full() {
                break;
            }
        }
        folder
    }
}

/// A consumer which consumes items that are fed to it.
pub trait Consumer<Item>: Send + Sized {
    type Folder: Folder<Item, Result = Self::Result>;
    type Reducer: Reducer<Self::Result>;
    type Result: Send;

    /// Reports whether the consumer has explicit weights.
    fn weighted(&self) -> bool {
        false
    }

    /// If it costs `producer_cost` to produce the items we will
    /// consume, returns cost adjusted to account for consuming them.
    fn cost(&mut self, producer_cost: f64) -> f64;

    /// Divide the consumer into two consumers, one processing items
    /// `0..index` and one processing items from `index..`. Also
    /// produces a reducer that can be used to reduce the results at
    /// the end.
    fn split_at(self, index: usize) -> (Self, Self, Self::Reducer);

    /// Convert the consumer into a folder that can consume items
    /// sequentially, eventually producing a final result.
    fn into_folder(self) -> Self::Folder;

    /// Hint whether this `Consumer` would like to stop processing
    /// further items, e.g. if a search has been completed.
    fn full(&self) -> bool {
        false
    }
}

pub trait Folder<Item> {
    type Result;

    /// Consume next item and return new sequential state.
    fn consume(self, item: Item) -> Self;

    /// Finish consuming items, produce final result.
    fn complete(self) -> Self::Result;

    /// Hint whether this `Folder` would like to stop processing
    /// further items, e.g. if a search has been completed.
    fn full(&self) -> bool {
        false
    }
}

pub trait Reducer<Result> {
    /// Reduce two final results into one; this is executed after a
    /// split.
    fn reduce(self, left: Result, right: Result) -> Result;
}

/// A stateless consumer can be freely copied.
pub trait UnindexedConsumer<ITEM>: Consumer<ITEM> {
    fn split_off(&self) -> Self;
    fn to_reducer(&self) -> Self::Reducer;
}

/// An unindexed producer that doesn't know its exact length.
/// (or can't represent its known length in a `usize`)
pub trait UnindexedProducer: IntoIterator + Send + Sized {
    fn can_split(&self) -> bool;
    fn split(self) -> (Self, Self);

    /// Iterate the producer, feeding each element to `folder`, and
    /// stop when the folder is full (or all elements have been consumed).
    ///
    /// The provided implementation is sufficient for most iterables.
    fn fold_with<F>(self, mut folder: F) -> F
        where F: Folder<Self::Item>,
    {
        for item in self {
            folder = folder.consume(item);
            if folder.full() {
                break;
            }
        }
        folder
    }
}

/// A splitter controls the policy for splitting into smaller work items.
#[derive(Clone, Copy)]
enum Splitter {
    /// Classic cost-splitting uses weights to split until below a threshold.
    Cost(f64),

    /// Thief-splitting is an adaptive policy that starts by splitting into
    /// enough jobs for every worker thread, and then resets itself whenever a
    /// job is actually stolen into a different thread.
    Thief(usize, usize),
}

impl Splitter {
    #[inline]
    fn thief_id() -> usize {
        // The actual `ID` value is irrelevant.  We're just using its TLS
        // address as a unique thread key, faster than a real thread-id call.
        thread_local!{ static ID: bool = false }
        ID.with(|id| id as *const bool as usize)
    }

    #[inline]
    fn new_thief() -> Splitter {
        Splitter::Thief(Splitter::thief_id(), get_registry().num_threads())
    }

    #[inline]
    fn try(&mut self) -> bool {
        match *self {
            Splitter::Cost(ref mut cost) => {
                if *cost > THRESHOLD {
                    *cost /= 2.0;
                    true
                } else {
                    false
                }
            }

            Splitter::Thief(ref mut origin, ref mut splits) => {
                let id = Splitter::thief_id();
                if *origin != id {
                    *origin = id;
                    *splits = get_registry().num_threads();
                    true
                } else if *splits > 0 {
                    *splits /= 2;
                    true
                } else {
                    false
                }
            }
        }
    }
}

pub fn bridge<PAR_ITER, C>(mut par_iter: PAR_ITER, consumer: C) -> C::Result
    where PAR_ITER: IndexedParallelIterator,
          C: Consumer<PAR_ITER::Item>
{
    let len = par_iter.len();
    return par_iter.with_producer(Callback {
        len: len,
        consumer: consumer,
    });

    struct Callback<C> {
        len: usize,
        consumer: C,
    }

    impl<C, ITEM> ProducerCallback<ITEM> for Callback<C>
        where C: Consumer<ITEM>
    {
        type Output = C::Result;
        fn callback<P>(self, producer: P) -> C::Result
            where P: Producer<Item = ITEM>
        {
            bridge_producer_consumer(self.len, producer, self.consumer)
        }
    }
}

pub fn bridge_producer_consumer<P, C>(len: usize, mut producer: P, mut consumer: C) -> C::Result
    where P: Producer,
          C: Consumer<P::Item>
{
    let splitter = if producer.weighted() || consumer.weighted() {
        let producer_cost = producer.cost(len);
        let cost = consumer.cost(producer_cost);
        Splitter::Cost(cost)
    } else {
        Splitter::new_thief()
    };
    return helper(len, splitter, producer, consumer);

    fn helper<P, C>(len: usize, mut splitter: Splitter, producer: P, consumer: C) -> C::Result
        where P: Producer,
              C: Consumer<P::Item>
    {
        if consumer.full() {
            consumer.into_folder().complete()
        } else if len > 1 && splitter.try() {
            let mid = len / 2;
            let (left_producer, right_producer) = producer.split_at(mid);
            let (left_consumer, right_consumer, reducer) = consumer.split_at(mid);
            let (left_result, right_result) =
                join(|| helper(mid, splitter, left_producer, left_consumer),
                     || helper(len - mid, splitter, right_producer, right_consumer));
            reducer.reduce(left_result, right_result)
        } else {
            producer.fold_with(consumer.into_folder()).complete()
        }
    }
}

pub fn bridge_unindexed<P, C>(producer: P, consumer: C) -> C::Result
    where P: UnindexedProducer,
          C: UnindexedConsumer<P::Item>
{
    let splitter = Splitter::new_thief();
    bridge_unindexed_producer_consumer(splitter, producer, consumer)
}

fn bridge_unindexed_producer_consumer<P, C>(mut splitter: Splitter,
                                            producer: P,
                                            consumer: C)
                                            -> C::Result
    where P: UnindexedProducer,
          C: UnindexedConsumer<P::Item>
{
    if consumer.full() {
        consumer.into_folder().complete()
    } else if producer.can_split() && splitter.try() {
        let (left_producer, right_producer) = producer.split();
        let (reducer, left_consumer, right_consumer) =
            (consumer.to_reducer(), consumer.split_off(), consumer);
        let (left_result, right_result) =
            join(|| bridge_unindexed_producer_consumer(splitter, left_producer, left_consumer),
                 || bridge_unindexed_producer_consumer(splitter, right_producer, right_consumer));
        reducer.reduce(left_result, right_result)
    } else {
        producer.fold_with(consumer.into_folder()).complete()
    }
}
<|endoftext|>