<commit_before>use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::path::PathBuf;

use serde_json::Value;

use crate::protocol::{
    read_message, write_error_response, write_notification, write_success_response,
    write_success_result, ErrorCodes, Message, NotificationMessage, RequestMessage, ResponseError,
};

use crate::definition;
use crate::import::{check_imports, ImportedFiles};
use crate::mojomast::MojomAst;
use crate::{Error, Result};

#[derive(PartialEq)]
enum State {
    Initialized,
    ShuttingDown,
}

struct ServerContext {
    state: State,
    // Workspace root path.
    root_path: std::path::PathBuf,
    // True when the previous text document has errors.
    has_error: bool,
    // Contains the current document text and ast. None when the text is an
    // invalid mojom.
    ast: Option<MojomAst>,
    // Parsed mojom files that are imported from the current document.
    imported_files: Option<ImportedFiles>,
    // Set when `exit` notification is received.
    exit_code: Option<i32>,
}

impl ServerContext {
    fn new(root_path: PathBuf) -> ServerContext {
        ServerContext {
            state: State::Initialized,
            root_path: root_path,
            has_error: false,
            ast: None,
            imported_files: None,
            exit_code: None,
        }
    }
}

fn create_server_capabilities() -> lsp_types::ServerCapabilities {
    let options = lsp_types::TextDocumentSyncOptions {
        open_close: Some(true),
        change: Some(lsp_types::TextDocumentSyncKind::Full),
        will_save: None,
        will_save_wait_until: None,
        save: None,
    };

    let text_document_sync = lsp_types::TextDocumentSyncCapability::Options(options);

    lsp_types::ServerCapabilities {
        text_document_sync: Some(text_document_sync),
        hover_provider: None,
        completion_provider: None,
        signature_help_provider: None,
        definition_provider: Some(true),
        type_definition_provider: None,
        implementation_provider: None,
        references_provider: None,
        document_highlight_provider: None,
        document_symbol_provider: None,
        workspace_symbol_provider: None,
        code_action_provider: None,
        code_lens_provider: None,
        document_formatting_provider: None,
        document_range_formatting_provider: None,
        document_on_type_formatting_provider: None,
        rename_provider: None,
        color_provider: None,
        folding_range_provider: None,
        execute_command_provider: None,
        workspace: None,
    }
}

// Requests

fn get_request_params<P: serde::de::DeserializeOwned>(
    params: Value,
) -> std::result::Result<P, ResponseError> {
    serde_json::from_value::<P>(params)
        .map_err(|err| ResponseError::new(ErrorCodes::InvalidRequest, err.to_string()))
}

fn handle_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: RequestMessage,
) -> Result<()> {
    let id = msg.id;
    let method = msg.method.as_str();

    use lsp_types::request::*;
    let res = match method {
        Initialize::METHOD => initialize_request(),
        Shutdown::METHOD => shutdown_request(ctx),
        GotoDefinition::METHOD => get_request_params(msg.params)
            .and_then(|params| goto_definition_request(writer, ctx, params)),
        _ => {
            eprintln!("Received unimplemented request: {:?}", msg);
            unimplemented!();
        }
    };
    match res {
        Ok(res) => write_success_response(writer, id, res)?,
        Err(error) => write_error_response(writer, id, error)?,
    }
    Ok(())
}

type RequestResult = std::result::Result<Value, ResponseError>;

fn initialize_request() -> RequestResult {
    // The server has been initialized already.
    let error_message = "Unexpected initialize message".to_owned();
    Err(ResponseError::new(
        ErrorCodes::ServerNotInitialized,
        error_message,
    ))
}

fn shutdown_request(ctx: &mut ServerContext) -> RequestResult {
    ctx.state = State::ShuttingDown;
    Ok(Value::Null)
}

fn _get_offset_from_position(text: &str, pos: lsp_types::Position) -> usize {
    let pos_line = pos.line as usize;
    let pos_col = pos.character as usize;
    let mut offset = 0;
    for (i, line) in text.lines().enumerate() {
        if i == pos_line {
            break;
        }
        offset += line.len() + 1;
    }
    offset + pos_col
}

#[inline(always)]
fn is_identifier_char(ch: char) -> bool {
    ch.is_ascii_alphanumeric() || ch == '_' || ch == '.'
}

fn get_identifier(text: &str, pos: lsp_types::Position) -> &str {
    // TODO: The current implementation isn't accurate.

    let offset = _get_offset_from_position(text, pos);
    let mut s = offset;
    for ch in text[..offset].chars().rev() {
        if !is_identifier_char(ch) {
            break;
        }
        s -= 1;
    }
    let mut e = offset;
    for ch in text[offset..].chars() {
        if !is_identifier_char(ch) {
            break;
        }
        e += 1;
    }
    &text[s..e]
}

fn find_definition_in_doc(ast: &MojomAst, ident: &str) -> Option<lsp_types::Location> {
    definition::find_definition(ident, ast)
}

fn find_definition_in_imports(ctx: &ServerContext, ident: &str) -> Option<lsp_types::Location> {
    ctx.imported_files
        .as_ref()
        .and_then(|ref imported_files| imported_files.find_definition(ident))
}

fn goto_definition_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::TextDocumentPositionParams,
) -> RequestResult {
    let is_same_uri = if let Some(ref ast) = ctx.ast {
        params.text_document.uri == ast.uri
    } else {
        false
    };
    if !is_same_uri {
        // Invalidate the current document.
        use std::io::Read;
        let uri = params.text_document.uri.clone();
        let path = uri.to_file_path().unwrap();
        let mut text = String::new();
        std::fs::File::open(path)
            .unwrap()
            .read_to_string(&mut text)
            .unwrap();

        match _check_syntax(writer, ctx, text, uri) {
            Ok(_) => (),
            Err(err) => {
                // TODO: Should return an Err that indicates the file specified
                // by `uri` isn't a vailid mojom file.
                eprintln!("{:#?}", err);
                unimplemented!();
            }
        }
    }

    if let Some(ref ast) = &ctx.ast {
        let ident = get_identifier(&ast.text, params.position);
        let loc = find_definition_in_doc(ast, ident).or(find_definition_in_imports(ctx, ident));
        let res = match loc {
            Some(loc) => serde_json::to_value(loc).unwrap(),
            None => Value::Null,
        };
        Ok(res)
    } else {
        Ok(Value::Null)
    }
}

// Notifications

fn get_params<P: serde::de::DeserializeOwned>(params: Value) -> Result<P> {
    serde_json::from_value::<P>(params).map_err(|err| Error::ProtocolError(err.to_string()))
}

fn do_nothing(msg: &NotificationMessage) -> Result<()> {
    eprintln!("Received `{}` but do nothing.", msg.method.as_str());
    Ok(())
}

fn handle_notification(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: NotificationMessage,
) -> Result<()> {
    let method = msg.method.as_str();
    eprintln!("Got notification: {}", method);

    use lsp_types::notification::*;
    match msg.method.as_str() {
        Exit::METHOD => exit_notification(ctx),
        DidOpenTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_open_text_document(writer, ctx, params))
        }
        DidChangeTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_change_text_document(writer, ctx, params))
        }
        // Accept following notifications but do nothing.
        DidChangeConfiguration::METHOD => do_nothing(&msg),
        WillSaveTextDocument::METHOD => do_nothing(&msg),
        DidSaveTextDocument::METHOD => do_nothing(&msg),
        _ => {
            eprintln!("Received unimplemented notification: {:#?}", msg);
            Ok(())
        }
    }
}

fn publish_diagnotics(
    writer: &mut impl Write,
    params: lsp_types::PublishDiagnosticsParams,
) -> Result<()> {
    // TODO: Don't use unwrap().
    let params = serde_json::to_value(&params).unwrap();
    write_notification(writer, "textDocument/publishDiagnostics", params)
}

fn convert_error_position(line_col: &mojom_parser::LineColLocation) -> lsp_types::Range {
    let (start, end) = match line_col {
        mojom_parser::LineColLocation::Pos((line, col)) => {
            let start = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            // ???
            let end = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            (start, end)
        }
        mojom_parser::LineColLocation::Span(start, end) => {
            // `start` and `end` are tuples like (line, col).
            let start = lsp_types::Position {
                line: start.0 as u64 - 1,
                character: start.1 as u64 - 1,
            };
            let end = lsp_types::Position {
                line: end.0 as u64 - 1,
                character: end.1 as u64 - 1,
            };
            (start, end)
        }
    };
    lsp_types::Range {
        start: start,
        end: end,
    }
}

fn _check_syntax(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    text: String,
    uri: lsp_types::Url,
) -> Result<()> {
    match MojomAst::new(uri.clone(), text) {
        Ok(ast) => {
            ctx.ast = Some(ast);
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![],
            };
            publish_diagnotics(writer, params)?;
            ctx.has_error = false;
        }
        Err(err) => {
            let range = convert_error_position(&err.line_col);
            let diagnostic = lsp_types::Diagnostic {
                range: range,
                severity: Some(lsp_types::DiagnosticSeverity::Error),
                code: Some(lsp_types::NumberOrString::String("mojom".to_owned())),
                source: Some("mojom-lsp".to_owned()),
                message: err.to_string(),
                related_information: None,
            };
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![diagnostic],
            };
            publish_diagnotics(writer, params)?;
            ctx.ast = None;
            ctx.has_error = true;
        }
    }
    // TODO: Tentative
    if let Some(ast) = &ctx.ast {
        let imported_files = check_imports(&ctx.root_path, ast);
        ctx.imported_files = Some(imported_files);
    }
    Ok(())
}

fn exit_notification(ctx: &mut ServerContext) -> Result<()> {
    // https://microsoft.github.io/language-server-protocol/specification#exit
    if ctx.state == State::ShuttingDown {
        ctx.exit_code = Some(0);
    } else {
        ctx.exit_code = Some(1);
    }
    Ok(())
}

fn did_open_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidOpenTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    _check_syntax(writer, ctx, params.text_document.text, uri)
}

fn did_change_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidChangeTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    let content = params
        .content_changes
        .iter()
        .map(|i| i.text.to_owned())
        .collect::<Vec<_>>();
    let text = content.join("");
    _check_syntax(writer, ctx, text, uri)
}

// Initialization

fn initialize(
    reader: &mut impl BufRead,
    writer: &mut impl Write,
) -> Result<lsp_types::InitializeParams> {
    let message = read_message(reader)?;

    let (id, params) = match message {
        Message::Request(req) => req.cast::<lsp_types::request::Initialize>()?,
        _ => {
            // TODO: Gracefully handle `exit` and `shutdown` messages.
            let error_message = format!("Expected initialize message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    let capabilities = create_server_capabilities();
    let res = lsp_types::InitializeResult {
        capabilities: capabilities,
    };
    write_success_result(writer, id, res)?;

    let message = read_message(reader)?;
    match message {
        Message::Notofication(notif) => notif.cast::<lsp_types::notification::Initialized>()?,
        _ => {
            let error_message = format!("Expected initialized message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    Ok(params)
}

fn get_root_path(params: &lsp_types::InitializeParams) -> PathBuf {
    if let Some(ref uri) = params.root_uri.as_ref() {
        if uri.scheme() == "file" {
            if let Ok(path) = uri.to_file_path() {
                return path;
            }
        }
    }
    if let Some(ref path) = params.root_path.as_ref() {
        return PathBuf::from(path);
    }
    PathBuf::new()
}

// Returns exit code.
pub fn start() -> Result<i32> {
    let mut reader = BufReader::new(io::stdin());
    let mut writer = BufWriter::new(io::stdout());

    let params = initialize(&mut reader, &mut writer)?;

    let root_path = get_root_path(&params);
    eprintln!("root_path: {:?}", root_path);

    let mut ctx = ServerContext::new(root_path);

    loop {
        eprintln!("Reading message...");
        let message = read_message(&mut reader)?;
        match message {
            Message::Request(request) => handle_request(&mut writer, &mut ctx, request)?,
            Message::Notofication(notification) => {
                handle_notification(&mut writer, &mut ctx, notification)?
            }
            _ => unimplemented!(),
        };

        if let Some(exit_code) = ctx.exit_code {
            return Ok(exit_code);
        }
    }
}
<commit_msg>server: Add workaround for Eglot<commit_after>use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::path::PathBuf;

use serde_json::Value;

use crate::protocol::{
    read_message, write_error_response, write_notification, write_success_response,
    write_success_result, ErrorCodes, Message, NotificationMessage, RequestMessage, ResponseError,
};

use crate::definition;
use crate::import::{check_imports, ImportedFiles};
use crate::mojomast::MojomAst;
use crate::{Error, Result};

#[derive(PartialEq)]
enum State {
    Initialized,
    ShuttingDown,
}

struct ServerContext {
    state: State,
    // Workspace root path.
    root_path: std::path::PathBuf,
    // True when the previous text document has errors.
    has_error: bool,
    // Contains the current document text and ast. None when the text is an
    // invalid mojom.
    ast: Option<MojomAst>,
    // Parsed mojom files that are imported from the current document.
    imported_files: Option<ImportedFiles>,
    // Set when `exit` notification is received.
    exit_code: Option<i32>,
}

impl ServerContext {
    fn new(root_path: PathBuf) -> ServerContext {
        ServerContext {
            state: State::Initialized,
            root_path: root_path,
            has_error: false,
            ast: None,
            imported_files: None,
            exit_code: None,
        }
    }
}

fn create_server_capabilities() -> lsp_types::ServerCapabilities {
    let options = lsp_types::TextDocumentSyncOptions {
        open_close: Some(true),
        change: Some(lsp_types::TextDocumentSyncKind::Full),
        will_save: None,
        will_save_wait_until: None,
        save: None,
    };

    let text_document_sync = lsp_types::TextDocumentSyncCapability::Options(options);

    lsp_types::ServerCapabilities {
        text_document_sync: Some(text_document_sync),
        hover_provider: None,
        completion_provider: None,
        signature_help_provider: None,
        definition_provider: Some(true),
        type_definition_provider: None,
        implementation_provider: None,
        references_provider: None,
        document_highlight_provider: None,
        document_symbol_provider: None,
        workspace_symbol_provider: None,
        code_action_provider: None,
        code_lens_provider: None,
        document_formatting_provider: None,
        document_range_formatting_provider: None,
        document_on_type_formatting_provider: None,
        rename_provider: None,
        color_provider: None,
        folding_range_provider: None,
        execute_command_provider: None,
        workspace: None,
    }
}

// Requests

fn get_request_params<P: serde::de::DeserializeOwned>(
    params: Value,
) -> std::result::Result<P, ResponseError> {
    serde_json::from_value::<P>(params)
        .map_err(|err| ResponseError::new(ErrorCodes::InvalidRequest, err.to_string()))
}

fn handle_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: RequestMessage,
) -> Result<()> {
    let id = msg.id;
    let method = msg.method.as_str();

    // Workaround for Eglot. It sends "exit" as a request, not as a notification.
    if method == "exit" {
        return exit_notification(ctx);
    }

    use lsp_types::request::*;
    let res = match method {
        Initialize::METHOD => initialize_request(),
        Shutdown::METHOD => shutdown_request(ctx),
        GotoDefinition::METHOD => get_request_params(msg.params)
            .and_then(|params| goto_definition_request(writer, ctx, params)),
        _ => {
            eprintln!("Received unimplemented request: {:?}", msg);
            unimplemented!();
        }
    };
    match res {
        Ok(res) => write_success_response(writer, id, res)?,
        Err(error) => write_error_response(writer, id, error)?,
    }
    Ok(())
}

type RequestResult = std::result::Result<Value, ResponseError>;

fn initialize_request() -> RequestResult {
    // The server has been initialized already.
    let error_message = "Unexpected initialize message".to_owned();
    Err(ResponseError::new(
        ErrorCodes::ServerNotInitialized,
        error_message,
    ))
}

fn shutdown_request(ctx: &mut ServerContext) -> RequestResult {
    ctx.state = State::ShuttingDown;
    Ok(Value::Null)
}

fn _get_offset_from_position(text: &str, pos: lsp_types::Position) -> usize {
    let pos_line = pos.line as usize;
    let pos_col = pos.character as usize;
    let mut offset = 0;
    for (i, line) in text.lines().enumerate() {
        if i == pos_line {
            break;
        }
        offset += line.len() + 1;
    }
    offset + pos_col
}

#[inline(always)]
fn is_identifier_char(ch: char) -> bool {
    ch.is_ascii_alphanumeric() || ch == '_' || ch == '.'
}

fn get_identifier(text: &str, pos: lsp_types::Position) -> &str {
    // TODO: The current implementation isn't accurate.

    let offset = _get_offset_from_position(text, pos);
    let mut s = offset;
    for ch in text[..offset].chars().rev() {
        if !is_identifier_char(ch) {
            break;
        }
        s -= 1;
    }
    let mut e = offset;
    for ch in text[offset..].chars() {
        if !is_identifier_char(ch) {
            break;
        }
        e += 1;
    }
    &text[s..e]
}

fn find_definition_in_doc(ast: &MojomAst, ident: &str) -> Option<lsp_types::Location> {
    definition::find_definition(ident, ast)
}

fn find_definition_in_imports(ctx: &ServerContext, ident: &str) -> Option<lsp_types::Location> {
    ctx.imported_files
        .as_ref()
        .and_then(|ref imported_files| imported_files.find_definition(ident))
}

fn goto_definition_request(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::TextDocumentPositionParams,
) -> RequestResult {
    let is_same_uri = if let Some(ref ast) = ctx.ast {
        params.text_document.uri == ast.uri
    } else {
        false
    };
    if !is_same_uri {
        // Invalidate the current document.
        use std::io::Read;
        let uri = params.text_document.uri.clone();
        let path = uri.to_file_path().unwrap();
        let mut text = String::new();
        std::fs::File::open(path)
            .unwrap()
            .read_to_string(&mut text)
            .unwrap();

        match _check_syntax(writer, ctx, text, uri) {
            Ok(_) => (),
            Err(err) => {
                // TODO: Should return an Err that indicates the file specified
                // by `uri` isn't a vailid mojom file.
                eprintln!("{:#?}", err);
                unimplemented!();
            }
        }
    }

    if let Some(ref ast) = &ctx.ast {
        let ident = get_identifier(&ast.text, params.position);
        let loc = find_definition_in_doc(ast, ident).or(find_definition_in_imports(ctx, ident));
        let res = match loc {
            Some(loc) => serde_json::to_value(loc).unwrap(),
            None => Value::Null,
        };
        Ok(res)
    } else {
        Ok(Value::Null)
    }
}

// Notifications

fn get_params<P: serde::de::DeserializeOwned>(params: Value) -> Result<P> {
    serde_json::from_value::<P>(params).map_err(|err| Error::ProtocolError(err.to_string()))
}

fn do_nothing(msg: &NotificationMessage) -> Result<()> {
    eprintln!("Received `{}` but do nothing.", msg.method.as_str());
    Ok(())
}

fn handle_notification(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    msg: NotificationMessage,
) -> Result<()> {
    let method = msg.method.as_str();
    eprintln!("Got notification: {}", method);

    use lsp_types::notification::*;
    match msg.method.as_str() {
        Exit::METHOD => exit_notification(ctx),
        DidOpenTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_open_text_document(writer, ctx, params))
        }
        DidChangeTextDocument::METHOD => {
            get_params(msg.params).and_then(|params| did_change_text_document(writer, ctx, params))
        }
        // Accept following notifications but do nothing.
        DidChangeConfiguration::METHOD => do_nothing(&msg),
        WillSaveTextDocument::METHOD => do_nothing(&msg),
        DidSaveTextDocument::METHOD => do_nothing(&msg),
        _ => {
            eprintln!("Received unimplemented notification: {:#?}", msg);
            Ok(())
        }
    }
}

fn publish_diagnotics(
    writer: &mut impl Write,
    params: lsp_types::PublishDiagnosticsParams,
) -> Result<()> {
    // TODO: Don't use unwrap().
    let params = serde_json::to_value(&params).unwrap();
    write_notification(writer, "textDocument/publishDiagnostics", params)
}

fn convert_error_position(line_col: &mojom_parser::LineColLocation) -> lsp_types::Range {
    let (start, end) = match line_col {
        mojom_parser::LineColLocation::Pos((line, col)) => {
            let start = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            // ???
            let end = lsp_types::Position {
                line: *line as u64 - 1,
                character: *col as u64 - 1,
            };
            (start, end)
        }
        mojom_parser::LineColLocation::Span(start, end) => {
            // `start` and `end` are tuples like (line, col).
            let start = lsp_types::Position {
                line: start.0 as u64 - 1,
                character: start.1 as u64 - 1,
            };
            let end = lsp_types::Position {
                line: end.0 as u64 - 1,
                character: end.1 as u64 - 1,
            };
            (start, end)
        }
    };
    lsp_types::Range {
        start: start,
        end: end,
    }
}

fn _check_syntax(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    text: String,
    uri: lsp_types::Url,
) -> Result<()> {
    match MojomAst::new(uri.clone(), text) {
        Ok(ast) => {
            ctx.ast = Some(ast);
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![],
            };
            publish_diagnotics(writer, params)?;
            ctx.has_error = false;
        }
        Err(err) => {
            let range = convert_error_position(&err.line_col);
            let diagnostic = lsp_types::Diagnostic {
                range: range,
                severity: Some(lsp_types::DiagnosticSeverity::Error),
                code: Some(lsp_types::NumberOrString::String("mojom".to_owned())),
                source: Some("mojom-lsp".to_owned()),
                message: err.to_string(),
                related_information: None,
            };
            let params = lsp_types::PublishDiagnosticsParams {
                uri: uri,
                diagnostics: vec![diagnostic],
            };
            publish_diagnotics(writer, params)?;
            ctx.ast = None;
            ctx.has_error = true;
        }
    }
    // TODO: Tentative
    if let Some(ast) = &ctx.ast {
        let imported_files = check_imports(&ctx.root_path, ast);
        ctx.imported_files = Some(imported_files);
    }
    Ok(())
}

fn exit_notification(ctx: &mut ServerContext) -> Result<()> {
    // https://microsoft.github.io/language-server-protocol/specification#exit
    if ctx.state == State::ShuttingDown {
        ctx.exit_code = Some(0);
    } else {
        ctx.exit_code = Some(1);
    }
    Ok(())
}

fn did_open_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidOpenTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    _check_syntax(writer, ctx, params.text_document.text, uri)
}

fn did_change_text_document(
    writer: &mut impl Write,
    ctx: &mut ServerContext,
    params: lsp_types::DidChangeTextDocumentParams,
) -> Result<()> {
    let uri = params.text_document.uri.clone();
    let content = params
        .content_changes
        .iter()
        .map(|i| i.text.to_owned())
        .collect::<Vec<_>>();
    let text = content.join("");
    _check_syntax(writer, ctx, text, uri)
}

// Initialization

fn initialize(
    reader: &mut impl BufRead,
    writer: &mut impl Write,
) -> Result<lsp_types::InitializeParams> {
    let message = read_message(reader)?;

    let (id, params) = match message {
        Message::Request(req) => req.cast::<lsp_types::request::Initialize>()?,
        _ => {
            // TODO: Gracefully handle `exit` and `shutdown` messages.
            let error_message = format!("Expected initialize message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    let capabilities = create_server_capabilities();
    let res = lsp_types::InitializeResult {
        capabilities: capabilities,
    };
    write_success_result(writer, id, res)?;

    let message = read_message(reader)?;
    match message {
        Message::Notofication(notif) => notif.cast::<lsp_types::notification::Initialized>()?,
        _ => {
            let error_message = format!("Expected initialized message but got {:?}", message);
            return Err(Error::ProtocolError(error_message));
        }
    };

    Ok(params)
}

fn get_root_path(params: &lsp_types::InitializeParams) -> PathBuf {
    if let Some(ref uri) = params.root_uri.as_ref() {
        if uri.scheme() == "file" {
            if let Ok(path) = uri.to_file_path() {
                return path;
            }
        }
    }
    if let Some(ref path) = params.root_path.as_ref() {
        return PathBuf::from(path);
    }
    PathBuf::new()
}

// Returns exit code.
pub fn start() -> Result<i32> {
    let mut reader = BufReader::new(io::stdin());
    let mut writer = BufWriter::new(io::stdout());

    let params = initialize(&mut reader, &mut writer)?;

    let root_path = get_root_path(&params);
    eprintln!("root_path: {:?}", root_path);

    let mut ctx = ServerContext::new(root_path);

    loop {
        eprintln!("Reading message...");
        let message = read_message(&mut reader)?;
        match message {
            Message::Request(request) => handle_request(&mut writer, &mut ctx, request)?,
            Message::Notofication(notification) => {
                handle_notification(&mut writer, &mut ctx, notification)?
            }
            _ => unimplemented!(),
        };

        if let Some(exit_code) = ctx.exit_code {
            return Ok(exit_code);
        }
    }
}
<|endoftext|>


<commit_before>// src/models/transaction_model.rs

/// Transaction Model

// Import Modules
// External
use ::bson::oid::ObjectId;
use ::chrono::{DateTime, Duration, Local, TimeZone};
use ::chrono::offset::utc::UTC;
// Utilities
use ::common::validation::validators as Validators;
use ::common::validation::validation_result::{ValidationResult};

/// Transaction
#[derive(Clone)]
pub struct TransactionModel {
    pub id: Option<ObjectId>,
    pub money_map_id: Option<ObjectId>,
    pub account_id: Option<ObjectId>,
    pub datetime: Option<DateTime<UTC>>,
    pub payee: Option<String>,
    pub description: Option<String>,
    pub category_id: Option<ObjectId>,
    pub amount: Option<f64>,
    pub transaction_type: Option<String>,
    pub status: Option<String>
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PubTransactionModel {
    pub id: Option<ObjectId>,
    pub money_map_id: Option<ObjectId>,
    pub account_id: Option<ObjectId>,
    pub datetime: Option<String>,
    pub payee: Option<String>,
    pub description: Option<String>,
    pub category_id: Option<ObjectId>,
    pub amount: Option<f64>,
    pub transaction_type: Option<String>,
    pub status: Option<String>
}

// Transaction Model Methods
impl TransactionModel{

    /// Create TransactionModel from PubTransactionModel
    ///
    /// # Arguments
    /// `account` - PubTransactionModel
    ///
    /// # Returns
    /// `TransactionModel`
    pub fn new(transaction: &PubTransactionModel) -> TransactionModel{
        TransactionModel{
            id: transaction.get_id(),
            money_map_id: transaction.get_money_map_id(),
            account_id: transaction.get_account_id(),
            datetime: None,
            payee: transaction.get_payee(),
            description: transaction.get_description(),
            category_id: transaction.get_category_id(),
            amount: transaction.get_amount(),
            transaction_type: transaction.get_transaction_type(),
            status: transaction.get_status()
        }
    }

    /// Get ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - ID
    pub fn get_id(&self) -> Option<ObjectId>{
        self.id.clone()
    }

    /// Set ID
    ///
    /// # Arguments
    /// `self`
    pub fn set_id(&mut self, id: ObjectId) {
        self.id = Some(id);
    }

    /// Get Money Map ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Money Map ID
    pub fn get_money_map_id(&mut self) -> Option<ObjectId>{
        self.money_map_id.clone()
    }

    /// Get Account ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Account ID
    pub fn get_account_id(&self) -> Option<ObjectId>{
        self.account_id.clone()
    }

    /// Get Datetime
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<DateTime<UTC>>` - Datetime
    pub fn get_datetime(&self) -> Option<DateTime<UTC>>{
        self.datetime.clone()
    }

    /// Set Datetime
    ///
    /// # Arguments
    /// `self`
    /// `datetime`
    pub fn set_datetime(&mut self, datetime: Option<DateTime<UTC>>){
        self.datetime = datetime;
    }

    /// Get Payee
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Entity
    pub fn get_payee(&self) -> Option<String>{
        self.payee.clone()
    }

    /// Get Description
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - description
    pub fn get_description(&self) -> Option<String>{
        self.description.clone()
    }

    /// Get Category ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Category ID
    pub fn get_category_id(&self) -> Option<ObjectId>{
        self.category_id.clone()
    }

    /// Get Amount
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<f64>` - Amount
    pub fn get_amount(&self) -> Option<f64>{
        self.amount.clone()
    }

    /// Get Transaction Type
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Transaction Type
    pub fn get_transaction_type(&self) -> Option<String>{
        self.transaction_type.clone()
    }

    /// Get Status
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Status
    pub fn get_status(&self) -> Option<String>{
        self.status.clone()
    }

    /// Set Status
    ///
    /// # Arguments
    /// `self`
    /// `status` The Status
    pub fn set_status(&mut self, status: Option<String>){
        self.status = status;
    }
}

// Pub Transaction Model Methods
impl PubTransactionModel{

    /// Create PubTransactionModel from TransactionModel
    ///
    /// # Arguments
    /// `account` - TransactionModel
    ///
    /// # Returns
    /// 'PubTransactionModel'
    pub fn new(mut transaction: TransactionModel) -> PubTransactionModel{
        PubTransactionModel{
            id: transaction.get_id(),
            money_map_id: transaction.get_id(),
            account_id: transaction.get_id(),
            datetime: match transaction.get_datetime(){
                Some(timestamp) => {
                    Some(timestamp.to_string())
                },
                None => None
            },
            payee: transaction.get_payee(),
            description: transaction.get_description(),
            category_id: transaction.get_category_id(),
            amount: transaction.get_amount(),
            transaction_type: transaction.get_transaction_type(),
            status: transaction.get_status()
        }
    }

    /// Get ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// 'Option<ObjectId>' - ID
    pub fn get_id(&self) -> Option<ObjectId>{
        self.id.clone()
    }

    /// Get Money Map ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Money Map ID
    pub fn get_money_map_id(&self) -> Option<ObjectId>{
        self.money_map_id.clone()
    }

    /// Set Money Map ID
    ///
    /// # Arguments
    /// `self`
    /// `mm_id` Money Map ID
    pub fn set_money_map_id(&mut self, mm_id: Option<ObjectId>){
        self.money_map_id = mm_id
    }

    /// Get Account ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Account ID
    pub fn get_account_id(&self) -> Option<ObjectId>{
        self.account_id.clone()
    }

    /// Set Account ID
    ///
    /// # Arguments
    /// `self`
    /// `acc_id` Account ID
    pub fn set_account_id(&mut self, acc_id: Option<ObjectId>){
        self.account_id = acc_id
    }

    /// Get Datetime
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Datetime
    pub fn get_datetime(&self) -> Option<String>{
        self.datetime.clone()
    }

    /// Get Payee
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Entity
    pub fn get_payee(&self) -> Option<String>{
        self.payee.clone()
    }

    /// Get Description
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - description
    pub fn get_description(&self) -> Option<String>{
        self.description.clone()
    }

    /// Get Category ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Category ID
    pub fn get_category_id(&self) -> Option<ObjectId>{
        self.category_id.clone()
    }

    /// Get Amount
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<f64>` - Amount
    pub fn get_amount(&self) -> Option<f64>{
        self.amount.clone()
    }

    /// Get Transaction Type
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Transaction Type
    pub fn get_transaction_type(&self) -> Option<String>{
        self.transaction_type.clone()
    }

    /// Get Status
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Status
    pub fn get_status(&self) -> Option<String>{
        self.status.clone()
    }

    /// Validate Transaction
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `ValidationResult` - validation result
    pub fn validate(&self) -> ValidationResult{

        //validate Transaction
        let mut validation_result = ValidationResult::new();
        if !Validators::not_empty_string(self.payee.clone()){
            validation_result.add_error("payee".to_string(), "Payee is required.".to_string());
        }
        if !Validators::not_empty(self.amount.clone()){
            validation_result.add_error("amount".to_string(), "Amount is required.".to_string());
        }
        // TODO: Validate amount, must be numeric
        if !Validators::not_empty_string(self.transaction_type.clone()){
            validation_result.add_error("transaction_type".to_string(), "Transaction Type is required.".to_string());
        }
        // TODO: Validate Transaction Type: "credit" | "debit"

        validation_result
    }//end validate
}
<commit_msg>Add transaction type validation<commit_after>// src/models/transaction_model.rs

/// Transaction Model

// Import Modules
// External
use ::bson::oid::ObjectId;
use ::chrono::{DateTime, Duration, Local, TimeZone};
use ::chrono::offset::utc::UTC;
// Utilities
use ::common::validation::validators as Validators;
use ::common::validation::validation_result::{ValidationResult};

/// Transaction
#[derive(Clone)]
pub struct TransactionModel {
    pub id: Option<ObjectId>,
    pub money_map_id: Option<ObjectId>,
    pub account_id: Option<ObjectId>,
    pub datetime: Option<DateTime<UTC>>,
    pub payee: Option<String>,
    pub description: Option<String>,
    pub category_id: Option<ObjectId>,
    pub amount: Option<f64>,
    pub transaction_type: Option<String>,
    pub status: Option<String>
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct PubTransactionModel {
    pub id: Option<ObjectId>,
    pub money_map_id: Option<ObjectId>,
    pub account_id: Option<ObjectId>,
    pub datetime: Option<String>,
    pub payee: Option<String>,
    pub description: Option<String>,
    pub category_id: Option<ObjectId>,
    pub amount: Option<f64>,
    pub transaction_type: Option<String>,
    pub status: Option<String>
}

// Transaction Model Methods
impl TransactionModel{

    /// Create TransactionModel from PubTransactionModel
    ///
    /// # Arguments
    /// `account` - PubTransactionModel
    ///
    /// # Returns
    /// `TransactionModel`
    pub fn new(transaction: &PubTransactionModel) -> TransactionModel{
        TransactionModel{
            id: transaction.get_id(),
            money_map_id: transaction.get_money_map_id(),
            account_id: transaction.get_account_id(),
            datetime: None,
            payee: transaction.get_payee(),
            description: transaction.get_description(),
            category_id: transaction.get_category_id(),
            amount: transaction.get_amount(),
            transaction_type: transaction.get_transaction_type(),
            status: transaction.get_status()
        }
    }

    /// Get ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - ID
    pub fn get_id(&self) -> Option<ObjectId>{
        self.id.clone()
    }

    /// Set ID
    ///
    /// # Arguments
    /// `self`
    pub fn set_id(&mut self, id: ObjectId) {
        self.id = Some(id);
    }

    /// Get Money Map ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Money Map ID
    pub fn get_money_map_id(&mut self) -> Option<ObjectId>{
        self.money_map_id.clone()
    }

    /// Get Account ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Account ID
    pub fn get_account_id(&self) -> Option<ObjectId>{
        self.account_id.clone()
    }

    /// Get Datetime
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<DateTime<UTC>>` - Datetime
    pub fn get_datetime(&self) -> Option<DateTime<UTC>>{
        self.datetime.clone()
    }

    /// Set Datetime
    ///
    /// # Arguments
    /// `self`
    /// `datetime`
    pub fn set_datetime(&mut self, datetime: Option<DateTime<UTC>>){
        self.datetime = datetime;
    }

    /// Get Payee
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Entity
    pub fn get_payee(&self) -> Option<String>{
        self.payee.clone()
    }

    /// Get Description
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - description
    pub fn get_description(&self) -> Option<String>{
        self.description.clone()
    }

    /// Get Category ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Category ID
    pub fn get_category_id(&self) -> Option<ObjectId>{
        self.category_id.clone()
    }

    /// Get Amount
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<f64>` - Amount
    pub fn get_amount(&self) -> Option<f64>{
        self.amount.clone()
    }

    /// Get Transaction Type
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Transaction Type
    pub fn get_transaction_type(&self) -> Option<String>{
        self.transaction_type.clone()
    }

    /// Get Status
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Status
    pub fn get_status(&self) -> Option<String>{
        self.status.clone()
    }

    /// Set Status
    ///
    /// # Arguments
    /// `self`
    /// `status` The Status
    pub fn set_status(&mut self, status: Option<String>){
        self.status = status;
    }
}

// Pub Transaction Model Methods
impl PubTransactionModel{

    /// Create PubTransactionModel from TransactionModel
    ///
    /// # Arguments
    /// `account` - TransactionModel
    ///
    /// # Returns
    /// 'PubTransactionModel'
    pub fn new(mut transaction: TransactionModel) -> PubTransactionModel{
        PubTransactionModel{
            id: transaction.get_id(),
            money_map_id: transaction.get_id(),
            account_id: transaction.get_id(),
            datetime: match transaction.get_datetime(){
                Some(timestamp) => {
                    Some(timestamp.to_string())
                },
                None => None
            },
            payee: transaction.get_payee(),
            description: transaction.get_description(),
            category_id: transaction.get_category_id(),
            amount: transaction.get_amount(),
            transaction_type: transaction.get_transaction_type(),
            status: transaction.get_status()
        }
    }

    /// Get ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// 'Option<ObjectId>' - ID
    pub fn get_id(&self) -> Option<ObjectId>{
        self.id.clone()
    }

    /// Get Money Map ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Money Map ID
    pub fn get_money_map_id(&self) -> Option<ObjectId>{
        self.money_map_id.clone()
    }

    /// Set Money Map ID
    ///
    /// # Arguments
    /// `self`
    /// `mm_id` Money Map ID
    pub fn set_money_map_id(&mut self, mm_id: Option<ObjectId>){
        self.money_map_id = mm_id
    }

    /// Get Account ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Account ID
    pub fn get_account_id(&self) -> Option<ObjectId>{
        self.account_id.clone()
    }

    /// Set Account ID
    ///
    /// # Arguments
    /// `self`
    /// `acc_id` Account ID
    pub fn set_account_id(&mut self, acc_id: Option<ObjectId>){
        self.account_id = acc_id
    }

    /// Get Datetime
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Datetime
    pub fn get_datetime(&self) -> Option<String>{
        self.datetime.clone()
    }

    /// Get Payee
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Entity
    pub fn get_payee(&self) -> Option<String>{
        self.payee.clone()
    }

    /// Get Description
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - description
    pub fn get_description(&self) -> Option<String>{
        self.description.clone()
    }

    /// Get Category ID
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<ObjectId>` - Category ID
    pub fn get_category_id(&self) -> Option<ObjectId>{
        self.category_id.clone()
    }

    /// Get Amount
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<f64>` - Amount
    pub fn get_amount(&self) -> Option<f64>{
        self.amount.clone()
    }

    /// Get Transaction Type
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Transaction Type
    pub fn get_transaction_type(&self) -> Option<String>{
        self.transaction_type.clone()
    }

    /// Get Status
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `Option<String>` - Status
    pub fn get_status(&self) -> Option<String>{
        self.status.clone()
    }

    /// Validate Transaction
    ///
    /// # Arguments
    /// `self`
    ///
    /// # Returns
    /// `ValidationResult` - validation result
    pub fn validate(&self) -> ValidationResult{
        // Valid Options
        let transaction_types = vec!["credit".to_string(), "debit".to_string()];

        //validate Transaction
        let mut validation_result = ValidationResult::new();
        if !Validators::not_empty_string(self.payee.clone()){
            validation_result.add_error("payee".to_string(), "Payee is required.".to_string());
        }
        if !Validators::not_empty(self.amount.clone()){
            validation_result.add_error("amount".to_string(), "Amount is required.".to_string());
        }
        // TODO: Validate amount, must be numeric
        if !Validators::not_empty_string(self.transaction_type.clone()){
            validation_result.add_error("transaction_type".to_string(), "Transaction Type is required.".to_string());
        }else{
            // Validate transaction_type
            if !transaction_types.contains(&self.transaction_type.clone().unwrap()){
                validation_result.add_error("transaction_type".to_string(), "Transaction Type is not valid.".to_string());
            }
        }

        validation_result
    }//end validate
}
<|endoftext|>


<commit_before>//***********************************************************************
//  Copyright 2006 John A. Trangenstein
//
//  This software is made available for research and instructional use
//  only.
//  You may copy and use this software without charge for these
//  non-commercial purposes, provided that the copyright notice and
//  associated text is reproduced on all copies.
//  For all other uses (including distribution of modified versions),
//  please contact the author at
//    John A. Trangenstein
//    Department of Mathematics
//    Duke University
//    Durham, NC 27708-0320
//    USA
//  or
//    johnt@math.duke.edu
//
//  This software is made available "as is" without any assurance that it
//  is completely correct, or that it will work for your purposes.
//  Use the software at your own risk.
//***********************************************************************

// Permission to publish the translation of the code
// was graciously granted by Prof. John A. Trangenstein

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

#![feature(duration_span)]

extern crate argparse;

use std::time::Duration;
use std::cmp;
use std::process::exit;
use argparse::{ArgumentParser, StoreTrue, Store};

struct Options {
    verbose: bool,
    num_steps: usize,
    num_cells: usize,
    num_runs: usize,
}

fn do_computation(nsteps: usize, ncells: usize, tmax: f64, ifirst: usize, ilast: usize,
                  statelft: f64, statergt: f64, velocity: f64, dt: f64,
                  fc: isize, lc: usize, flux: &mut Vec<f64>, x: &Vec<f64>, u: &mut Vec<f64>)
{
}

fn main() {
    let mut options = Options {
        verbose:   false,
        num_steps: 2000,
        num_cells: 2000,
        num_runs:  10,
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Time elapsing of model PDE for scalar conservation law.");
        ap.refer(&mut options.verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
            "set verbose");
        ap.refer(&mut options.num_steps)
            .add_option(&["-t", "--num_steps"], Store,
            "set number of time steps");
        ap.refer(&mut options.num_cells)
            .add_option(&["-n", "--num_cells"], Store,
            "set number of grid cells");
        ap.refer(&mut options.num_runs)
            .add_option(&["-b", "--num_runs"], Store,
            "set number of runs");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }

    if options.verbose {
        println!("number of time steps : {} number of grid cells : {} number of runs : {}",
                 options.num_steps, options.num_cells, options.num_runs);
    }

    // problem-specific parameters:
    const jump     : f64   =  0.0f64;
    const x_left   : f64   = -0.2f64;
    const x_right  : f64   =  1.0f64;
    const statelft : f64   =  2.0f64;
    const statergt : f64   =  0.0f64;
    const velocity : f64   =  1.0f64;

    const tmax     : f64   =  0.8f64;
    const cfl      : f64   =  0.9f64;

    // array bounds:
    let   nsteps           = options.num_steps;
    let   ncells           = options.num_cells;
    const fc       : isize = -2;
    let   lc               = ncells+1;
    const ifirst   : usize = 0;
    let   ilast            = ncells-1;

    // work arrays
    // #   double precision
    // #  &  u(-2:ncells+1),
    // #  &  x(0:ncells),
    // #  &  flux(0:ncells)

    let mut u    = vec![0.0f64; ncells+4];
    let mut x    = vec![0.0f64; ncells+1];
    let mut flux = vec![0.0f64; ncells+1];

    //  uniform mesh:
    let dx = (x_right-x_left) / ncells as f64;
    for ie in ifirst .. ilast+1 {
        x[ie] = x_left + ie as f64 * dx;
    }

    // initial values for diffential equation:
    let ijump = cmp::max(ifirst as isize -1,
                         cmp::min((ncells as f64 * (jump-x_left)/(x_right-x_left) + 0.5) as isize,
                                  (ilast+1) as isize)) as usize;
    if options.verbose {
        println!("ijump : {}", ijump);
    }

    // left state to left of jump
    for ic in ifirst .. ijump-1 {
        u[ic+3] = statelft;
    }

    // volume-weighted average in cell containing jump
    let frac = (jump-x_left-ijump as f64 *dx)/(x_right-x_left);
    u[ijump+3] = statelft*frac+statergt*(1.0f64-frac);

    // right state to right of jump
    for ic in ijump+1 .. ilast {
        u[ic+3]=statergt;
    }

    // stable timestep (independent of time for linear advection):
    let mut mindx = 1.0e300f64;
    for ic in ifirst .. ilast {
        mindx = mindx.min(x[ic+2]-x[ic+1]);
    }
    let dt = cfl*mindx/velocity.abs();

    let istep = 0;
    let t = 0.0f64;

    let d = Duration::span(||{
        for r in 0..options.num_runs {
            if options.verbose {
                println!("run number : {}", r);
            }

            do_computation(nsteps, ncells, tmax, ifirst, ilast,
                           statelft, statergt, velocity, dt, fc, lc, &mut flux, &x, &mut u);
        }
    });
    println!("elapsed time: {}", d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);
}
<commit_msg>implementation for `scl` finished. FIX array bounds, coonstants to UC.<commit_after>//***********************************************************************
//  Copyright 2006 John A. Trangenstein
//
//  This software is made available for research and instructional use
//  only.e available for research and instructional use
//  only.
//  You may copy and use this software without charge for these
//  non-commercial purposes, provided that the copyright notice and
//  associated text is reproduced on all copies.
//  For all other uses (including distribution of modified versions),
//  please contact the author at
//    John A. Trangenstein
//    Department of Mathematics
//    Duke University
//    Durham, NC 27708-0320
//    USA
//  or
//    johnt@math.duke.edu
//
//  This software is made available "as is" without any assurance that it
//  is completely correct, or that it will work for your purposes.
//  Use the software at your own risk.
//***********************************************************************

// Permission to publish the translation of the code
// was graciously granted by Prof. John A. Trangenstein

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

#![feature(duration_span)]

extern crate argparse;

use std::time::Duration;
use std::cmp;
use std::process::exit;
use argparse::{ArgumentParser, StoreTrue, Store};

struct Options {
    verbose: bool,
    num_steps: usize,
    num_cells: usize,
    num_runs: usize,
}

fn do_computation(nsteps: usize, ncells: usize, tmax: f64, ifirst: usize, ilast: usize,
                  statelft: f64, statergt: f64, velocity: f64, dt: f64,
                  fc: usize, lc: usize, x: &Vec<f64>, u: &mut Vec<f64>)
{
    let mut istep   =   0;
    let mut t       =   0.0f64;
    let mut flux    =   vec![0.0f64; x.len()];

    // loop over timesteps
    while istep < nsteps && t < tmax {

        // right boundary condition: outgoing wave
        for ic in ncells .. lc {
            u[ic]=u[ncells-1];
        }
        // left boundary condition: specified value
        for ic in 0 .. fc {
            u[ic]=statelft;
        }

        // upwind fluxes times dt (ie, flux time integral over cell side)
        // assumes velocity > 0
        let vdt=velocity*dt;
        for ie in ifirst .. ilast+1 {
            flux[ie]=vdt*u[ie-1];
        }

        // conservative difference
        for ic in ifirst .. ilast {
            u[ic] -= (flux[ic+1]-flux[ic]) / (x[ic+1]-x[ic])
        }

        // update time and step number
        t       +=  dt;
        istep   +=  1
    }
}

fn main() {
    let mut options = Options {
        verbose:   false,
        num_steps: 2000,
        num_cells: 2000,
        num_runs:  10,
    };

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Time elapsing of model PDE for scalar conservation law.");
        ap.refer(&mut options.verbose)
            .add_option(&["-v", "--verbose"], StoreTrue,
            "set verbose");
        ap.refer(&mut options.num_steps)
            .add_option(&["-t", "--num_steps"], Store,
            "set number of time steps");
        ap.refer(&mut options.num_cells)
            .add_option(&["-n", "--num_cells"], Store,
            "set number of grid cells");
        ap.refer(&mut options.num_runs)
            .add_option(&["-b", "--num_runs"], Store,
            "set number of runs");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }

    if options.verbose {
        println!("number of time steps : {} number of grid cells : {} number of runs : {}",
                 options.num_steps, options.num_cells, options.num_runs);
    }

    // problem-specific parameters:
    const jump     : f64   =  0.0f64;
    const x_left   : f64   = -0.2f64;
    const x_right  : f64   =  1.0f64;
    const statelft : f64   =  2.0f64;
    const statergt : f64   =  0.0f64;
    const velocity : f64   =  1.0f64;

    const tmax     : f64   =  0.8f64;
    const cfl      : f64   =  0.9f64;

    // array bounds:
    let   nsteps           = options.num_steps;
    let   ncells           = options.num_cells;
    const fc       : usize = 2;
    let   lc               = ncells+1;
    const ifirst   : usize = 0;
    let   ilast            = ncells-1;

    // work arrays
    // #   double precision
    // #  &  u(-2:ncells+1),
    // #  &  x(0:ncells),
    // #  &  flux(0:ncells)

    let mut u    = vec![0.0f64; ncells+4];
    let mut x    = vec![0.0f64; ncells+1];

    //  uniform mesh:
    let dx = (x_right-x_left) / ncells as f64;
    for ie in ifirst .. ilast+1 {
        x[ie] = x_left + ie as f64 * dx;
    }

    // initial values for diffential equation:
    let ijump = cmp::max(ifirst as isize -1,
                         cmp::min((ncells as f64 * (jump-x_left)/(x_right-x_left) + 0.5) as isize,
                                  (ilast+1) as isize)) as usize;
    if options.verbose {
        println!("ijump : {}", ijump);
    }

    // stable timestep (independent of time for linear advection):
    let mut mindx = 1.0e300f64;
    for ic in ifirst .. ilast {
        mindx = mindx.min(x[ic+2]-x[ic+1]);
    }
    let dt = cfl*mindx/velocity.abs();

    let d = Duration::span(||{
        for r in 0..options.num_runs {
            if options.verbose {
                println!("run number : {}", r);
            }

            // left state to left of jump
            for ic in ifirst .. ijump-1 {
                u[ic+3] = statelft;
            }

            // volume-weighted average in cell containing jump
            let frac = (jump-x_left-ijump as f64 *dx)/(x_right-x_left);
            u[ijump+3] = statelft*frac+statergt*(1.0f64-frac);

            // right state to right of jump
            for ic in ijump+1 .. ilast {
                u[ic+3]=statergt;
            }

            do_computation(nsteps, ncells, tmax, ifirst, ilast,
                           statelft, statergt, velocity, dt, fc, lc, &x, &mut u);
        }
    });
    println!("elapsed time: {}", d.as_secs() as f64 + d.subsec_nanos() as f64 / 1.0e9f64);
}
<|endoftext|>


<commit_before>extern mod std;

/* For future reference, if necessary (from pcwalton, #rust, 2012-10-17 20:38)
   basically to allocate something in the same lifetime as yourself, you need to use an arena
   take in the arena, and then allocate into it. for example:
   impl Foo { fn make_foo(&self, arena_self_is_in: &self/Arena) -> &self/Foo { ... } }
   you need to pass in the arena that self is in
   so that the function can allocate into it
*/

struct Tree/&{
    mut leftTree: Option<~Tree>,
    mut rightTree: Option<~Tree>,
    key: int
}

impl Tree {
    fn insert(&self, i: int) -> () {
        let mut node = None;
        if i > self.key {
            node <-> self.rightTree;
            match node {
                Some(ref t) => t.insert(i),
                None => node = Some(~Tree {leftTree: None, rightTree: None, key: i})
            }
            node <-> self.rightTree;
        }
        if i < self.key {
            node <-> self.leftTree;
            match node {
                Some (ref t) => t.insert(i),
                None => node = Some(~Tree {leftTree: None, rightTree: None, key: i})
            }
            node <-> self.leftTree;
        }

    }

    pure fn treeDepth (&self) -> int {
        let leftCount = match self.leftTree {
            Some (ref t) => t.treeDepth(),
            None => 0
        };
        let rightCount = match self.rightTree {
            Some (ref t) => t.treeDepth(),
            None => 0
        };
        1 + int::max(leftCount, rightCount)
    }

    pure fn contains(i: int) -> bool {
        if i == self.key {
            return true;
        }

        if i > self.key {
            match self.rightTree {
                Some(~t) => t.contains(i),
                None => false
            }
        }
        else {
            match self.leftTree {
                Some(~t) => t.contains(i),
                None => false
            }
        }
    }

    fn print() {
        let mut node = None;
        node <-> self.leftTree;
        match node {
            Some(ref t) => t.print(),
            None => ()
        };
        node <-> self.leftTree;

        io::print(fmt!("%d ", self.key));

        node <-> self.rightTree;
        match node {
            Some(ref t) => t.print(),
            None => ()
        };
        node <-> self.rightTree;
    }

}

fn fillTree (tree: &Tree, numNodes: int) {
    let r = rand::Rng();
    for int::range(0, numNodes) |_index| {
        tree.insert(r.gen_int_range(0, numNodes));
    }
}

fn main () {
    let root: Tree = Tree {leftTree: None, rightTree: None, key: 5};

    root.insert(8);
    fillTree(&root, 1000);

    root.print();
    io::println("");

    if root.contains(4) {
        io::println("Contains 4")
    }
    else {
        io::println("Doesn't contain 4")
    }

    root.insert(4);

    if root.contains(4) {
        io::println("Contains 4 now ...")
    }
    else {
        io::println("Doesn't contain 4 yet")
    }

    io::println(fmt!("Tree depth: %d", root.treeDepth()));

}
<commit_msg>Cleanup binary-tree a little bit<commit_after>extern mod std;

/* For future reference, if necessary (from pcwalton, #rust, 2012-10-17 20:38)
   basically to allocate something in the same lifetime as yourself, you need to use an arena
   take in the arena, and then allocate into it. for example:
   impl Foo { fn make_foo(&self, arena_self_is_in: &self/Arena) -> &self/Foo { ... } }
   you need to pass in the arena that self is in
   so that the function can allocate into it
*/

struct Tree {
    mut leftTree: Option<~Tree>,
    mut rightTree: Option<~Tree>,
    key: int
}

impl Tree {
    fn insert(&self, i: int) {
        let mut node = None;
        if i > self.key {
            node <-> self.rightTree;
            match node {
                Some(ref t) => t.insert(i),
                None => node = Some(~Tree {leftTree: None, rightTree: None, key: i})
            }
            node <-> self.rightTree;
        }
        if i < self.key {
            node <-> self.leftTree;
            match node {
                Some (ref t) => t.insert(i),
                None => node = Some(~Tree {leftTree: None, rightTree: None, key: i})
            }
            node <-> self.leftTree;
        }

    }

    pure fn treeDepth (&self) -> int {
        let leftCount = match self.leftTree {
            Some (ref t) => t.treeDepth(),
            None => 0
        };
        let rightCount = match self.rightTree {
            Some (ref t) => t.treeDepth(),
            None => 0
        };
        1 + int::max(leftCount, rightCount)
    }

    pure fn contains(i: int) -> bool {
        if i == self.key {
            return true;
        }

        if i > self.key {
            match self.rightTree {
                Some(~t) => t.contains(i),
                None => false
            }
        }
        else {
            match self.leftTree {
                Some(~t) => t.contains(i),
                None => false
            }
        }
    }

    fn print() {
        let mut node = None;
        node <-> self.leftTree;
        match node {
            Some(ref t) => t.print(),
            None => ()
        };
        node <-> self.leftTree;

        io::print(fmt!("%d ", self.key));

        node <-> self.rightTree;
        match node {
            Some(ref t) => t.print(),
            None => ()
        };
        node <-> self.rightTree;
    }

}

fn fillTree (tree: &Tree, numNodes: int) {
    let r = rand::Rng();
    for int::range(0, numNodes) |_index| {
        tree.insert(r.gen_int_range(0, numNodes));
    }
}

fn main () {
    let root: Tree = Tree {leftTree: None, rightTree: None, key: 5};

    root.insert(8);
    fillTree(&root, 1000);

    root.print();
    io::println("");

    if root.contains(4) {
        io::println("Contains 4")
    }
    else {
        io::println("Doesn't contain 4")
    }

    root.insert(4);

    if root.contains(4) {
        io::println("Contains 4 now ...")
    }
    else {
        io::println("Doesn't contain 4 yet")
    }

    io::println(fmt!("Tree depth: %d", root.treeDepth()));

}
<|endoftext|>


<commit_before>use super::*;
use crate::shapes::{GeoClass, GeoKind, Shape};
use crate::StrError;
use russell_lab::Vector;

/// Defines options to select integration points
#[derive(Eq, PartialEq)]
pub enum OptionIntPoint {
    /// Option for Tri (points on edge)
    Edge,

    /// Option for Qua and Hex (Wilson formula)
    WilsonStable,
}

// Defines an alias for integration points data
pub type IpData = &'static [[f64; 4]];

pub struct Integrator {
    /// Number of integration points
    pub nip: usize,

    /// Integration points and weights
    pub points: IpData,
    // pub int_points: Vec<Vector>,
}

impl Integrator {
    /// Creates new Integrator
    pub fn new(kind: GeoKind) -> Self {
        match kind {
            // Lin
            GeoKind::Lin2 => Integrator {
                nip: 2,
                points: &IP_LIN_LEGENDRE_2,
            },
            GeoKind::Lin3 => Integrator {
                nip: 3,
                points: &IP_LIN_LEGENDRE_3,
            },
            GeoKind::Lin4 => Integrator {
                nip: 4,
                points: &IP_LIN_LEGENDRE_4,
            },
            GeoKind::Lin5 => Integrator {
                nip: 5,
                points: &IP_LIN_LEGENDRE_5,
            },

            // Tri
            GeoKind::Tri3 => Integrator {
                nip: 1,
                points: &IP_TRI_INTERNAL_1,
            },
            GeoKind::Tri6 => Integrator {
                nip: 3,
                points: &IP_TRI_INTERNAL_3,
            },
            GeoKind::Tri10 => Integrator {
                nip: 12,
                points: &IP_TRI_INTERNAL_12,
            },
            GeoKind::Tri15 => Integrator {
                nip: 12,
                points: &IP_TRI_INTERNAL_12,
            },

            // Qua
            GeoKind::Qua4 => Integrator {
                nip: 4,
                points: &IP_QUA_LEGENDRE_4,
            },
            GeoKind::Qua8 => Integrator {
                nip: 9,
                points: &IP_QUA_LEGENDRE_9,
            },
            GeoKind::Qua9 => Integrator {
                nip: 9,
                points: &IP_QUA_LEGENDRE_9,
            },
            GeoKind::Qua12 => Integrator {
                nip: 9,
                points: &IP_QUA_LEGENDRE_9,
            },
            GeoKind::Qua16 => Integrator {
                nip: 16,
                points: &IP_QUA_LEGENDRE_16,
            },
            GeoKind::Qua17 => Integrator {
                nip: 16,
                points: &IP_QUA_LEGENDRE_16,
            },

            // Tet
            GeoKind::Tet4 => Integrator {
                nip: 1,
                points: &IP_TET_INTERNAL_1,
            },
            GeoKind::Tet10 => Integrator {
                nip: 4,
                points: &IP_TET_INTERNAL_4,
            },

            // Hex
            GeoKind::Hex8 => Integrator {
                nip: 8,
                points: &IP_HEX_LEGENDRE_8,
            },
            GeoKind::Hex20 => Integrator {
                nip: 27,
                points: &IP_HEX_LEGENDRE_27,
            },
        }
    }

    /// Selects integrations points and weights
    ///
    /// # Options
    ///
    /// ## nip for Lin class
    ///
    /// * `1` -- Legendre points
    /// * `2` -- Legendre points
    /// * `3` -- Legendre points
    /// * `4` -- Legendre points
    /// * `5` -- Legendre points
    ///
    /// ## nip for Tri class
    ///
    /// * `1` -- Internal points
    /// * `3`:
    ///     - `Edge` -- Points on edge
    ///     - Otherwise, internal points
    /// * `4` -- Internal points
    /// * `12` -- Internal points
    /// * `16` -- Internal points
    ///
    /// ## nip for Qua class
    ///
    /// * `1` -- Legendre points
    /// * `4` -- Legendre points
    /// * `5`:
    ///     - `WilsonStable` -- Wilson's "Stable" version with w0=0.004 and wa=0.999 to mimic 4-point rule
    ///     - Otherwise, Wilson's formula
    /// * `9` -- Legendre points
    /// * `16` -- Legendre points
    ///
    /// ## nip for Tet class
    ///
    /// * `1` -- Internal points
    /// * `4` -- Internal points
    /// * `5` -- Internal points
    /// * `6` -- Internal points
    ///
    /// ## nip for Hex class
    ///
    /// * `6` -- Iron's formula
    /// * `8` -- Legendre points
    /// * `9`:
    ///     - `WilsonStable` -- Wilson's "Stable" version
    ///     - Otherwise, Wilson's formula
    /// * `14` -- Iron's formula
    /// * `27` -- Legendre points
    pub fn select_int_points(&mut self, class: GeoClass, nip: usize, option: OptionIntPoint) -> Result<(), StrError> {
        match class {
            // Lin
            GeoClass::Lin => match nip {
                1 => self.points = &IP_LIN_LEGENDRE_1,
                2 => self.points = &IP_LIN_LEGENDRE_2,
                3 => self.points = &IP_LIN_LEGENDRE_3,
                4 => self.points = &IP_LIN_LEGENDRE_4,
                5 => self.points = &IP_LIN_LEGENDRE_5,
                _ => return Err("number of integration points is not available for Lin class"),
            },

            // Tri
            GeoClass::Tri => match nip {
                1 => self.points = &IP_TRI_INTERNAL_1,
                3 => {
                    if option == OptionIntPoint::Edge {
                        self.points = &IP_TRI_EDGE_3
                    } else {
                        self.points = &IP_TRI_INTERNAL_3
                    }
                }
                4 => self.points = &IP_TRI_INTERNAL_4,
                12 => self.points = &IP_TRI_INTERNAL_12,
                16 => self.points = &IP_TRI_INTERNAL_16,
                _ => return Err("number of integration points is not available for Tri class"),
            },

            // Qua
            GeoClass::Qua => match nip {
                1 => self.points = &IP_QUA_LEGENDRE_1,
                4 => self.points = &IP_QUA_LEGENDRE_4,
                5 => {
                    if option == OptionIntPoint::WilsonStable {
                        self.points = &IP_QUA_WILSON_STABLE_5
                    } else {
                        self.points = &IP_QUA_WILSON_CORNER_5
                    }
                }
                8 => self.points = &IP_QUA_WILSON_8,
                9 => self.points = &IP_QUA_LEGENDRE_9,
                16 => self.points = &IP_QUA_LEGENDRE_16,
                _ => return Err("number of integration points is not available for Qua class"),
            },

            // Tet
            GeoClass::Tet => match nip {
                1 => self.points = &IP_TET_INTERNAL_1,
                4 => self.points = &IP_TET_INTERNAL_4,
                5 => self.points = &IP_TET_INTERNAL_5,
                6 => self.points = &IP_TET_INTERNAL_6,
                _ => return Err("number of integration points is not available for Tet class"),
            },

            // Hex
            GeoClass::Hex => match nip {
                6 => self.points = &IP_HEX_IRONS_6,
                8 => self.points = &IP_HEX_LEGENDRE_8,
                9 => {
                    if option == OptionIntPoint::WilsonStable {
                        self.points = &IP_HEX_WILSON_STABLE_9
                    } else {
                        self.points = &IP_HEX_WILSON_CORNER_9
                    }
                }
                14 => self.points = &IP_HEX_IRONS_14,
                27 => self.points = &IP_HEX_LEGENDRE_27,
                _ => return Err("number of integration points is not available for Hex class"),
            },
        }
        self.nip = nip;
        Ok(())
    }

    pub fn case_a(&self, shape: &mut Shape) -> Result<(), StrError> {
        let res = 0.0;
        for point in self.points {
            shape.calc_jacobian(point)?;
            println!("{:?}", point);
        }
        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn new_works() -> Result<(), StrError> {
        let shape = Shape::new(2, 2, 4)?;
        let integ = Integrator::new(shape.kind);
        assert_eq!(integ.nip, 4);
        Ok(())
    }

    #[test]
    fn case_a_works() -> Result<(), StrError> {
        let mut shape = Shape::new(2, 2, 4)?;
        shape.set_point(0, 0, 0.0)?;
        shape.set_point(0, 1, 0.0)?;
        shape.set_point(1, 0, 1.0)?;
        shape.set_point(1, 1, 0.0)?;
        shape.set_point(2, 0, 1.0)?;
        shape.set_point(2, 1, 1.0)?;
        shape.set_point(3, 0, 0.0)?;
        shape.set_point(3, 1, 1.0)?;

        let integ = Integrator::new(shape.kind);
        assert_eq!(integ.nip, 4);
        Ok(())
    }
}
<commit_msg>[wip] Draft case_a integration<commit_after>use super::*;
use crate::shapes::{GeoClass, GeoKind, Shape};
use crate::StrError;

/// Defines options to select integration points
#[derive(Eq, PartialEq)]
pub enum OptionIntPoint {
    /// Option for Tri (points on edge)
    Edge,

    /// Option for Qua and Hex (Wilson formula)
    WilsonStable,
}

// Defines an alias for integration points data
pub type IpData = &'static [[f64; 4]];

pub struct Integrator {
    /// Number of integration points
    pub nip: usize,

    /// Integration points and weights
    pub points: IpData,
    // pub int_points: Vec<Vector>,
}

/// Defines scalar function (index,ksi)
pub type FnScalar = fn(usize, &[f64]) -> f64;

impl Integrator {
    /// Creates new Integrator
    pub fn new(kind: GeoKind) -> Self {
        match kind {
            // Lin
            GeoKind::Lin2 => Integrator {
                nip: 2,
                points: &IP_LIN_LEGENDRE_2,
            },
            GeoKind::Lin3 => Integrator {
                nip: 3,
                points: &IP_LIN_LEGENDRE_3,
            },
            GeoKind::Lin4 => Integrator {
                nip: 4,
                points: &IP_LIN_LEGENDRE_4,
            },
            GeoKind::Lin5 => Integrator {
                nip: 5,
                points: &IP_LIN_LEGENDRE_5,
            },

            // Tri
            GeoKind::Tri3 => Integrator {
                nip: 1,
                points: &IP_TRI_INTERNAL_1,
            },
            GeoKind::Tri6 => Integrator {
                nip: 3,
                points: &IP_TRI_INTERNAL_3,
            },
            GeoKind::Tri10 => Integrator {
                nip: 12,
                points: &IP_TRI_INTERNAL_12,
            },
            GeoKind::Tri15 => Integrator {
                nip: 12,
                points: &IP_TRI_INTERNAL_12,
            },

            // Qua
            GeoKind::Qua4 => Integrator {
                nip: 4,
                points: &IP_QUA_LEGENDRE_4,
            },
            GeoKind::Qua8 => Integrator {
                nip: 9,
                points: &IP_QUA_LEGENDRE_9,
            },
            GeoKind::Qua9 => Integrator {
                nip: 9,
                points: &IP_QUA_LEGENDRE_9,
            },
            GeoKind::Qua12 => Integrator {
                nip: 9,
                points: &IP_QUA_LEGENDRE_9,
            },
            GeoKind::Qua16 => Integrator {
                nip: 16,
                points: &IP_QUA_LEGENDRE_16,
            },
            GeoKind::Qua17 => Integrator {
                nip: 16,
                points: &IP_QUA_LEGENDRE_16,
            },

            // Tet
            GeoKind::Tet4 => Integrator {
                nip: 1,
                points: &IP_TET_INTERNAL_1,
            },
            GeoKind::Tet10 => Integrator {
                nip: 4,
                points: &IP_TET_INTERNAL_4,
            },

            // Hex
            GeoKind::Hex8 => Integrator {
                nip: 8,
                points: &IP_HEX_LEGENDRE_8,
            },
            GeoKind::Hex20 => Integrator {
                nip: 27,
                points: &IP_HEX_LEGENDRE_27,
            },
        }
    }

    /// Selects integrations points and weights
    ///
    /// # Options
    ///
    /// ## nip for Lin class
    ///
    /// * `1` -- Legendre points
    /// * `2` -- Legendre points
    /// * `3` -- Legendre points
    /// * `4` -- Legendre points
    /// * `5` -- Legendre points
    ///
    /// ## nip for Tri class
    ///
    /// * `1` -- Internal points
    /// * `3`:
    ///     - `Edge` -- Points on edge
    ///     - Otherwise, internal points
    /// * `4` -- Internal points
    /// * `12` -- Internal points
    /// * `16` -- Internal points
    ///
    /// ## nip for Qua class
    ///
    /// * `1` -- Legendre points
    /// * `4` -- Legendre points
    /// * `5`:
    ///     - `WilsonStable` -- Wilson's "Stable" version with w0=0.004 and wa=0.999 to mimic 4-point rule
    ///     - Otherwise, Wilson's formula
    /// * `9` -- Legendre points
    /// * `16` -- Legendre points
    ///
    /// ## nip for Tet class
    ///
    /// * `1` -- Internal points
    /// * `4` -- Internal points
    /// * `5` -- Internal points
    /// * `6` -- Internal points
    ///
    /// ## nip for Hex class
    ///
    /// * `6` -- Iron's formula
    /// * `8` -- Legendre points
    /// * `9`:
    ///     - `WilsonStable` -- Wilson's "Stable" version
    ///     - Otherwise, Wilson's formula
    /// * `14` -- Iron's formula
    /// * `27` -- Legendre points
    pub fn select_int_points(&mut self, class: GeoClass, nip: usize, option: OptionIntPoint) -> Result<(), StrError> {
        match class {
            // Lin
            GeoClass::Lin => match nip {
                1 => self.points = &IP_LIN_LEGENDRE_1,
                2 => self.points = &IP_LIN_LEGENDRE_2,
                3 => self.points = &IP_LIN_LEGENDRE_3,
                4 => self.points = &IP_LIN_LEGENDRE_4,
                5 => self.points = &IP_LIN_LEGENDRE_5,
                _ => return Err("number of integration points is not available for Lin class"),
            },

            // Tri
            GeoClass::Tri => match nip {
                1 => self.points = &IP_TRI_INTERNAL_1,
                3 => {
                    if option == OptionIntPoint::Edge {
                        self.points = &IP_TRI_EDGE_3
                    } else {
                        self.points = &IP_TRI_INTERNAL_3
                    }
                }
                4 => self.points = &IP_TRI_INTERNAL_4,
                12 => self.points = &IP_TRI_INTERNAL_12,
                16 => self.points = &IP_TRI_INTERNAL_16,
                _ => return Err("number of integration points is not available for Tri class"),
            },

            // Qua
            GeoClass::Qua => match nip {
                1 => self.points = &IP_QUA_LEGENDRE_1,
                4 => self.points = &IP_QUA_LEGENDRE_4,
                5 => {
                    if option == OptionIntPoint::WilsonStable {
                        self.points = &IP_QUA_WILSON_STABLE_5
                    } else {
                        self.points = &IP_QUA_WILSON_CORNER_5
                    }
                }
                8 => self.points = &IP_QUA_WILSON_8,
                9 => self.points = &IP_QUA_LEGENDRE_9,
                16 => self.points = &IP_QUA_LEGENDRE_16,
                _ => return Err("number of integration points is not available for Qua class"),
            },

            // Tet
            GeoClass::Tet => match nip {
                1 => self.points = &IP_TET_INTERNAL_1,
                4 => self.points = &IP_TET_INTERNAL_4,
                5 => self.points = &IP_TET_INTERNAL_5,
                6 => self.points = &IP_TET_INTERNAL_6,
                _ => return Err("number of integration points is not available for Tet class"),
            },

            // Hex
            GeoClass::Hex => match nip {
                6 => self.points = &IP_HEX_IRONS_6,
                8 => self.points = &IP_HEX_LEGENDRE_8,
                9 => {
                    if option == OptionIntPoint::WilsonStable {
                        self.points = &IP_HEX_WILSON_STABLE_9
                    } else {
                        self.points = &IP_HEX_WILSON_CORNER_9
                    }
                }
                14 => self.points = &IP_HEX_IRONS_14,
                27 => self.points = &IP_HEX_LEGENDRE_27,
                _ => return Err("number of integration points is not available for Hex class"),
            },
        }
        self.nip = nip;
        Ok(())
    }

    pub fn case_a(&self, a: &mut [f64], shape: &mut Shape, s: FnScalar) -> Result<(), StrError> {
        a.fill(0.0);
        for (index, iota) in self.points.iter().enumerate() {
            let ksi = &iota[0..shape.geo_ndim];
            let weight = iota[3];
            shape.calc_interp(ksi);
            let det_jac = shape.calc_jacobian(ksi)?;
            for m in 0..shape.npoint {
                a[m] += shape.interp[m] * s(index, ksi) * det_jac * weight;
            }
        }
        Ok(())
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

    const NOISE: f64 = 1234.56;

    #[test]
    fn new_works() -> Result<(), StrError> {
        let shape = Shape::new(2, 2, 4)?;
        let integ = Integrator::new(shape.kind);
        assert_eq!(integ.nip, 4);
        Ok(())
    }

    #[test]
    fn case_a_works() -> Result<(), StrError> {
        let mut shape = Shape::new(2, 2, 4)?;
        shape.set_point(0, 0, 0.0)?;
        shape.set_point(0, 1, 0.0)?;
        shape.set_point(1, 0, 1.0)?;
        shape.set_point(1, 1, 0.0)?;
        shape.set_point(2, 0, 1.0)?;
        shape.set_point(2, 1, 1.0)?;
        shape.set_point(3, 0, 0.0)?;
        shape.set_point(3, 1, 1.0)?;

        let integ = Integrator::new(shape.kind);
        let mut a = vec![NOISE; shape.npoint];
        integ.case_a(&mut a, &mut shape, |_, _| 0.0)?;
        println!("a = {:?}", a);

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