<commit_before>use RealNumber;
use std::result;
use std::sync::{Arc, Mutex};
use super::Identifier;
use super::super::{
    Domain, NumberSpace, ToSliceMut, DspVec, ErrorReason, RededicateOps,
	Buffer, Owner, TimeOrFrequencyData, RealOrComplexData, Vector,
	DataDomain
};
use super::operations_enum::*;

const ARGUMENT1: usize = 0;
const ARGUMENT2: usize = 1;

/// An operation on one data vector which has been prepared in
/// advance.
///
/// The type arguments can be read like a function definition: TI1 -> TO1
#[derive(Clone)]
pub struct PreparedOperation1<T, NI, DI, NO, DO>
    where T: RealNumber,
		NI: NumberSpace, DI: Domain,
		NO: NumberSpace, DO: Domain {
    number_space_in: NI,
	domain_in: DI,
	number_space_out: NO,
	domain_out: DO,
    ops: Vec<Operation<T>>
}

/// Executes the prepared operations to convert `S`to `D`,
pub trait PreparedOperation1Exec<B, S, D> {
    /// Executes the prepared operations to convert `S`to `D`,
    fn exec(&self, buffer: &mut B, source: S) -> result::Result<D, (ErrorReason, D)>;
}

/// Prepares an operation with one input and one output.
pub fn prepare32_1<N, D>(number_space: N, domain: D)
    -> PreparedOperation1<f32, N, D, N, D>
    where
        N: NumberSpace,
		D: Domain {
    PreparedOperation1
         {
			number_space_in: number_space.clone(),
 			domain_in: domain.clone(),
 			number_space_out: number_space,
 			domain_out: domain,
            ops: Vec::new()
         }
}

/// Prepares an operation with one input and one output.
pub fn prepare64_1<N, D>(number_space: N, domain: D)
    -> PreparedOperation1<f64, N, D, N, D>
    where
        N: NumberSpace,
		D: Domain {
    PreparedOperation1
         {
			number_space_in: number_space.clone(),
 			domain_in: domain.clone(),
 			number_space_out: number_space,
 			domain_out: domain,
            ops: Vec::new()
         }
}

/// An operation on two data vectors which has been prepared in
/// advance.
///
/// The type arguments can be read like a function definition: (TI1, TI2) -> (TO1, TO2)
#[derive(Clone)]
pub struct PreparedOperation2<T, NI1, DI1, NI2, DI2, NO1, DO1, NO2, DO2>
    where T: RealNumber,
		NI1: NumberSpace, DI1: Domain, NI2: NumberSpace, DI2: Domain,
		NO1: NumberSpace, DO1: Domain, NO2: NumberSpace, DO2: Domain {
	number_space_in1: NI1,
	domain_in1: DI1,
	number_space_in2: NI2,
	domain_in2: DI2,
	number_space_out1: NO1,
	domain_out1: DO1,
	number_space_out2: NO2,
	domain_out2: DO2,
    ops: Vec<Operation<T>>,
    swap: bool
}
/*
/// A multi operation which holds a vector and records all changes
/// which need to be done to the vectors. By calling `get` on the struct
/// all operations will be executed in one run.
pub struct MultiOperation1<S, T, NI, DI, NO, DO>
    where T: RealNumber, S: ToSlice<T>,
		NI: NumberSpace, DI: Domain,
		NO: NumberSpace, DO: Domain {
    a: DspVec<S, T, NI, DI>,
    prepared_ops: PreparedOperation1<S, T, NI, DI, NO, DO>
}

/// A multi operation which holds two vectors and records all changes
/// which need to be done to the vectors. By calling `get` on the struct
/// all operations will be executed in one run.
pub struct MultiOperation2<S, T, NI1, DI1, NI2, DI2, NO1, DO1, NO2, DO2>
    where T: RealNumber, S: ToSlice<T>,
		NI1: NumberSpace, DI1: Domain, NI2: NumberSpace, DI2: Domain,
		NO1: NumberSpace, DO1: Domain, NO2: NumberSpace, DO2: Domain {
    a: DspVec<S, T, NI2, DI1>,
    b: DspVec<S, T, NI2, DI2>,
    prepared_ops: PreparedOperation2<S, T, NI1, DI1, NI2, DI2, NO1, DO1, NO2, DO2>
}
*/
/// An operation which can be prepared in advance and operates on one
/// input and produces one output
impl<T, NI, DI, NO, DO> PreparedOperation1<T, NI, DI, NO, DO>
	where T: RealNumber + 'static,
		NI: NumberSpace, DI: Domain,
		NO: NumberSpace, DO: Domain {

	/// Extends the operation to operate on one more vector.
	pub fn extend<NI2, DI2>(self, number_space: NI2, domain: DI2)
			-> PreparedOperation2<T, NI, DI, NI2, DI2, NO, DO, NI2, DI2>
		where NI2: NumberSpace, DI2: Domain {
		PreparedOperation2
		{
			number_space_in1: self.number_space_in,
			domain_in1: self.domain_in,
			number_space_in2: number_space.clone(),
			domain_in2: domain.clone(),
			number_space_out1: self.number_space_out,
			domain_out1: self.domain_out,
			number_space_out2: number_space,
			domain_out2: domain,
			ops: self.ops,
			swap: false
		}
	}

	/// Adds new operations which will be executed with the next call to `exec`
	///
	/// As a background: The function `operation` will be executed immediately. It only operated on `Identifier` types and these serve as
	/// placeholder for vectors. Every operation done to an `Identifier`
	/// is recorded and will be executed on vectors if `exec` is called.
	pub fn add_ops<F, NT, DT>(self, operation: F)
		-> PreparedOperation1<T, NI, DI, NT, DT>
		where F: Fn(Identifier<T, NO, DO>) -> Identifier<T, NT, DT>,
		      DT: Domain,
			  NT: NumberSpace

	{
		let mut ops = self.ops;
		let (new_ops, domain_new, number_space_new) =
		{
			let counter = Arc::new(Mutex::new(0));
			let t1 = Identifier
				{
				    arg: ARGUMENT1,
				    ops: Vec::new(),
				    counter: counter,
				    domain: self.domain_out.clone(),
				    number_space: self.number_space_out.clone()
				};

			let r1 = operation(t1);
			(r1.ops, r1.domain, r1.number_space)
		};
		for v in new_ops {
			ops.push(v.1);
		}

		PreparedOperation1
		{
			domain_in: self.domain_in,
			number_space_in: self.number_space_in,
			domain_out: domain_new,
			number_space_out: number_space_new,
			ops: ops
		}
	}
}

fn generic_vector_from_any_vector<S, T, N, D>(vec: DspVec<S, T, N, D>) -> (N, D, DspVec<S, T, RealOrComplexData, TimeOrFrequencyData>)
    where T: RealNumber + 'static,
        S: ToSliceMut<T> + Owner,
        N: NumberSpace, D: Domain {
    let domain = vec.domain.clone();
    let number_space = vec.number_space.clone();
    let gen = DspVec {
            delta: vec.delta,
            domain: TimeOrFrequencyData { domain_current: vec.domain() },
            number_space: RealOrComplexData { is_complex_current: vec.is_complex() },
            valid_len: vec.valid_len,
            multicore_settings: vec.multicore_settings,
            data: vec.data
    };
    (number_space, domain, gen)
}

fn generic_vector_back_to_vector<S, T, N, D>(number_space: N, domain: D, vec: DspVec<S, T, RealOrComplexData, TimeOrFrequencyData>) -> DspVec<S, T, N, D>
    where T: RealNumber + 'static,
        S: ToSliceMut<T> + Owner,
        N: NumberSpace, D: Domain {
    let is_complex = vec.is_complex();
    let result_domain = vec.domain();
    let mut vec = DspVec {
            data: vec.data,
            delta: vec.delta,
            domain: domain,
            number_space: number_space,
            valid_len: vec.valid_len,
            multicore_settings: vec.multicore_settings
    };

    if is_complex {
        vec.number_space.to_complex();
    } else {
        vec.number_space.to_real();
    }

    if result_domain == DataDomain::Time {
        vec.domain.to_time();
    } else {
        vec.domain.to_freq();
    }

    vec
}

impl<T, S, B, NI, DI, NO, DO> PreparedOperation1Exec<B, DspVec<S, T, NI, DI>, DspVec<S, T, NO, DO>> for PreparedOperation1<T, NI, DI, NO, DO>
	where T: RealNumber + 'static,
        S: ToSliceMut<T> + Owner,
        DspVec<S, T, NO, DO>: RededicateOps<DspVec<S, T, NI, DI>>,
		NI: NumberSpace, DI: Domain,
		NO: NumberSpace, DO: Domain,
        B: Buffer<S, T> {
	/// Executes all recorded operations on the input vectors.
	fn exec(&self, buffer: &mut B, a: DspVec<S, T, NI, DI>) -> result::Result<DspVec<S, T, NO, DO>, (ErrorReason, DspVec<S, T, NO, DO>)> {
		let mut vec = Vec::new();
		let (number_space, domain, gen) = generic_vector_from_any_vector(a);
		vec.push(gen);

		// at this point we would execute all ops and cast the result to the right types
		let result = DspVec::<S, T, RealOrComplexData, TimeOrFrequencyData>::perform_operations(buffer, vec, &self.ops);

		match result {
			Err((reason, mut vec)) => {
				let a = vec.pop().unwrap();
				let a = generic_vector_back_to_vector(number_space, domain, a);
				Err((
					reason,
					DspVec::<S, T, NO, DO>::rededicate_from(a)))
			},
			Ok(mut vec) => {
				let a = vec.pop().unwrap();
				let a = generic_vector_back_to_vector(number_space, domain, a);
				// Convert back
				Ok(DspVec::<S, T, NO, DO>::rededicate_from(a))
			}
		}
	}
}

impl<T, S, B, N, D> PreparedOperation1Exec<B, DspVec<S, T, N, D>, DspVec<S, T, N, D>> for PreparedOperation1<T, N, D, N, D>
	where T: RealNumber + 'static,
        S: ToSliceMut<T> + Owner,
		N: NumberSpace, D: Domain,
        B: Buffer<S, T> {
	/// Executes all recorded operations on the input vectors.
	fn exec(&self, buffer: &mut B, a: DspVec<S, T, N, D>) -> result::Result<DspVec<S, T, N, D>, (ErrorReason, DspVec<S, T, N, D>)> {
		let mut vec = Vec::new();
		let (number_space, domain, gen) = generic_vector_from_any_vector(a);
		vec.push(gen);

		// at this point we would execute all ops and cast the result to the right types
		let result = DspVec::<S, T, RealOrComplexData, TimeOrFrequencyData>::perform_operations(buffer, vec, &self.ops);

		match result {
			Err((reason, mut vec)) => {
				let a = vec.pop().unwrap();
				let a = generic_vector_back_to_vector(number_space, domain, a);
				Err((reason, a))
			},
			Ok(mut vec) => {
                let a = vec.pop().unwrap();
				let a = generic_vector_back_to_vector(number_space, domain, a);
				// Convert back
				Ok(a)
			}
		}
	}
}
<commit_msg>Further reduced code duplication<commit_after>use RealNumber;
use std::result;
use std::sync::{Arc, Mutex};
use super::Identifier;
use super::super::{
    Domain, NumberSpace, ToSliceMut, DspVec, ErrorReason, RededicateForceOps,
	Buffer, Owner, TimeOrFrequencyData, RealOrComplexData, Vector,
	DataDomain
};
use super::operations_enum::*;

const ARGUMENT1: usize = 0;
const ARGUMENT2: usize = 1;

/// An operation on one data vector which has been prepared in
/// advance.
///
/// The type arguments can be read like a function definition: TI1 -> TO1
#[derive(Clone)]
pub struct PreparedOperation1<T, NI, DI, NO, DO>
    where T: RealNumber,
		NI: NumberSpace, DI: Domain,
		NO: NumberSpace, DO: Domain {
    number_space_in: NI,
	domain_in: DI,
	number_space_out: NO,
	domain_out: DO,
    ops: Vec<Operation<T>>
}

/// Executes the prepared operations to convert `S`to `D`,
pub trait PreparedOperation1Exec<B, S, D> {
    /// Executes the prepared operations to convert `S`to `D`,
    fn exec(&self, buffer: &mut B, source: S) -> result::Result<D, (ErrorReason, D)>;
}

/// Prepares an operation with one input and one output.
pub fn prepare32_1<N, D>(number_space: N, domain: D)
    -> PreparedOperation1<f32, N, D, N, D>
    where
        N: NumberSpace,
		D: Domain {
    PreparedOperation1
         {
			number_space_in: number_space.clone(),
 			domain_in: domain.clone(),
 			number_space_out: number_space,
 			domain_out: domain,
            ops: Vec::new()
         }
}

/// Prepares an operation with one input and one output.
pub fn prepare64_1<N, D>(number_space: N, domain: D)
    -> PreparedOperation1<f64, N, D, N, D>
    where
        N: NumberSpace,
		D: Domain {
    PreparedOperation1
         {
			number_space_in: number_space.clone(),
 			domain_in: domain.clone(),
 			number_space_out: number_space,
 			domain_out: domain,
            ops: Vec::new()
         }
}

/// An operation on two data vectors which has been prepared in
/// advance.
///
/// The type arguments can be read like a function definition: (TI1, TI2) -> (TO1, TO2)
#[derive(Clone)]
pub struct PreparedOperation2<T, NI1, DI1, NI2, DI2, NO1, DO1, NO2, DO2>
    where T: RealNumber,
		NI1: NumberSpace, DI1: Domain, NI2: NumberSpace, DI2: Domain,
		NO1: NumberSpace, DO1: Domain, NO2: NumberSpace, DO2: Domain {
	number_space_in1: NI1,
	domain_in1: DI1,
	number_space_in2: NI2,
	domain_in2: DI2,
	number_space_out1: NO1,
	domain_out1: DO1,
	number_space_out2: NO2,
	domain_out2: DO2,
    ops: Vec<Operation<T>>,
    swap: bool
}
/*
/// A multi operation which holds a vector and records all changes
/// which need to be done to the vectors. By calling `get` on the struct
/// all operations will be executed in one run.
pub struct MultiOperation1<S, T, NI, DI, NO, DO>
    where T: RealNumber, S: ToSlice<T>,
		NI: NumberSpace, DI: Domain,
		NO: NumberSpace, DO: Domain {
    a: DspVec<S, T, NI, DI>,
    prepared_ops: PreparedOperation1<S, T, NI, DI, NO, DO>
}

/// A multi operation which holds two vectors and records all changes
/// which need to be done to the vectors. By calling `get` on the struct
/// all operations will be executed in one run.
pub struct MultiOperation2<S, T, NI1, DI1, NI2, DI2, NO1, DO1, NO2, DO2>
    where T: RealNumber, S: ToSlice<T>,
		NI1: NumberSpace, DI1: Domain, NI2: NumberSpace, DI2: Domain,
		NO1: NumberSpace, DO1: Domain, NO2: NumberSpace, DO2: Domain {
    a: DspVec<S, T, NI2, DI1>,
    b: DspVec<S, T, NI2, DI2>,
    prepared_ops: PreparedOperation2<S, T, NI1, DI1, NI2, DI2, NO1, DO1, NO2, DO2>
}
*/
/// An operation which can be prepared in advance and operates on one
/// input and produces one output
impl<T, NI, DI, NO, DO> PreparedOperation1<T, NI, DI, NO, DO>
	where T: RealNumber + 'static,
		NI: NumberSpace, DI: Domain,
		NO: NumberSpace, DO: Domain {

	/// Extends the operation to operate on one more vector.
	pub fn extend<NI2, DI2>(self, number_space: NI2, domain: DI2)
			-> PreparedOperation2<T, NI, DI, NI2, DI2, NO, DO, NI2, DI2>
		where NI2: NumberSpace, DI2: Domain {
		PreparedOperation2
		{
			number_space_in1: self.number_space_in,
			domain_in1: self.domain_in,
			number_space_in2: number_space.clone(),
			domain_in2: domain.clone(),
			number_space_out1: self.number_space_out,
			domain_out1: self.domain_out,
			number_space_out2: number_space,
			domain_out2: domain,
			ops: self.ops,
			swap: false
		}
	}

	/// Adds new operations which will be executed with the next call to `exec`
	///
	/// As a background: The function `operation` will be executed immediately. It only operated on `Identifier` types and these serve as
	/// placeholder for vectors. Every operation done to an `Identifier`
	/// is recorded and will be executed on vectors if `exec` is called.
	pub fn add_ops<F, NT, DT>(self, operation: F)
		-> PreparedOperation1<T, NI, DI, NT, DT>
		where F: Fn(Identifier<T, NO, DO>) -> Identifier<T, NT, DT>,
		      DT: Domain,
			  NT: NumberSpace

	{
		let mut ops = self.ops;
		let (new_ops, domain_new, number_space_new) =
		{
			let counter = Arc::new(Mutex::new(0));
			let t1 = Identifier
				{
				    arg: ARGUMENT1,
				    ops: Vec::new(),
				    counter: counter,
				    domain: self.domain_out.clone(),
				    number_space: self.number_space_out.clone()
				};

			let r1 = operation(t1);
			(r1.ops, r1.domain, r1.number_space)
		};
		for v in new_ops {
			ops.push(v.1);
		}

		PreparedOperation1
		{
			domain_in: self.domain_in,
			number_space_in: self.number_space_in,
			domain_out: domain_new,
			number_space_out: number_space_new,
			ops: ops
		}
	}
}

fn generic_vector_from_any_vector<S, T, N, D>(vec: DspVec<S, T, N, D>) -> (N, D, DspVec<S, T, RealOrComplexData, TimeOrFrequencyData>)
    where T: RealNumber + 'static,
        S: ToSliceMut<T> + Owner,
        N: NumberSpace, D: Domain {
    let domain = vec.domain.clone();
    let number_space = vec.number_space.clone();
    let gen = DspVec {
            delta: vec.delta,
            domain: TimeOrFrequencyData { domain_current: vec.domain() },
            number_space: RealOrComplexData { is_complex_current: vec.is_complex() },
            valid_len: vec.valid_len,
            multicore_settings: vec.multicore_settings,
            data: vec.data
    };
    (number_space, domain, gen)
}

fn generic_vector_back_to_vector<S, T, N, D>(number_space: N, domain: D, vec: DspVec<S, T, RealOrComplexData, TimeOrFrequencyData>) -> DspVec<S, T, N, D>
    where T: RealNumber + 'static,
        S: ToSliceMut<T> + Owner,
        N: NumberSpace, D: Domain {
    let is_complex = vec.is_complex();
    let result_domain = vec.domain();
    let mut vec = DspVec {
            data: vec.data,
            delta: vec.delta,
            domain: domain,
            number_space: number_space,
            valid_len: vec.valid_len,
            multicore_settings: vec.multicore_settings
    };

    if is_complex {
        vec.number_space.to_complex();
    } else {
        vec.number_space.to_real();
    }

    if result_domain == DataDomain::Time {
        vec.domain.to_time();
    } else {
        vec.domain.to_freq();
    }

    vec
}

impl<T, S, B, NI, DI, NO, DO> PreparedOperation1Exec<B, DspVec<S, T, NI, DI>, DspVec<S, T, NO, DO>> for PreparedOperation1<T, NI, DI, NO, DO>
	where T: RealNumber + 'static,
        S: ToSliceMut<T> + Owner,
        DspVec<S, T, NO, DO>: RededicateForceOps<DspVec<S, T, NI, DI>>,
		NI: NumberSpace, DI: Domain,
		NO: NumberSpace, DO: Domain,
        B: Buffer<S, T> {
	/// Executes all recorded operations on the input vectors.
	fn exec(&self, buffer: &mut B, a: DspVec<S, T, NI, DI>) -> result::Result<DspVec<S, T, NO, DO>, (ErrorReason, DspVec<S, T, NO, DO>)> {
		let mut vec = Vec::new();
		let (number_space, domain, gen) = generic_vector_from_any_vector(a);
		vec.push(gen);

		// at this point we would execute all ops and cast the result to the right types
		let result = DspVec::<S, T, RealOrComplexData, TimeOrFrequencyData>::perform_operations(buffer, vec, &self.ops);

		match result {
			Err((reason, mut vec)) => {
				let a = vec.pop().unwrap();
				let a = generic_vector_back_to_vector(number_space, domain, a);
				Err((
					reason,
					DspVec::<S, T, NO, DO>::rededicate_from_force(a)))
			},
			Ok(mut vec) => {
				let a = vec.pop().unwrap();
				let a = generic_vector_back_to_vector(number_space, domain, a);
				// Convert back
				Ok(DspVec::<S, T, NO, DO>::rededicate_from_force(a))
			}
		}
	}
}
/*
impl<T, S, B, N, D> PreparedOperation1Exec<B, DspVec<S, T, N, D>, DspVec<S, T, N, D>> for PreparedOperation1<T, N, D, N, D>
	where T: RealNumber + 'static,
        S: ToSliceMut<T> + Owner,
		N: NumberSpace, D: Domain,
        B: Buffer<S, T> {
	/// Executes all recorded operations on the input vectors.
	fn exec(&self, buffer: &mut B, a: DspVec<S, T, N, D>) -> result::Result<DspVec<S, T, N, D>, (ErrorReason, DspVec<S, T, N, D>)> {
		let mut vec = Vec::new();
		let (number_space, domain, gen) = generic_vector_from_any_vector(a);
		vec.push(gen);

		// at this point we would execute all ops and cast the result to the right types
		let result = DspVec::<S, T, RealOrComplexData, TimeOrFrequencyData>::perform_operations(buffer, vec, &self.ops);

		match result {
			Err((reason, mut vec)) => {
				let a = vec.pop().unwrap();
				let a = generic_vector_back_to_vector(number_space, domain, a);
				Err((reason, a))
			},
			Ok(mut vec) => {
                let a = vec.pop().unwrap();
				let a = generic_vector_back_to_vector(number_space, domain, a);
				// Convert back
				Ok(a)
			}
		}
	}
}*/
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (methods) isnt

#[macro_use]
extern crate uucore;

use std::{
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{crate_version, App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

const BELL: &str = "\x07";

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = App::new(executable!())
        .about("A file perusal filter for CRT viewing.")
        .version(crate_version!())
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        // The commented arguments below are unimplemented:
        /*
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screen full"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        */
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
        .get_matches_from(args);

    let mut buff = String::new();
    let silent = matches.is_present(options::SILENT);
    if let Some(files) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = files.len();

        let mut files_iter = files.peekable();
        while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) {
            let file = Path::new(file);
            if file.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("'{}' is a directory.", file.display());
                return 1;
            }
            if !file.exists() {
                terminal::disable_raw_mode().unwrap();
                show_error!("cannot open {}: No such file or directory", file.display());
                return 1;
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(file).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            more(&buff, &mut stdout, next_file.copied(), silent);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, None, silent);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));

    let mut pager = Pager::new(rows as usize, lines, next_file, silent);
    pager.draw(stdout, false);
    if pager.should_close() {
        return;
    }

    loop {
        let mut wrong_key = false;
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(&mut stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    if pager.should_close() {
                        return;
                    } else {
                        pager.page_down();
                    }
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    pager.page_up();
                }
                _ => {
                    wrong_key = true;
                }
            }

            pager.draw(stdout, wrong_key);
        }
    }
}

struct Pager<'a> {
    // The current line at the top of the screen
    upper_mark: usize,
    // The number of rows that fit on the screen
    content_rows: usize,
    lines: Vec<String>,
    next_file: Option<&'a str>,
    line_count: usize,
    silent: bool,
}

impl<'a> Pager<'a> {
    fn new(rows: usize, lines: Vec<String>, next_file: Option<&'a str>, silent: bool) -> Self {
        let line_count = lines.len();
        Self {
            upper_mark: 0,
            content_rows: rows.saturating_sub(1),
            lines,
            next_file,
            line_count,
            silent,
        }
    }

    fn should_close(&mut self) -> bool {
        self.upper_mark
            .saturating_add(self.content_rows)
            .eq(&self.line_count)
    }

    fn page_down(&mut self) {
        self.upper_mark = self
            .upper_mark
            .saturating_add(self.content_rows)
            .min(self.line_count.saturating_sub(self.content_rows));
    }

    fn page_up(&mut self) {
        self.upper_mark = self.upper_mark.saturating_sub(self.content_rows);
    }

    fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: bool) {
        let lower_mark = self.line_count.min(self.upper_mark + self.content_rows);
        self.draw_lines(stdout);
        self.draw_prompt(stdout, lower_mark, wrong_key);
        stdout.flush().unwrap();
    }

    fn draw_lines(&self, stdout: &mut std::io::Stdout) {
        execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
        let displayed_lines = self
            .lines
            .iter()
            .skip(self.upper_mark)
            .take(self.content_rows);

        for line in displayed_lines {
            stdout
                .write_all(format!("\r{}\n", line).as_bytes())
                .unwrap();
        }
    }

    fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: bool) {
        let status_inner = if lower_mark == self.line_count {
            format!("Next file: {}", self.next_file.unwrap_or_default())
        } else {
            format!(
                "{}%",
                (lower_mark as f64 / self.line_count as f64 * 100.0).round() as u16
            )
        };

        let status = format!("--More--({})", status_inner);

        let banner = match (self.silent, wrong_key) {
            (true, true) => "[Press 'h' for instructions. (unimplemented)]".to_string(),
            (true, false) => format!("{}[Press space to continue, 'q' to quit.]", status),
            (false, true) => format!("{}{}", status, BELL),
            (false, false) => status,
        };

        write!(
            stdout,
            "\r{}{}{}",
            Attribute::Reverse,
            banner,
            Attribute::Reset
        )
        .unwrap();
    }
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::with_capacity(buff.lines().count());

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(line: &str, cols: usize) -> Vec<String> {
    let width = UnicodeWidthStr::width(line);
    let mut lines = Vec::new();
    if width < cols {
        lines.push(line.to_string());
        return lines;
    }

    let gr_idx = UnicodeSegmentation::grapheme_indices(line, true);
    let mut last_index = 0;
    let mut total_width = 0;
    for (index, grapheme) in gr_idx {
        let width = UnicodeWidthStr::width(grapheme);
        total_width += width;

        if total_width > cols {
            lines.push(line[last_index..index].to_string());
            last_index = index;
            total_width = width;
        }
    }

    if last_index != line.len() {
        lines.push(line[last_index..].to_string());
    }
    lines
}

#[cfg(test)]
mod tests {
    use super::break_line;
    use unicode_width::UnicodeWidthStr;

    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);
        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        assert_eq!((80, 80, 40), (widths[0], widths[1], widths[2]));
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }

    #[test]
    fn test_break_line_zwj() {
        let mut test_string = String::with_capacity(1100);
        for _ in 0..20 {
            test_string.push_str("👩🏻🔬");
        }

        let lines = break_line(&test_string, 80);

        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        // Each 👩🏻🔬 is 6 character width it break line to the closest number to 80 => 6 * 13 = 78
        assert_eq!((78, 42), (widths[0], widths[1]));
    }
}
<commit_msg>Fixed numeric type<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * (c) Martin Kysel <code@martinkysel.com>
//  *
//  * For the full copyright and license information, please view the LICENSE file
//  * that was distributed with this source code.

// spell-checker:ignore (methods) isnt

#[macro_use]
extern crate uucore;

use std::{
    fs::File,
    io::{stdin, stdout, BufReader, Read, Stdout, Write},
    path::Path,
    time::Duration,
};

#[cfg(all(unix, not(target_os = "fuchsia")))]
extern crate nix;

use clap::{crate_version, App, Arg};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent, KeyModifiers},
    execute, queue,
    style::Attribute,
    terminal,
};

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

const BELL: &str = "\x07";

pub mod options {
    pub const SILENT: &str = "silent";
    pub const LOGICAL: &str = "logical";
    pub const NO_PAUSE: &str = "no-pause";
    pub const PRINT_OVER: &str = "print-over";
    pub const CLEAN_PRINT: &str = "clean-print";
    pub const SQUEEZE: &str = "squeeze";
    pub const PLAIN: &str = "plain";
    pub const LINES: &str = "lines";
    pub const NUMBER: &str = "number";
    pub const PATTERN: &str = "pattern";
    pub const FROM_LINE: &str = "from-line";
    pub const FILES: &str = "files";
}

const MULTI_FILE_TOP_PROMPT: &str = "::::::::::::::\n{}\n::::::::::::::\n";

pub fn uumain(args: impl uucore::Args) -> i32 {
    let matches = App::new(executable!())
        .about("A file perusal filter for CRT viewing.")
        .version(crate_version!())
        .arg(
            Arg::with_name(options::SILENT)
                .short("d")
                .long(options::SILENT)
                .help("Display help instead of ringing bell"),
        )
        // The commented arguments below are unimplemented:
        /*
        .arg(
            Arg::with_name(options::LOGICAL)
                .short("f")
                .long(options::LOGICAL)
                .help("Count logical rather than screen lines"),
        )
        .arg(
            Arg::with_name(options::NO_PAUSE)
                .short("l")
                .long(options::NO_PAUSE)
                .help("Suppress pause after form feed"),
        )
        .arg(
            Arg::with_name(options::PRINT_OVER)
                .short("c")
                .long(options::PRINT_OVER)
                .help("Do not scroll, display text and clean line ends"),
        )
        .arg(
            Arg::with_name(options::CLEAN_PRINT)
                .short("p")
                .long(options::CLEAN_PRINT)
                .help("Do not scroll, clean screen and display text"),
        )
        .arg(
            Arg::with_name(options::SQUEEZE)
                .short("s")
                .long(options::SQUEEZE)
                .help("Squeeze multiple blank lines into one"),
        )
        .arg(
            Arg::with_name(options::PLAIN)
                .short("u")
                .long(options::PLAIN)
                .help("Suppress underlining and bold"),
        )
        .arg(
            Arg::with_name(options::LINES)
                .short("n")
                .long(options::LINES)
                .value_name("number")
                .takes_value(true)
                .help("The number of lines per screen full"),
        )
        .arg(
            Arg::with_name(options::NUMBER)
                .allow_hyphen_values(true)
                .long(options::NUMBER)
                .required(false)
                .takes_value(true)
                .help("Same as --lines"),
        )
        .arg(
            Arg::with_name(options::FROM_LINE)
                .short("F")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .value_name("number")
                .help("Display file beginning from line number"),
        )
        .arg(
            Arg::with_name(options::PATTERN)
                .short("P")
                .allow_hyphen_values(true)
                .required(false)
                .takes_value(true)
                .help("Display file beginning from pattern match"),
        )
        */
        .arg(
            Arg::with_name(options::FILES)
                .required(false)
                .multiple(true)
                .help("Path to the files to be read"),
        )
        .get_matches_from(args);

    let mut buff = String::new();
    let silent = matches.is_present(options::SILENT);
    if let Some(files) = matches.values_of(options::FILES) {
        let mut stdout = setup_term();
        let length = files.len();

        let mut files_iter = files.peekable();
        while let (Some(file), next_file) = (files_iter.next(), files_iter.peek()) {
            let file = Path::new(file);
            if file.is_dir() {
                terminal::disable_raw_mode().unwrap();
                show_usage_error!("'{}' is a directory.", file.display());
                return 1;
            }
            if !file.exists() {
                terminal::disable_raw_mode().unwrap();
                show_error!("cannot open {}: No such file or directory", file.display());
                return 1;
            }
            if length > 1 {
                buff.push_str(&MULTI_FILE_TOP_PROMPT.replace("{}", file.to_str().unwrap()));
            }
            let mut reader = BufReader::new(File::open(file).unwrap());
            reader.read_to_string(&mut buff).unwrap();
            more(&buff, &mut stdout, next_file.copied(), silent);
            buff.clear();
        }
        reset_term(&mut stdout);
    } else if atty::isnt(atty::Stream::Stdin) {
        stdin().read_to_string(&mut buff).unwrap();
        let mut stdout = setup_term();
        more(&buff, &mut stdout, None, silent);
        reset_term(&mut stdout);
    } else {
        show_usage_error!("bad usage");
    }
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn setup_term() -> std::io::Stdout {
    let stdout = stdout();
    terminal::enable_raw_mode().unwrap();
    stdout
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn setup_term() -> usize {
    0
}

#[cfg(not(target_os = "fuchsia"))]
fn reset_term(stdout: &mut std::io::Stdout) {
    terminal::disable_raw_mode().unwrap();
    // Clear the prompt
    queue!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
    // Move cursor to the beginning without printing new line
    print!("\r");
    stdout.flush().unwrap();
}

#[cfg(target_os = "fuchsia")]
#[inline(always)]
fn reset_term(_: &mut usize) {}

fn more(buff: &str, mut stdout: &mut Stdout, next_file: Option<&str>, silent: bool) {
    let (cols, rows) = terminal::size().unwrap();
    let lines = break_buff(buff, usize::from(cols));

    let mut pager = Pager::new(rows, lines, next_file, silent);
    pager.draw(stdout, false);
    if pager.should_close() {
        return;
    }

    loop {
        let mut wrong_key = false;
        if event::poll(Duration::from_millis(10)).unwrap() {
            match event::read().unwrap() {
                Event::Key(KeyEvent {
                    code: KeyCode::Char('q'),
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char('c'),
                    modifiers: KeyModifiers::CONTROL,
                }) => {
                    reset_term(&mut stdout);
                    std::process::exit(0);
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Down,
                    modifiers: KeyModifiers::NONE,
                })
                | Event::Key(KeyEvent {
                    code: KeyCode::Char(' '),
                    modifiers: KeyModifiers::NONE,
                }) => {
                    if pager.should_close() {
                        return;
                    } else {
                        pager.page_down();
                    }
                }
                Event::Key(KeyEvent {
                    code: KeyCode::Up,
                    modifiers: KeyModifiers::NONE,
                }) => {
                    pager.page_up();
                }
                _ => {
                    wrong_key = true;
                }
            }

            pager.draw(stdout, wrong_key);
        }
    }
}

struct Pager<'a> {
    // The current line at the top of the screen
    upper_mark: usize,
    // The number of rows that fit on the screen
    content_rows: u16,
    lines: Vec<String>,
    next_file: Option<&'a str>,
    line_count: usize,
    silent: bool,
}

impl<'a> Pager<'a> {
    fn new(rows: u16, lines: Vec<String>, next_file: Option<&'a str>, silent: bool) -> Self {
        let line_count = lines.len();
        Self {
            upper_mark: 0,
            content_rows: rows.saturating_sub(1),
            lines,
            next_file,
            line_count,
            silent,
        }
    }

    fn should_close(&mut self) -> bool {
        self.upper_mark
            .saturating_add(self.content_rows.into())
            .eq(&self.line_count)
    }

    fn page_down(&mut self) {
        self.upper_mark = self
            .upper_mark
            .saturating_add(self.content_rows.into())
            .min(self.line_count.saturating_sub(self.content_rows.into()));
    }

    fn page_up(&mut self) {
        self.upper_mark = self.upper_mark.saturating_sub(self.content_rows.into());
    }

    fn draw(&self, stdout: &mut std::io::Stdout, wrong_key: bool) {
        let lower_mark = self
            .line_count
            .min(self.upper_mark.saturating_add(self.content_rows.into()));
        self.draw_lines(stdout);
        self.draw_prompt(stdout, lower_mark, wrong_key);
        stdout.flush().unwrap();
    }

    fn draw_lines(&self, stdout: &mut std::io::Stdout) {
        execute!(stdout, terminal::Clear(terminal::ClearType::CurrentLine)).unwrap();
        let displayed_lines = self
            .lines
            .iter()
            .skip(self.upper_mark)
            .take(self.content_rows.into());

        for line in displayed_lines {
            stdout
                .write_all(format!("\r{}\n", line).as_bytes())
                .unwrap();
        }
    }

    fn draw_prompt(&self, stdout: &mut Stdout, lower_mark: usize, wrong_key: bool) {
        let status_inner = if lower_mark == self.line_count {
            format!("Next file: {}", self.next_file.unwrap_or_default())
        } else {
            format!(
                "{}%",
                (lower_mark as f64 / self.line_count as f64 * 100.0).round() as u16
            )
        };

        let status = format!("--More--({})", status_inner);

        let banner = match (self.silent, wrong_key) {
            (true, true) => "[Press 'h' for instructions. (unimplemented)]".to_string(),
            (true, false) => format!("{}[Press space to continue, 'q' to quit.]", status),
            (false, true) => format!("{}{}", status, BELL),
            (false, false) => status,
        };

        write!(
            stdout,
            "\r{}{}{}",
            Attribute::Reverse,
            banner,
            Attribute::Reset
        )
        .unwrap();
    }
}

// Break the lines on the cols of the terminal
fn break_buff(buff: &str, cols: usize) -> Vec<String> {
    let mut lines = Vec::with_capacity(buff.lines().count());

    for l in buff.lines() {
        lines.append(&mut break_line(l, cols));
    }
    lines
}

fn break_line(line: &str, cols: usize) -> Vec<String> {
    let width = UnicodeWidthStr::width(line);
    let mut lines = Vec::new();
    if width < cols {
        lines.push(line.to_string());
        return lines;
    }

    let gr_idx = UnicodeSegmentation::grapheme_indices(line, true);
    let mut last_index = 0;
    let mut total_width = 0;
    for (index, grapheme) in gr_idx {
        let width = UnicodeWidthStr::width(grapheme);
        total_width += width;

        if total_width > cols {
            lines.push(line[last_index..index].to_string());
            last_index = index;
            total_width = width;
        }
    }

    if last_index != line.len() {
        lines.push(line[last_index..].to_string());
    }
    lines
}

#[cfg(test)]
mod tests {
    use super::break_line;
    use unicode_width::UnicodeWidthStr;

    #[test]
    fn test_break_lines_long() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..200 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);
        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        assert_eq!((80, 80, 40), (widths[0], widths[1], widths[2]));
    }

    #[test]
    fn test_break_lines_short() {
        let mut test_string = String::with_capacity(100);
        for _ in 0..20 {
            test_string.push('#');
        }

        let lines = break_line(&test_string, 80);

        assert_eq!(20, lines[0].len());
    }

    #[test]
    fn test_break_line_zwj() {
        let mut test_string = String::with_capacity(1100);
        for _ in 0..20 {
            test_string.push_str("👩🏻🔬");
        }

        let lines = break_line(&test_string, 80);

        let widths: Vec<usize> = lines
            .iter()
            .map(|s| UnicodeWidthStr::width(&s[..]))
            .collect();

        // Each 👩🏻🔬 is 6 character width it break line to the closest number to 80 => 6 * 13 = 78
        assert_eq!((78, 42), (widths[0], widths[1]));
    }
}
<|endoftext|>


<commit_before>use super::Board;

#[bench]
fn generations(b: &mut ::test::Bencher) {
    b.iter(|| super::generations(Board::new(200, 200).random(), 100));
}

#[bench]
fn parallel_generations(b: &mut ::test::Bencher) {
    b.iter(|| super::parallel_generations(Board::new(200, 200).random(), 100));
}

#[bench]
fn as_parallel_generations(b: &mut ::test::Bencher) {
    b.iter(|| super::par_bridge_generations(Board::new(200, 200).random(), 100));
}
<commit_msg>rename `parallel_generations` to have unique names<commit_after>use super::Board;

#[bench]
fn generations(b: &mut ::test::Bencher) {
    b.iter(|| super::generations(Board::new(200, 200).random(), 100));
}

#[bench]
fn par_iter_generations(b: &mut ::test::Bencher) {
    b.iter(|| super::parallel_generations(Board::new(200, 200).random(), 100));
}

#[bench]
fn par_bridge_generations(b: &mut ::test::Bencher) {
    b.iter(|| super::par_bridge_generations(Board::new(200, 200).random(), 100));
}
<|endoftext|>


<commit_before>use circle::Circle;
use fx32::Fx32;
use ray2::Ray2;
use vec2::Vec2;

#[derive (Clone, Copy)]
pub enum Ray2TraceResult {
	Hit (Fx32, Vec2, Vec2),
	Miss,
}

pub fn fold_closer_result (a: Ray2TraceResult, b: Ray2TraceResult) -> Ray2TraceResult {
	match a {
		Ray2TraceResult::Miss => {
			return b;
		},
		Ray2TraceResult::Hit (a_t, ..) => {
			match b {
				Ray2TraceResult::Miss => {
					return a;
				},
				Ray2TraceResult::Hit (b_t, ..) => {
					if a_t < b_t {
						return a;
					}
					else {
						return b;
					}
				}
			}
		},
	}
}

pub fn ray_trace_circle (ray: &Ray2, circle: &Circle) -> Ray2TraceResult {
	let toward_circle = circle.center - ray.start;

	if toward_circle * ray.dir < Fx32::from_int (0) {
		// Ray is heading out of the circle's space or is already out
		return Ray2TraceResult::Miss;
	}

	// TODO: Handle ray starting inside circle

	// The following is derived from the quadratic formula and my Lua code
	let a = ray.dir.length_sq ();

	if a == Fx32::from_int (0) {
		return Ray2TraceResult::Miss;
	}

	let b = (ray.dir * toward_circle) * Fx32::from_int (-2);

	let c = toward_circle.length_sq () - circle.radius.square_64 ();

	let determinant = b * b - Fx32::from_int (4) * a * c;
	if determinant < Fx32::from_int (0) {
		return Ray2TraceResult::Miss;
	}

	let t = (-b - determinant.sqrt_64 ()) / (Fx32::from_int (2) * a);

	if t >= Fx32::from_int (0) && t <= Fx32::from_int (1) {
		let ccd_pos = ray.at (t);

		let normal = (ccd_pos - circle.center).normalized ();

		return Ray2TraceResult::Hit (t, ccd_pos, normal);
	}

	Ray2TraceResult::Miss
}
<commit_msg>Removing a negative sign<commit_after>use circle::Circle;
use fx32::Fx32;
use ray2::Ray2;
use vec2::Vec2;

#[derive (Clone, Copy)]
pub enum Ray2TraceResult {
	Hit (Fx32, Vec2, Vec2),
	Miss,
}

pub fn fold_closer_result (a: Ray2TraceResult, b: Ray2TraceResult) -> Ray2TraceResult {
	match a {
		Ray2TraceResult::Miss => {
			return b;
		},
		Ray2TraceResult::Hit (a_t, ..) => {
			match b {
				Ray2TraceResult::Miss => {
					return a;
				},
				Ray2TraceResult::Hit (b_t, ..) => {
					if a_t < b_t {
						return a;
					}
					else {
						return b;
					}
				}
			}
		},
	}
}

pub fn ray_trace_circle (ray: &Ray2, circle: &Circle) -> Ray2TraceResult {
	let toward_circle = circle.center - ray.start;

	if toward_circle * ray.dir < Fx32::from_int (0) {
		// Ray is heading out of the circle's space or is already out
		return Ray2TraceResult::Miss;
	}

	// TODO: Handle ray starting inside circle

	// The following is derived from the quadratic formula and my Lua code
	let a = ray.dir.length_sq ();

	if a == Fx32::from_int (0) {
		return Ray2TraceResult::Miss;
	}

	// This part is different from the canonical quadratic formula
	// I negated it because it will save a negation later on
	let b = (ray.dir * toward_circle) * Fx32::from_int (2);

	let c = toward_circle.length_sq () - circle.radius.square_64 ();

	let determinant = b * b - Fx32::from_int (4) * a * c;
	if determinant < Fx32::from_int (0) {
		return Ray2TraceResult::Miss;
	}

	let t = (b - determinant.sqrt_64 ()) / (Fx32::from_int (2) * a);

	if t >= Fx32::from_int (0) && t <= Fx32::from_int (1) {
		let ccd_pos = ray.at (t);

		let normal = (ccd_pos - circle.center).normalized ();

		return Ray2TraceResult::Hit (t, ccd_pos, normal);
	}

	Ray2TraceResult::Miss
}
<|endoftext|>


<commit_before>// mod shifts;
// use shifts::get_shifts_hashmap;
use super::PI;
use super::AIRY_1830_SEMI_MAJOR;
use super::AIRY_1830_SEMI_MINOR;
use super::GRS80_SEMI_MAJOR;
use super::GRS80_SEMI_MINOR;

use super::RAD;
use super::MIN_LONGITUDE;
use super::MAX_LONGITUDE;
use super::MIN_LATITUDE;
use super::MAX_LATITUDE;

const WGS84_A: f64 = GRS80_SEMI_MAJOR;
const WGS84_B: f64 = GRS80_SEMI_MINOR;

// lon and lat of true origin
const LAM0: f64 = RAD * -2.0;
const PHI0: f64 = RAD * 49.0;

// Easting and Northing of origin
const E0: f64 = 400000.;
const N0: f64 = -100000.;
// convergence factor
const F0: f64 = 0.9996012717;

const MIN_X_SHIFT: f64 = 86.275;
const MIN_Y_SHIFT: f64 = -81.603;
const MIN_Z_SHIFT: f64 = 43.982;

use std::collections::HashMap;
use ostn02_phf::ostn02_lookup;
use super::check;

// Herbie's going to have a field day with this
pub fn round_to_nearest_mm(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
    let new_x = (x * 1000.).round() as f64 / 1000.;
    let new_y = (y * 1000.).round() as f64 / 1000.;
    let new_z = (z * 1000.).round() as f64 / 1000.;
    (new_x, new_y, new_z)
}

pub fn get_ostn_ref(x: &i32, y: &i32) -> (f64, f64, f64) {
    let key = format!("{:03x}{:03x}", y, x);
    // some or None, so try! this
    let result = ostn02_lookup(&*key).unwrap();
    // if we get a hit
    let data2 = (result.0 as f64 / 1000. + MIN_X_SHIFT,
                 result.1 as f64 / 1000. + MIN_Y_SHIFT,
                 result.2 as f64 / 1000. + MIN_Z_SHIFT);
    data2
}

// Input values must be valid ETRS89 grid references
// See p20 of the transformation user guide at
// https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/navigation-technology/os-net/formats-for-developers.html
pub fn ostn02_shifts(x: &f64, y: &f64) -> (f64, f64, f64) {
    let e_index = (*x / 1000.) as i32;
    let n_index = (*y / 1000.) as i32;

    // eastings and northings of the south-west corner of the cell
    let x0 = e_index * 1000;
    let y0 = n_index * 1000;

    // The easting, northing and geoid shifts for the four corners of the cell
    // any of these could be Err, so use try!
    let s0_ref: (f64, f64, f64) = get_ostn_ref(&(e_index + 0), &(n_index + 0));
    let s1_ref: (f64, f64, f64) = get_ostn_ref(&(e_index + 1), &(n_index + 0));
    let s2_ref: (f64, f64, f64) = get_ostn_ref(&(e_index + 0), &(n_index + 1));
    let s3_ref: (f64, f64, f64) = get_ostn_ref(&(e_index + 1), &(n_index + 1));
    // only continue if we get Results for the above

    // offset within square
    let dx = *x - (x0 as f64);
    let dy = *y - (y0 as f64);

    // the python script divides by an int here, which = 0 (e.g. 300 / 1000)
    let t = dx / 1000.;
    let u = dy / 1000.;

    let f0 = (1. - t as f64) * (1. - u as f64);
    let f1 = t as f64 * (1. - u as f64);
    let f2 = (1. - t as f64) * u as f64;
    let f3 = t as f64 * u as f64;

    // bilinear interpolation, to obtain the actual shifts
    let se = f0 * s0_ref.0 + f1 * s1_ref.0 + f2 * s2_ref.0 + f3 * s3_ref.0;
    let sn = f0 * s0_ref.1 + f1 * s1_ref.1 + f2 * s2_ref.1 + f3 * s3_ref.1;
    let sg = f0 * s0_ref.2 + f1 * s1_ref.2 + f2 * s2_ref.2 + f3 * s3_ref.2;

    let (r_se, r_sn, r_sg) = round_to_nearest_mm(se, sn, sg);
    (r_se, r_sn, r_sg)

}

/// Perform Longitude, Latitude to ETRS89 conversion
///
/// # Examples
///
/// ```
/// use lonlat_bng::ostn02::convert_etrs89
/// assert_eq!((651307.003, 313255.686), convert_etrs89(&1.716073973, &52.658007833).unwrap());
#[allow(non_snake_case)]
// See Annexe B (p23) of the transformation user guide for instructions
pub fn convert_etrs89(longitude: &f64, latitude: &f64) -> Result<(f64, f64), f64> {
    // input is restricted to the UK bounding box
    // Convert bounds-checked input to degrees, or return an Err
    let lon_1: f64 = try!(check(*longitude as f32, (MIN_LONGITUDE, MAX_LONGITUDE)).map_err(|e| e)) as f64 * RAD;
    let lat_1: f64 = try!(check(*latitude as f32, (MIN_LATITUDE, MAX_LATITUDE)).map_err(|e| e)) as f64 * RAD;
    let alt = 0.0;
    // ellipsoid squared eccentricity constant
    let e2 = (WGS84_A.powf(2.) - WGS84_B.powf(2.)) / WGS84_A.powf(2.);
    let n = (WGS84_A - WGS84_B) / (WGS84_A + WGS84_B);
    let phi = RAD * *latitude;
    let lambda = RAD * *longitude;

    let sp2 = phi.sin().powf(2.);
    let nu = WGS84_A * F0 * (1. - e2 * sp2).powf(-0.5); // v
    let rho = WGS84_A * F0 * (1. - e2) * (1. - e2 * sp2).powf(-1.5);
    let eta2 = nu / rho - 1.;

    let m = compute_m(&phi, &WGS84_B, &n);

    let cp = phi.cos();
    let sp = phi.sin();
    let tp = phi.tan();
    let tp2 = tp.powf(2.);
    let tp4 = tp.powf(4.);

    let I = m + N0;
    let II = nu / 2. * sp * cp;
    let III = nu / 24. * sp * cp.powf(3.) * (5. - tp2 + 9. * eta2);
    let IIIA = nu / 720. * sp * cp.powf(5.) * (61. - 58. * tp2 + tp4);

    let IV = nu * cp;
    let V = nu / 6. * cp.powf(3.) * (nu / rho - tp2);
    let VI = nu / 120. * cp.powf(5.) * (5. - 18. * tp2 + tp4 + 14. * eta2 - 58. * tp2 * eta2);

    let l = lambda - LAM0;
    let north = I + II * l.powf(2.) + III * l.powf(4.) + IIIA * l.powf(6.);
    let east = E0 + IV * l + V * l.powf(3.) + VI * l.powf(5.);
    let (rounded_eastings, rounded_northings, _) = round_to_nearest_mm(east, north, 1.00);
    Ok((rounded_eastings, rounded_northings))
}

/// Perform Longitude, Latitude to OSGB36 conversion
///
/// # Examples
///
/// ```
/// use lonlat_bng::ostn02::convert_osgb36
/// assert_eq!((651409.792, 313177.448), convert_etrs89(&1.716073973, &52.658007833).unwrap());
#[allow(non_snake_case)]
pub fn convert_osgb36(longitude: &f64, latitude: &f64) -> Result<(f64, f64), f64> {
    // convert input to ETRS89
    let (eastings, northings) = try!(convert_etrs89(longitude, latitude).map_err(|e| e));
    let alt = 0.0;
    // obtain OSTN02 corrections, and incorporate
    let (e_shift, n_shift, _) = ostn02_shifts(&eastings, &northings);
    let (shifted_e, shifted_n) = (eastings + e_shift, northings + n_shift);
    Ok((shifted_e, shifted_n))
}

fn compute_m(phi: &f64, b: &f64, n: &f64) -> f64 {
    let p_plus = *phi + PHI0;
    let p_minus = *phi - PHI0;

    let result = *b * F0 *
                 ((1. + *n * (1. + 5. / 4. * *n * (1. + *n))) * p_minus -
                  3. * *n * (1. + *n * (1. + 7. / 8. * *n)) * p_minus.sin() * p_plus.cos() +
                  (15. / 8. * *n * (*n * (1. + *n))) * (2. * p_minus).sin() * (2. * p_plus).cos() -
                  35. / 24. * n.powf(3.) * (3. * p_minus).sin() * (3. * p_plus).cos());
    result
}
// Convert OSGB36 coordinates to ETRS89 using OSTN02 shifts
// pub fn shift_osgb36_to_etrs89(E: &f64, N: &f64) -> (f64, f64) {
//     let z0 = 0.000;
//     let epsilon = 0.00001;
//     let (dx, dy, dz) = ostn02_shifts(&E, &N);
//     let (x, y, z) = (E - dx, N - dy, dz);
//     let (last_dx, last_dy) = (dx.clone(), dy.clone());
//     while (dx - last_dx).abs() < epsilon && (dy - last_dy).abs() < epsilon {
//         let (dx, dy, dz) = ostn02_shifts(&x, &y);
//         let (x, y) = (E - dx, N - dy);
//         let (last_dx, last_dy) = (dx, dy);
//     }
//     let (x, y, z) = round_to_nearest_mm(E - dx, N - dy, z0 + dz);
//     (x, y)
// }
<commit_msg>Remove unused imports<commit_after>// mod shifts;
// use shifts::get_shifts_hashmap;
use super::GRS80_SEMI_MAJOR;
use super::GRS80_SEMI_MINOR;

use super::RAD;
use super::MIN_LONGITUDE;
use super::MAX_LONGITUDE;
use super::MIN_LATITUDE;
use super::MAX_LATITUDE;

use super::TRUE_ORIGIN_EASTING;
use super::TRUE_ORIGIN_NORTHING;

const WGS84_A: f64 = GRS80_SEMI_MAJOR;
const WGS84_B: f64 = GRS80_SEMI_MINOR;

// lon and lat of true origin
const LAM0: f64 = RAD * -2.0;
const PHI0: f64 = RAD * 49.0;

// Easting and Northing of origin
const E0: f64 = TRUE_ORIGIN_EASTING;
const N0: f64 = TRUE_ORIGIN_NORTHING;
// convergence factor
const F0: f64 = 0.9996012717;

const MIN_X_SHIFT: f64 = 86.275;
const MIN_Y_SHIFT: f64 = -81.603;
const MIN_Z_SHIFT: f64 = 43.982;

use ostn02_phf::ostn02_lookup;
use super::check;

// Herbie's going to have a field day with this
pub fn round_to_nearest_mm(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
    let new_x = (x * 1000.).round() as f64 / 1000.;
    let new_y = (y * 1000.).round() as f64 / 1000.;
    let new_z = (z * 1000.).round() as f64 / 1000.;
    (new_x, new_y, new_z)
}

pub fn get_ostn_ref(x: &i32, y: &i32) -> (f64, f64, f64) {
    let key = format!("{:03x}{:03x}", y, x);
    // some or None, so try! this
    let result = ostn02_lookup(&*key).unwrap();
    // if we get a hit
    let data2 = (result.0 as f64 / 1000. + MIN_X_SHIFT,
                 result.1 as f64 / 1000. + MIN_Y_SHIFT,
                 result.2 as f64 / 1000. + MIN_Z_SHIFT);
    data2
}

// Input values must be valid ETRS89 grid references
// See p20 of the transformation user guide at
// https://www.ordnancesurvey.co.uk/business-and-government/help-and-support/navigation-technology/os-net/formats-for-developers.html
pub fn ostn02_shifts(x: &f64, y: &f64) -> (f64, f64, f64) {
    let e_index = (*x / 1000.) as i32;
    let n_index = (*y / 1000.) as i32;

    // eastings and northings of the south-west corner of the cell
    let x0 = e_index * 1000;
    let y0 = n_index * 1000;

    // The easting, northing and geoid shifts for the four corners of the cell
    // any of these could be Err, so use try!
    let s0_ref: (f64, f64, f64) = get_ostn_ref(&(e_index + 0), &(n_index + 0));
    let s1_ref: (f64, f64, f64) = get_ostn_ref(&(e_index + 1), &(n_index + 0));
    let s2_ref: (f64, f64, f64) = get_ostn_ref(&(e_index + 0), &(n_index + 1));
    let s3_ref: (f64, f64, f64) = get_ostn_ref(&(e_index + 1), &(n_index + 1));
    // only continue if we get Results for the above

    // offset within square
    let dx = *x - (x0 as f64);
    let dy = *y - (y0 as f64);

    // the python script divides by an int here, which = 0 (e.g. 300 / 1000)
    let t = dx / 1000.;
    let u = dy / 1000.;

    let f0 = (1. - t as f64) * (1. - u as f64);
    let f1 = t as f64 * (1. - u as f64);
    let f2 = (1. - t as f64) * u as f64;
    let f3 = t as f64 * u as f64;

    // bilinear interpolation, to obtain the actual shifts
    let se = f0 * s0_ref.0 + f1 * s1_ref.0 + f2 * s2_ref.0 + f3 * s3_ref.0;
    let sn = f0 * s0_ref.1 + f1 * s1_ref.1 + f2 * s2_ref.1 + f3 * s3_ref.1;
    let sg = f0 * s0_ref.2 + f1 * s1_ref.2 + f2 * s2_ref.2 + f3 * s3_ref.2;

    let (r_se, r_sn, r_sg) = round_to_nearest_mm(se, sn, sg);
    (r_se, r_sn, r_sg)

}

/// Perform Longitude, Latitude to ETRS89 conversion
///
/// # Examples
///
/// ```
/// use lonlat_bng::ostn02::convert_etrs89
/// assert_eq!((651307.003, 313255.686), convert_etrs89(&1.716073973, &52.658007833).unwrap());
#[allow(non_snake_case)]
// See Annexe B (p23) of the transformation user guide for instructions
pub fn convert_etrs89(longitude: &f64, latitude: &f64) -> Result<(f64, f64), f64> {
    // input is restricted to the UK bounding box
    // Convert bounds-checked input to degrees, or return an Err
    let lon_1: f64 = try!(check(*longitude as f32, (MIN_LONGITUDE, MAX_LONGITUDE)).map_err(|e| e)) as f64 * RAD;
    let lat_1: f64 = try!(check(*latitude as f32, (MIN_LATITUDE, MAX_LATITUDE)).map_err(|e| e)) as f64 * RAD;
    let alt = 0.0;
    // ellipsoid squared eccentricity constant
    let e2 = (WGS84_A.powf(2.) - WGS84_B.powf(2.)) / WGS84_A.powf(2.);
    let n = (WGS84_A - WGS84_B) / (WGS84_A + WGS84_B);
    let phi = RAD * *latitude;
    let lambda = RAD * *longitude;

    let sp2 = phi.sin().powf(2.);
    let nu = WGS84_A * F0 * (1. - e2 * sp2).powf(-0.5); // v
    let rho = WGS84_A * F0 * (1. - e2) * (1. - e2 * sp2).powf(-1.5);
    let eta2 = nu / rho - 1.;

    let m = compute_m(&phi, &WGS84_B, &n);

    let cp = phi.cos();
    let sp = phi.sin();
    let tp = phi.tan();
    let tp2 = tp.powf(2.);
    let tp4 = tp.powf(4.);

    let I = m + N0;
    let II = nu / 2. * sp * cp;
    let III = nu / 24. * sp * cp.powf(3.) * (5. - tp2 + 9. * eta2);
    let IIIA = nu / 720. * sp * cp.powf(5.) * (61. - 58. * tp2 + tp4);

    let IV = nu * cp;
    let V = nu / 6. * cp.powf(3.) * (nu / rho - tp2);
    let VI = nu / 120. * cp.powf(5.) * (5. - 18. * tp2 + tp4 + 14. * eta2 - 58. * tp2 * eta2);

    let l = lambda - LAM0;
    let north = I + II * l.powf(2.) + III * l.powf(4.) + IIIA * l.powf(6.);
    let east = E0 + IV * l + V * l.powf(3.) + VI * l.powf(5.);
    let (rounded_eastings, rounded_northings, _) = round_to_nearest_mm(east, north, 1.00);
    Ok((rounded_eastings, rounded_northings))
}

/// Perform Longitude, Latitude to OSGB36 conversion
///
/// # Examples
///
/// ```
/// use lonlat_bng::ostn02::convert_osgb36
/// assert_eq!((651409.792, 313177.448), convert_etrs89(&1.716073973, &52.658007833).unwrap());
#[allow(non_snake_case)]
pub fn convert_osgb36(longitude: &f64, latitude: &f64) -> Result<(f64, f64), f64> {
    // convert input to ETRS89
    let (eastings, northings) = try!(convert_etrs89(longitude, latitude).map_err(|e| e));
    let alt = 0.0;
    // obtain OSTN02 corrections, and incorporate
    let (e_shift, n_shift, _) = ostn02_shifts(&eastings, &northings);
    let (shifted_e, shifted_n) = (eastings + e_shift, northings + n_shift);
    Ok((shifted_e, shifted_n))
}

fn compute_m(phi: &f64, b: &f64, n: &f64) -> f64 {
    let p_plus = *phi + PHI0;
    let p_minus = *phi - PHI0;

    let result = *b * F0 *
                 ((1. + *n * (1. + 5. / 4. * *n * (1. + *n))) * p_minus -
                  3. * *n * (1. + *n * (1. + 7. / 8. * *n)) * p_minus.sin() * p_plus.cos() +
                  (15. / 8. * *n * (*n * (1. + *n))) * (2. * p_minus).sin() * (2. * p_plus).cos() -
                  35. / 24. * n.powf(3.) * (3. * p_minus).sin() * (3. * p_plus).cos());
    result
}
// Convert OSGB36 coordinates to ETRS89 using OSTN02 shifts
// pub fn shift_osgb36_to_etrs89(E: &f64, N: &f64) -> (f64, f64) {
//     let z0 = 0.000;
//     let epsilon = 0.00001;
//     let (dx, dy, dz) = ostn02_shifts(&E, &N);
//     let (x, y, z) = (E - dx, N - dy, dz);
//     let (last_dx, last_dy) = (dx.clone(), dy.clone());
//     while (dx - last_dx).abs() < epsilon && (dy - last_dy).abs() < epsilon {
//         let (dx, dy, dz) = ostn02_shifts(&x, &y);
//         let (x, y) = (E - dx, N - dy);
//         let (last_dx, last_dy) = (dx, dy);
//     }
//     let (x, y, z) = round_to_nearest_mm(E - dx, N - dy, z0 + dz);
//     (x, y)
// }
<|endoftext|>


<commit_before>use lexer::{
    TokenType,
    Token,
    BinaryOp,
};

#[derive(Debug, Clone)]
pub enum Expression {
    Double(f64),
    StringLiteral(String),
    Bool(bool),
    Call(Box<Expression>, Box<Vec<Expression>>),
    Operation(Box<Expression>, BinaryOp, Box<Expression>),
}

#[derive(Debug, Clone)]
pub enum Statement {

}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Parser {
    source: Vec<Token>,
    pos:    usize,
}

impl Parser {
    pub fn new(source: Vec<Token>) -> Parser {
        let parser = Parser {
            source: source,
            pos:    0,
        };

        parser
    }

    fn guard_look(source: Vec<Token>, pos: usize) -> Token {
        if pos > source.len() - 1 {
            return source[source.len() - 1].clone()
        }

        source[pos].clone()
    }

    fn look(&self, offset: usize) -> &Token {
        if self.pos + offset > self.source.len() - 1 {
            return &self.source[self.source.len() - 1]
        }

        &self.source[self.pos + offset]
    }

    fn has_next(&self) -> bool {
        self.pos < self.source.len() - 1
    }

    fn parse_word(&mut self) -> Result<Expression, String> {
        match &self.look(0).token_type {
            &TokenType::NumberLiteral(ref n) => {
                match n.parse::<f64>() {
                    Ok(i)  => Ok(Expression::Double(i)),
                    Err(_) => Err(String::from("failed to parse number literal")),
                }
            },

            &TokenType::Bool(b) => Ok(Expression::Bool(b)),

            _ => {
                Err(String::from("v bad expression, didn't parse correctly"))
            }
        }
    }

    fn parse_bin_op(&mut self, expr: Expression) -> Result<Expression, String> {
        let mut expr_list = vec!(expr);
        let mut oper_list: Vec<(BinaryOp, u8)> = Vec::new();

        match &self.look(0).token_type {
            &TokenType::BinaryOp((ref b, ref prec)) => {
                oper_list.push((b.clone(), prec.clone()));
            },

            _ => {
                Err(String::from("failed to parse binary operation")).unwrap()
            },
        }

        self.pos += 1;

        expr_list.push(try!(self.parse_word()));

        let mut done = false;

        while expr_list.len() > 1 {
            let a = Self::guard_look(self.source.clone(), self.pos.clone());

            if !done && self.has_next() {
                let (op, prec) = match &a.token_type {
                    &TokenType::BinaryOp((ref b, ref prec)) => {
                        (b, prec)
                    },

                    _ => {
                        self.pos -= 1;
                        done = true;

                        continue
                    },
                };

                if prec > &oper_list.last().unwrap().1 {

                    let left = expr_list.pop().unwrap();
                    let right = expr_list.pop().unwrap();

                    expr_list.push(Expression::Operation(
                        Box::new(left),
                        oper_list.pop().unwrap().0,
                        Box::new(right),
                    ));

                    self.pos += 1;

                    expr_list.push(try!(self.parse_word()));
                    oper_list.push((op.clone(), prec.clone()));

                    continue
                }

                let left  = expr_list.pop().unwrap();
                let right = expr_list.pop().unwrap();

                expr_list.push(Expression::Operation(
                    Box::new(left), oper_list.pop().unwrap().0, Box::new(right),
                ));
            }
        }

        Ok(expr_list.pop().unwrap())
    }
}<commit_msg>Implement call and expression parsing<commit_after>use lexer::{
    TokenType,
    Token,
    BinaryOp,
};

#[derive(Debug, Clone)]
pub enum Expression {
    DoubleLiteral(f64),
    StringLiteral(String),
    Bool(bool),
    Call(Box<Expression>, Box<Vec<Expression>>),
    Operation(Box<Expression>, BinaryOp, Box<Expression>),
}

#[derive(Debug, Clone)]
pub enum Statement {

}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Parser {
    source: Vec<Token>,
    pos:    usize,
}

impl Parser {
    pub fn new(source: Vec<Token>) -> Parser {
        let parser = Parser {
            source: source,
            pos:    0,
        };

        parser
    }

    fn guard_look(source: Vec<Token>, pos: usize) -> Token {
        if pos > source.len() - 1 {
            return source[source.len() - 1].clone()
        }

        source[pos].clone()
    }

    fn look(&self, offset: usize) -> &Token {
        if self.pos + offset > self.source.len() - 1 {
            return &self.source[self.source.len() - 1]
        }

        &self.source[self.pos + offset]
    }

    fn has_next(&self) -> bool {
        self.pos < self.source.len() - 1
    }

    fn match_current_token(&self, t: TokenType) -> Result<Token, String> {
        let a = Self::guard_look(self.source.clone(), self.pos.clone());

        match a.token_type == t {
            true  => Ok(a.clone()),
            false => Err(format!(
                "expected {:?} but found {:?}", t, a,
            )),
        }
    }

    fn parse_word(&mut self) -> Result<Expression, String> {
        match Self::guard_look(self.source.clone(), self.pos.clone()).token_type {
            TokenType::NumberLiteral(n) => {
                match n.parse::<f64>() {
                    Ok(i)  => Ok(Expression::DoubleLiteral(i)),
                    Err(_) => Err(String::from("failed to parse number literal")),
                }
            },

            TokenType::Bool(b) => Ok(Expression::Bool(b)),
            TokenType::StringLiteral(s) => Ok(Expression::StringLiteral(s)),

            TokenType::LParen => {
                self.pos += 1;

                let expr = try!(self.parse_expression());

                try!(self.match_current_token(TokenType::RParen));

                self.pos += 1;

                if Self::guard_look(self.source.clone(), self.pos.clone()).token_type == TokenType::LParen {
                    return self.parse_call(expr)
                }

                self.pos -= 1;

                Ok(expr)
            },

            _ => {
                Err(String::from("v bad expression, didn't parse correctly"))
            }
        }
    }

    fn parse_bin_op(&mut self, expr: Expression) -> Result<Expression, String> {
        let mut expr_list = vec!(expr);
        let mut oper_list: Vec<(BinaryOp, u8)> = Vec::new();

        match &self.look(0).token_type {
            &TokenType::BinaryOp((ref b, ref prec)) => {
                oper_list.push((b.clone(), prec.clone()));
            },

            _ => {
                Err(String::from("failed to parse binary operation")).unwrap()
            },
        }

        self.pos += 1;

        expr_list.push(try!(self.parse_word()));

        let mut done = false;

        while expr_list.len() > 1 {
            let a = Self::guard_look(self.source.clone(), self.pos.clone());

            if !done && self.has_next() {
                let (op, prec) = match &a.token_type {
                    &TokenType::BinaryOp((ref b, ref prec)) => {
                        (b, prec)
                    },

                    _ => {
                        self.pos -= 1;
                        done = true;

                        continue
                    },
                };

                if prec > &oper_list.last().unwrap().1 {

                    let left = expr_list.pop().unwrap();
                    let right = expr_list.pop().unwrap();

                    expr_list.push(Expression::Operation(
                        Box::new(left),
                        oper_list.pop().unwrap().0,
                        Box::new(right),
                    ));

                    self.pos += 1;

                    expr_list.push(try!(self.parse_word()));
                    oper_list.push((op.clone(), prec.clone()));

                    continue
                }

                let left  = expr_list.pop().unwrap();
                let right = expr_list.pop().unwrap();

                expr_list.push(Expression::Operation(
                    Box::new(left), oper_list.pop().unwrap().0, Box::new(right),
                ));
            }
        }

        Ok(expr_list.pop().unwrap())
    }

    fn parse_expression(&mut self) -> Result<Expression, String> {
        let expr = try!(self.parse_word());

        self.pos += 1;

        if self.source[self.pos..].len() > 0 {

            let is_bin_op = match Self::guard_look(self.source.clone(), self.pos.clone()).token_type {
                TokenType::BinaryOp((_, _)) => true,
                _                => false,
            };

            if is_bin_op {
                return self.parse_bin_op(expr)
            }

            self.pos -= 1;
        }

        Ok(expr)
    }

    fn parse_call(&mut self, callee: Expression) -> Result<Expression, String> {
        let mut stack = Vec::new();

        self.pos += 1;

        while Self::guard_look(self.source.clone(), self.pos.clone()).token_type != TokenType::RParen {
            stack.push(try!(self.parse_expression()));

            self.pos += 1;

            if Self::guard_look(self.source.clone(), self.pos.clone()).token_type == TokenType::Comma {
                self.pos += 1;
            }
        }

        Ok(Expression::Call(Box::new(callee), Box::new(stack)))
    }
}<|endoftext|>