<commit_before>extern crate rust_vision;
use rust_vision::*;

struct SelectionStatus {
    selection: Rect,
    status: bool,
}

fn on_mouse(e: i32, x: i32, y: i32, _: i32, data: *mut CVoid) {
    let event: MouseEventTypes = unsafe { std::mem::transmute(e as u8) };
    match event {
        MouseEventTypes::LButtonDown => {
            let ss = data as *mut SelectionStatus;
            let mut selection = unsafe { &mut (*ss).selection };
            selection.x = x;
            selection.y = y;
        }
        MouseEventTypes::LButtonUp => {
            let ss = data as *mut SelectionStatus;
            let mut selection = unsafe { &mut (*ss).selection };
            let mut status = unsafe { &mut (*ss).status };
            selection.width = x - selection.x;
            selection.height = y - selection.y;

            if selection.width > 0 && selection.height > 0 {
                *status = true;
            }
        }
        _ => {}
    }
}

fn main() {
    let mut selection_status = SelectionStatus {
        selection: Rect::default(),
        status: false,
    };
    let ss_ptr = &mut selection_status as *mut SelectionStatus;

    let cap = VideoCapture::new(0);
    assert!(cap.is_open());

    highgui_named_window("Window", WindowFlags::WindowAutosize);
    highgui_set_mouse_callback("Window", on_mouse, ss_ptr as *mut CVoid);

    let m = Mat::new();
    let mut is_tracking = false;

    let mut hist = Mat::new();
    let hsize = 16;
    let hranges = [0 as f32, 180 as f32];
    let phranges: [*const f32; 1] = [&hranges[0] as *const f32];
    let mut track_window = Rect::default();

    loop {
        cap.read(&m);
        let hsv = m.cvt_color(ColorConversionCodes::BGR2HSV);

        let ch = [0, 0];
        let hue = hsv.mix_channels(1, 1, &ch[0] as *const i32, 1);
        let mask =
            hsv.in_range(Scalar::new(0, 30, 10, 0),
                         Scalar::new(180, 256, 256, 0));

        if selection_status.status {
            println!("Initialize tracking, setting up CAMShift search");
            let selection = selection_status.selection;
            let roi = hue.roi(selection);
            let maskroi = mask.roi(selection);

            let raw_hist = roi.calc_hist(std::ptr::null(),
                                         maskroi,
                                         1,
                                         &hsize,
                                         &phranges[0] as *const *const f32);
            hist =
                raw_hist.normalize(0 as f64, 255 as f64, NormTypes::NormMinMax);

            track_window = selection;
            m.rectangle(selection);
            selection_status.status = false;
            is_tracking = true;
        }

        if is_tracking {
            let mut back_project = hue.calc_back_project(std::ptr::null(),
                                                         &hist,
                                                         &phranges[0] as *const *const f32);
            back_project.logic_and(mask);
            let criteria = TermCriteria::new(TermType::Count, 10, 1 as f64);
            let track_box = back_project.camshift(track_window, &criteria);

            m.rectangle(track_box.bounding_rect());
        }

        m.show("Window", 30);
    }
}
<commit_msg>style(rust): correct wrong format<commit_after>extern crate rust_vision;
use rust_vision::*;

struct SelectionStatus {
    selection: Rect,
    status: bool,
}

fn on_mouse(e: i32, x: i32, y: i32, _: i32, data: *mut CVoid) {
    let event: MouseEventTypes = unsafe { std::mem::transmute(e as u8) };
    match event {
        MouseEventTypes::LButtonDown => {
            let ss = data as *mut SelectionStatus;
            let mut selection = unsafe { &mut (*ss).selection };
            selection.x = x;
            selection.y = y;
        }
        MouseEventTypes::LButtonUp => {
            let ss = data as *mut SelectionStatus;
            let mut selection = unsafe { &mut (*ss).selection };
            let mut status = unsafe { &mut (*ss).status };
            selection.width = x - selection.x;
            selection.height = y - selection.y;

            if selection.width > 0 && selection.height > 0 {
                *status = true;
            }
        }
        _ => {}
    }
}

fn main() {
    let mut selection_status = SelectionStatus {
        selection: Rect::default(),
        status: false,
    };
    let ss_ptr = &mut selection_status as *mut SelectionStatus;

    let cap = VideoCapture::new(0);
    assert!(cap.is_open());

    highgui_named_window("Window", WindowFlags::WindowAutosize);
    highgui_set_mouse_callback("Window", on_mouse, ss_ptr as *mut CVoid);

    let m = Mat::new();
    let mut is_tracking = false;

    let mut hist = Mat::new();
    let hsize = 16;
    let hranges = [0 as f32, 180 as f32];
    let phranges: [*const f32; 1] = [&hranges[0] as *const f32];
    let mut track_window = Rect::default();

    loop {
        cap.read(&m);
        let hsv = m.cvt_color(ColorConversionCodes::BGR2HSV);

        let ch = [0, 0];
        let hue = hsv.mix_channels(1, 1, &ch[0] as *const i32, 1);
        let mask =
            hsv.in_range(Scalar::new(0, 30, 10, 0),
                         Scalar::new(180, 256, 256, 0));

        if selection_status.status {
            println!("Initialize tracking, setting up CAMShift search");
            let selection = selection_status.selection;
            let roi = hue.roi(selection);
            let maskroi = mask.roi(selection);

            let raw_hist = roi.calc_hist(std::ptr::null(),
                                         maskroi,
                                         1,
                                         &hsize,
                                         &phranges[0] as *const *const f32);
            hist =
                raw_hist.normalize(0 as f64, 255 as f64, NormTypes::NormMinMax);

            track_window = selection;
            m.rectangle(selection);
            selection_status.status = false;
            is_tracking = true;
        }

        if is_tracking {
            let mut back_project = hue.calc_back_project(std::ptr::null(),
                                   &hist,
                                   &phranges[0] as *const *const f32);
            back_project.logic_and(mask);
            let criteria = TermCriteria::new(TermType::Count, 10, 1 as f64);
            let track_box = back_project.camshift(track_window, &criteria);

            m.rectangle(track_box.bounding_rect());
        }

        m.show("Window", 30);
    }
}
<|endoftext|>


<commit_before>extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;

mod lib;

use lib::config;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::DownloadError;
use lib::converter::Converter;

use std::fs::{remove_file,remove_dir};

static VERSION : &'static str = "0.2"; // String not valid
static SLEEP_MS: u32 = 5000;
static CODE_SUCCESS: i8 = 2;
static CODE_SUCCESS_WARNINGS: i8 = 3; // finished with warnings
static CODE_FAILED_INTERNAL: i8 = 10; // internal error
static CODE_FAILED_QUALITY: i8 = 11; // qualitz not available
static CODE_FAILED_UNAVAILABLE: i8 = 12; // source unavailable (private / removed)

lazy_static! {
    pub static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
// enum QueryCodes {
//     Waiting = 0,
//     InProgress = 1,
//     Finished = 2,
//     Failed = 3,
// }

enum Thing { String(String), Bool(bool), None }

fn main() {

    let pool = lib::db_connect(lib::mysql_options(), SLEEP_MS);

    let converter = Converter::new(&CONFIG.general.ffmpeg_bin,&CONFIG.general.mp3_quality , pool.clone());
    let mut print_pause = true;
    loop {
        if let Some(result) = lib::request_entry(& pool) {
            print_pause = true;
            let qid = result.qid.clone();                 //&QueryCodes::InProgress as i32
            lib::set_query_code(&mut pool.get_conn().unwrap(), &1, &result.qid).ok().expect("Failed to set query code!");
            lib::set_query_state(&pool.clone(),&qid, "started", false);

            let action_result: Result<Thing,DownloadError>;
            {
                let mut left_files: Vec<String> = Vec::with_capacity(2);
                if result.playlist {
                    action_result = handle_playlist(result, &converter,&mut left_files);
                }else{
                    action_result = handle_download(result, &None, &converter,&mut left_files);
                }

                if !left_files.is_empty() {
                    println!("cleaning up files");
                    for i in &left_files {
                        match remove_file(&i) {
                            Ok(_) => (println!("cleaning up {}",i)),
                            Err(e) => println!("unable to remove file '{}' {}",i,e),
                        }
                    }
                }
            }

            let code: i8 = match action_result {
                    Ok(t) => {
                        match t {
                            Thing::Bool(b) => {
                                if b { CODE_SUCCESS_WARNINGS } else { CODE_SUCCESS }
                            }
                            _ => CODE_SUCCESS,
                        }
                    },
                    Err(e) => {
                        println!("Error: {:?}", e);
                        match e {
                            DownloadError::NotAvailable => CODE_FAILED_UNAVAILABLE,
                            DownloadError::ExtractorError => CODE_FAILED_UNAVAILABLE,
                            DownloadError::QualityNotAvailable => CODE_FAILED_QUALITY,
                            _ => CODE_FAILED_INTERNAL,
                        }
                    }
            };
            lib::set_query_code(&mut pool.get_conn().unwrap(), &code,&qid).ok().expect("Failed to set query code!");

            let state = match code {
                2 => "finished",
                10 => "internal error",
                11 => "quality not available",
                12 => "source unavailable",
                _ => "unknown"
            };

            lib::set_query_state(&pool.clone(),&qid, state, true);

        } else {
            if print_pause { println!("Pausing.."); print_pause = false; }
            std::thread::sleep_ms(SLEEP_MS);
        }
    }
}

///Download handler
///Used by the playlist/file handler to download one file
///Based on the quality it needs to download audio & video separately and convert them together
///In case of a playlist download it depends on the target download folder & if it should be bezipped
///In case of a DMCA we need to download the file via the socket connector,
///which will output a mp3, or if requested, the video but with the highest quality.
///Thus in case of a DMCA we can't pick a quality anymore.
///Also the filename depends on the socket output then.
///
///If it's a non-zipped single file, it's moved after a successful download, converted etc to the
///main folder from which it should be downloadable.
///The original non-ascii & url_encode'd name of the file is stored in the DB
fn handle_download<'a>(downl_db: DownloadDB, folder: &Option<String>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing,DownloadError>{
    //update progress
    let is_zipped = match *folder {
        Some(_) => true,
        None => false,
    };
    if !downl_db.compress {
        lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, 0,false);
    }

    let download = Downloader::new(&downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded

    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            println!("DMCA error!");
            match download.lib_request_video(1,0) {
                Err(err) => { println!("Offliberty-call error {:?}", err); return Err(err); },
                Ok(v) => { dmca = true; v },
            }
        },
        Err(e) => { // unknown error / restricted source etc.. abort
            println!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    let name_http_valid = lib::format_file_name(&name, &download, &downl_db.qid);

    let file_path = lib::format_file_path(&downl_db.qid, folder.clone(), false);
    file_db.push(file_path.clone());
    let save_path = lib::format_save_path(folder.clone(),&name, &download, &downl_db.qid);

    println!("Filename: {}", name);

    let is_splitted_video = if dmca {
        false
    } else {
        lib::is_split_container(&downl_db.quality)
    };
    let convert_audio = CONFIG.extensions.mp3.contains(&downl_db.quality);

    let total_steps = if dmca {
        if download.is_audio() {
            3
        } else {
            2
        }
    } else if is_splitted_video {
        4
    } else if download.is_audio() {
        3
    } else {
        2
    };

    if !dmca {
        if !downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, total_steps,false);
        }

        //download first file, download audio raw source if specified or video
        try!(download.download_file(&file_path, convert_audio));

        if is_splitted_video {
            // download audio file & convert together
			if !downl_db.compress {
                lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 3, total_steps,false);
			}

            let audio_path = lib::format_file_path(&downl_db.qid, folder.clone(), true);
            file_db.push(audio_path.clone());

            println!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));

            if !downl_db.compress {
                lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 4, total_steps,false);
            }

            match converter.merge_files(&downl_db.qid,&file_path, &audio_path,&save_path, !downl_db.compress) {
                Err(e) => {println!("merge error: {:?}",e); return Err(e);},
                Ok(()) => {},
            }

            try!(remove_file(&audio_path));
            file_db.pop();

        }else{ // we're already done, only need to copy if it's not raw audio source
            if !download.is_audio() {
                try!(lib::move_file(&file_path, &save_path));
            }
        }
    }

    if download.is_audio(){ // if audio-> convert m4a to mp3, which converts directly to downl. dir
        if !downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, false);
        }
        try!(converter.extract_audio(&file_path, &save_path,convert_audio));
        try!(remove_file(&file_path));
    }else{
        if is_splitted_video {
            try!(remove_file(&file_path));
        }else{
            try!(lib::move_file(&file_path, &save_path));
        }
    }
    file_db.pop();

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        lib::add_file_entry(&downl_db.pool.clone(), &downl_db.qid,&name_http_valid, &name);
    }
    if !downl_db.compress {
        lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, true);
    }

    if folder.is_some() {
        Ok(Thing::String(save_path))
    } else {
        Ok(Thing::None)
    }
}

///Handles a playlist request
///If zipping isn't requested the downloads will be split up,
///so for each video in the playlist an own query entry will be created
///if warnings occured (unavailable video etc) the return will be true
fn handle_playlist(mut downl_db: DownloadDB, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing, DownloadError>{
    let mut max_steps: i32 = if downl_db.compress { 4 } else { 3 };
    lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, max_steps,false);

    let pl_id: i64 = downl_db.qid;
    downl_db.update_folder(format!("{}/{}",&CONFIG.general.save_dir,pl_id));
    println!("Folder:  {}",downl_db.folder);

    let db_copy = downl_db.clone();
    let download = Downloader::new(&db_copy, &CONFIG.general);

    let mut playlist_name = String::new();
    if downl_db.compress {
        playlist_name = try!(download.get_playlist_name());
        playlist_name = lib::url_encode(&playlist_name);
        println!("pl name {}",playlist_name);
        try!(std::fs::create_dir(&downl_db.folder));
        file_db.push(downl_db.folder.clone());
    }
    lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, max_steps,false);
    println!("retriving playlist videos..");
    let file_ids: Vec<String> = try!(download.get_playlist_ids());

    let handler_folder = if downl_db.compress {
        Some(pl_id.to_string())
    }else {
        None
    };

    println!("got em");
    max_steps += file_ids.len() as i32;
    let mut current_step = 2;
    let mut warnings = false;
    let mut current_url: String;
    let mut failed_log: String = String::from("Following urls couldn't be downloaded: \n");
    // we'll store all files and delete em later, so we don't need rm -rf
    let mut file_delete_list: Vec<String> = Vec::with_capacity(if downl_db.compress { file_ids.len() } else { 0 });
    for id in file_ids.iter() {
        current_step += 1;
        if downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        }
        current_url = format!("https://wwww.youtube.com/watch?v={}",id);
        downl_db.update_video(current_url.clone(), current_step as i64);
        println!("id: {}",id);
        let db_copy = downl_db.clone();
        match handle_download(db_copy, &handler_folder, converter, file_db) {
            Err(e) => { println!("error downloading {}: {:?}",id,e);
                        failed_log.push_str(&format!("{} {:?}\n", current_url, e));
                        warnings = true;
            },
            Ok(e) => {  if downl_db.compress {
                            match e {
                                Thing::String(v) => file_delete_list.push(v),
                                _ => panic!("handle_download not returning a filename"),
                            }
                        }
            },
        }
    }

    if warnings {
        lib::add_query_status(&downl_db.pool.clone(),&pl_id, &failed_log);
    }

    println!("downloaded all videos");
    if downl_db.compress { // zip to file, add to db & remove all sources
        current_step += 1;
        lib::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        let zip_name = format!("{}.zip",playlist_name);
        let zip_file = format!("{}/{}",&CONFIG.general.download_dir,zip_name);
        println!("zip file: {} \n zip source {}",zip_file, &downl_db.folder);
        try!(lib::zip_folder(&downl_db.folder, &zip_file));
        lib::add_file_entry(&downl_db.pool.clone(), &pl_id,&zip_name, &playlist_name);

        current_step += 1;
        lib::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        try!(lib::delete_files(file_delete_list));
        try!(remove_dir(downl_db.folder));
        file_db.pop();
    }

    Ok(Thing::Bool(warnings))
}
<commit_msg>added error detail insert & fixed style<commit_after>extern crate mysql;
extern crate toml;
extern crate rustc_serialize;
#[macro_use]
extern crate lazy_static;

mod lib;

use lib::config;
use lib::downloader::DownloadDB;
use lib::downloader::Downloader;
use lib::DownloadError;
use lib::converter::Converter;

use std::fs::{remove_file,remove_dir};

static VERSION : &'static str = "0.2"; // String not valid
static SLEEP_MS: u32 = 5000;
static CODE_SUCCESS: i8 = 2;
static CODE_SUCCESS_WARNINGS: i8 = 3; // finished with warnings
static CODE_FAILED_INTERNAL: i8 = 10; // internal error
static CODE_FAILED_QUALITY: i8 = 11; // qualitz not available
static CODE_FAILED_UNAVAILABLE: i8 = 12; // source unavailable (private / removed)

lazy_static! {
    pub static ref CONFIG: config::Config = {
        println!("Starting yayd-backend v{}",&VERSION);
        config::init_config() //return
    };

}

//#[repr(i8)] broken, enum not usable as of #10292
// enum QueryCodes {
//     Waiting = 0,
//     InProgress = 1,
//     Finished = 2,
//     Failed = 3,
// }

enum Thing { String(String), Bool(bool), None }

fn main() {

    let pool = lib::db_connect(lib::mysql_options(), SLEEP_MS);

    let converter = Converter::new(&CONFIG.general.ffmpeg_bin,&CONFIG.general.mp3_quality , pool.clone());
    let mut print_pause = true;
    loop {
        if let Some(result) = lib::request_entry(& pool) {
            print_pause = true;
            let qid = result.qid.clone();                 //&QueryCodes::InProgress as i32
            lib::set_query_code(&mut pool.get_conn().unwrap(), &1, &result.qid).ok().expect("Failed to set query code!");
            lib::set_query_state(&pool.clone(),&qid, "started", false);

            let action_result: Result<Thing,DownloadError>;
            {
                let mut left_files: Vec<String> = Vec::with_capacity(2);
                if result.playlist {
                    action_result = handle_playlist(result, &converter,&mut left_files);
                }else{
                    action_result = handle_download(result, &None, &converter,&mut left_files);
                }

                if !left_files.is_empty() {
                    println!("cleaning up files");
                    for i in &left_files {
                        match remove_file(&i) {
                            Ok(_) => (println!("cleaning up {}",i)),
                            Err(e) => println!("unable to remove file '{}' {}",i,e),
                        }
                    }
                }
            }

            let code: i8 = match action_result {
                Ok(t) => {
                    match t {
                        Thing::Bool(b) => {
                            if b { CODE_SUCCESS_WARNINGS } else { CODE_SUCCESS }
                        }
                        _ => CODE_SUCCESS,
                    }
                },
                Err(e) => {
                    println!("Error: {:?}", e);
                    match e {
                        DownloadError::NotAvailable => CODE_FAILED_UNAVAILABLE,
                        DownloadError::ExtractorError => CODE_FAILED_UNAVAILABLE,
                        DownloadError::QualityNotAvailable => CODE_FAILED_QUALITY,
                        _ => {
                            let details = match e {
                                DownloadError::DBError(s) => s,
                                DownloadError::DownloadError(s) => s,
                                DownloadError::FFMPEGError(s) => s,
                                DownloadError::InternalError(s) => s,
                                _ => unreachable!(),
                            };
                            lib::add_query_status(&pool.clone(),&qid, &details);
                            CODE_FAILED_INTERNAL
                        },
                    }
                }
            };
            lib::set_query_code(&mut pool.get_conn().unwrap(), &code,&qid).ok().expect("Failed to set query code!");

            let state = match code {
                2 => "finished",
                10 => "internal error",
                11 => "quality not available",
                12 => "source unavailable",
                _ => "unknown"
            };

            lib::set_query_state(&pool.clone(),&qid, state, true);

        } else {
            if print_pause { println!("Pausing.."); print_pause = false; }
            std::thread::sleep_ms(SLEEP_MS);
        }
    }
}

///Download handler
///Used by the playlist/file handler to download one file
///Based on the quality it needs to download audio & video separately and convert them together
///In case of a playlist download it depends on the target download folder & if it should be bezipped
///In case of a DMCA we need to download the file via the socket connector,
///which will output a mp3, or if requested, the video but with the highest quality.
///Thus in case of a DMCA we can't pick a quality anymore.
///Also the filename depends on the socket output then.
///
///If it's a non-zipped single file, it's moved after a successful download, converted etc to the
///main folder from which it should be downloadable.
///The original non-ascii & url_encode'd name of the file is stored in the DB
fn handle_download<'a>(downl_db: DownloadDB, folder: &Option<String>, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing,DownloadError>{
    //update progress
    let is_zipped = match *folder {
        Some(_) => true,
        None => false,
    };
    if !downl_db.compress {
        lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, 0,false);
    }

    let download = Downloader::new(&downl_db, &CONFIG.general);
    //get filename, check for DMCA
    let mut dmca = false; // "succ." dmca -> file already downloaded

    let name = match download.get_file_name() { // get filename
        Ok(v) => v,
        Err(DownloadError::DMCAError) => { //now request via lib.. // k if( k == Err(DownloadError::DMCAError) )
            println!("DMCA error!");
            match download.lib_request_video(1,0) {
                Err(err) => { println!("Offliberty-call error {:?}", err); return Err(err); },
                Ok(v) => { dmca = true; v },
            }
        },
        Err(e) => { // unknown error / restricted source etc.. abort
            println!("Unknown error: {:?}", e);
            return Err(e);
        },
    };

    let name_http_valid = lib::format_file_name(&name, &download, &downl_db.qid);

    let file_path = lib::format_file_path(&downl_db.qid, folder.clone(), false);
    file_db.push(file_path.clone());
    let save_path = lib::format_save_path(folder.clone(),&name, &download, &downl_db.qid);

    println!("Filename: {}", name);

    let is_splitted_video = if dmca {
        false
    } else {
        lib::is_split_container(&downl_db.quality)
    };
    let convert_audio = CONFIG.extensions.mp3.contains(&downl_db.quality);

    let total_steps = if dmca {
        if download.is_audio() {
            3
        } else {
            2
        }
    } else if is_splitted_video {
        4
    } else if download.is_audio() {
        3
    } else {
        2
    };

    if !dmca {
        if !downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, total_steps,false);
        }

        //download first file, download audio raw source if specified or video
        try!(download.download_file(&file_path, convert_audio));

        if is_splitted_video {
            // download audio file & convert together
			if !downl_db.compress {
                lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 3, total_steps,false);
			}

            let audio_path = lib::format_file_path(&downl_db.qid, folder.clone(), true);
            file_db.push(audio_path.clone());

            println!("Downloading audio.. {}", audio_path);
            try!(download.download_file(&audio_path, true));

            if !downl_db.compress {
                lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 4, total_steps,false);
            }

            match converter.merge_files(&downl_db.qid,&file_path, &audio_path,&save_path, !downl_db.compress) {
                Err(e) => {println!("merge error: {:?}",e); return Err(e);},
                Ok(()) => {},
            }

            try!(remove_file(&audio_path));
            file_db.pop();

        }else{ // we're already done, only need to copy if it's not raw audio source
            if !download.is_audio() {
                try!(lib::move_file(&file_path, &save_path));
            }
        }
    }

    if download.is_audio(){ // if audio-> convert m4a to mp3, which converts directly to downl. dir
        if !downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, false);
        }
        try!(converter.extract_audio(&file_path, &save_path,convert_audio));
        try!(remove_file(&file_path));
    }else{
        if is_splitted_video {
            try!(remove_file(&file_path));
        }else{
            try!(lib::move_file(&file_path, &save_path));
        }
    }
    file_db.pop();

    if !is_zipped { // add file to list, except it's for zip-compression later (=folder set)
        lib::add_file_entry(&downl_db.pool.clone(), &downl_db.qid,&name_http_valid, &name);
    }
    if !downl_db.compress {
        lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, total_steps, total_steps, true);
    }

    if folder.is_some() {
        Ok(Thing::String(save_path))
    } else {
        Ok(Thing::None)
    }
}

///Handles a playlist request
///If zipping isn't requested the downloads will be split up,
///so for each video in the playlist an own query entry will be created
///if warnings occured (unavailable video etc) the return will be true
fn handle_playlist(mut downl_db: DownloadDB, converter: &Converter, file_db: &mut Vec<String>) -> Result<Thing, DownloadError>{
    let mut max_steps: i32 = if downl_db.compress { 4 } else { 3 };
    lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 1, max_steps,false);

    let pl_id: i64 = downl_db.qid;
    downl_db.update_folder(format!("{}/{}",&CONFIG.general.save_dir,pl_id));
    println!("Folder:  {}",downl_db.folder);

    let db_copy = downl_db.clone();
    let download = Downloader::new(&db_copy, &CONFIG.general);

    let mut playlist_name = String::new();
    if downl_db.compress {
        playlist_name = try!(download.get_playlist_name());
        playlist_name = lib::url_encode(&playlist_name);
        println!("pl name {}",playlist_name);
        try!(std::fs::create_dir(&downl_db.folder));
        file_db.push(downl_db.folder.clone());
    }
    lib::update_steps(&downl_db.pool.clone(),&downl_db.qid, 2, max_steps,false);
    println!("retriving playlist videos..");
    let file_ids: Vec<String> = try!(download.get_playlist_ids());

    let handler_folder = if downl_db.compress {
        Some(pl_id.to_string())
    }else {
        None
    };

    println!("got em");
    max_steps += file_ids.len() as i32;
    let mut current_step = 2;
    let mut warnings = false;
    let mut current_url: String;
    let mut failed_log: String = String::from("Following urls couldn't be downloaded: \n");
    // we'll store all files and delete em later, so we don't need rm -rf
    let mut file_delete_list: Vec<String> = Vec::with_capacity(if downl_db.compress { file_ids.len() } else { 0 });
    for id in file_ids.iter() {
        current_step += 1;
        if downl_db.compress {
            lib::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        }
        current_url = format!("https://wwww.youtube.com/watch?v={}",id);
        downl_db.update_video(current_url.clone(), current_step as i64);
        println!("id: {}",id);
        let db_copy = downl_db.clone();
        match handle_download(db_copy, &handler_folder, converter, file_db) {
            Err(e) => { println!("error downloading {}: {:?}",id,e);
                        failed_log.push_str(&format!("{} {:?}\n", current_url, e));
                        warnings = true;
            },
            Ok(e) => {  if downl_db.compress {
                            match e {
                                Thing::String(v) => file_delete_list.push(v),
                                _ => panic!("handle_download not returning a filename"),
                            }
                        }
            },
        }
    }

    if warnings {
        lib::add_query_status(&downl_db.pool.clone(),&pl_id, &failed_log);
    }

    println!("downloaded all videos");
    if downl_db.compress { // zip to file, add to db & remove all sources
        current_step += 1;
        lib::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        let zip_name = format!("{}.zip",playlist_name);
        let zip_file = format!("{}/{}",&CONFIG.general.download_dir,zip_name);
        println!("zip file: {} \n zip source {}",zip_file, &downl_db.folder);
        try!(lib::zip_folder(&downl_db.folder, &zip_file));
        lib::add_file_entry(&downl_db.pool.clone(), &pl_id,&zip_name, &playlist_name);

        current_step += 1;
        lib::update_steps(&downl_db.pool.clone(),&pl_id, current_step, max_steps,false);
        try!(lib::delete_files(file_delete_list));
        try!(remove_dir(downl_db.folder));
        file_db.pop();
    }

    Ok(Thing::Bool(warnings))
}
<|endoftext|>


<commit_before>extern crate euclid;
#[macro_use]
extern crate glium;
extern crate image;
extern crate vitral;
extern crate serde;

mod backend;

use serde::{Serialize, Serializer, Deserialize, Deserializer};
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::hash::Hash;
pub use euclid::Point2D;
pub use glium::{glutin, DisplayBuild};
pub use backend::Backend;

type Color = [f32; 4];

type ImageRef = usize;

type Splat = Vec<(ImageRef, Point2D<f32>, Color)>;

/// Smart pointer for a static cached resource.
#[derive(Clone)]
struct Resource<T, K = String> {
    handle: Rc<T>,
    key: K,
}

impl<T: Sized, K> AsRef<T> for Resource<T, K> {
    fn as_ref(&self) -> &T {
        self.handle.as_ref()
    }
}

impl<T: Sized, K> Deref for Resource<T, K> {
    type Target = T;

    #[inline(always)]
    fn deref(&self) -> &T {
        self.handle.deref()
    }
}

impl<T> Serialize for Resource<T> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        self.key.serialize(serializer)
    }
}

impl<T: ResourceStore> Deserialize for Resource<T> {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        let key: String = try!(Deserialize::deserialize(deserializer));
        Ok(Self::new(key).unwrap())
    }
}

impl<K, T: ResourceStore<K>> Resource<T, K> {
    pub fn new(key: K) -> Option<Self> {
        if let Some(handle) = ResourceStore::get_resource(&key) {
            Some(Resource { handle: handle, key: key })
        } else {
            None
        }
    }
}

/// A value that can be aquired given a resource path.
trait Loadable<K = String> {
    fn load(key: &K) -> Option<Self> where Self: Sized {
        // Default implementation so that types with no load semantics can be used with
        // ResourceCache so that all inserts must be explicit.
        None
    }
}

impl Loadable for image::DynamicImage {
    fn load(key: &String) -> Option<Self> where Self: Sized {
        image::open(key).ok()
    }
}

/// A cache that associates resource values with paths.
///
/// Resources and paths are assumed to be immutable.
struct ResourceCache<T, K = String> {
    cache: HashMap<K, Rc<T>>,
}

impl<K: Eq + Hash + Clone, T: Loadable<K>> ResourceCache<T, K> {
    pub fn new() -> ResourceCache<T, K> {
        ResourceCache {
            cache: HashMap::new()
        }
    }

    pub fn get(&mut self, key: &K) -> Option<Rc<T>> {
        if let Some(v) = self.cache.get(key) {
            return Some(v.clone());
        }

        if let Some(v) = T::load(key) {
            let v = Rc::new(v);
            self.cache.insert(key.clone(), v.clone());
            Some(v)
        } else {
            None
        }
    }

    pub fn insert(&mut self, key: K, value: T) {
        self.cache.insert(key, Rc::new(value));
    }
}

/// A type that implements a singleton resource store.
trait ResourceStore<K = String> {
    fn get_resource(key: &K) -> Option<Rc<Self>> where Self: Sized;
}

thread_local!(static DYNAMIC_IMAGE: RefCell<ResourceCache<image::DynamicImage>> =
              RefCell::new(ResourceCache::new()));

impl ResourceStore for image::DynamicImage {
    fn get_resource(path: &String) -> Option<Rc<Self>> {
        DYNAMIC_IMAGE.with(|t| t.borrow_mut().get(path))
    }
}

pub fn main() {
    let display = glutin::WindowBuilder::new()
                      .build_glium()
                      .unwrap();

    let mut backend = Backend::new(&display);

    // Construct Vitral context.
    let mut context: backend::Context;
    let mut builder = vitral::Builder::new();

    let r: Resource<image::DynamicImage> = Resource::new("content/assets/blocks.png".to_string()).unwrap();
    let image = builder.add_image(&*r);

    context = builder.build(|img| backend.make_texture(&display, img));

    let font = context.default_font();

    loop {
        context.begin_frame();

        context.draw_text(font, Point2D::new(4.0, 20.0), [1.0, 1.0, 1.0, 1.0], "Hello, world!");

        context.draw_image(image, Point2D::new(50.0, 50.0), [1.0, 1.0, 1.0, 1.0]);

        if !backend.update(&display, &mut context) {
            return;
        }
    }
}
<commit_msg>WIP subimage spec<commit_after>extern crate euclid;
#[macro_use]
extern crate glium;
extern crate image;
extern crate vitral;
extern crate serde;

mod backend;

use serde::{Serialize, Serializer, Deserialize, Deserializer};
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::hash::Hash;
pub use euclid::Point2D;
pub use glium::{glutin, DisplayBuild};
pub use backend::Backend;

type Color = [f32; 4];

type ImageRef = usize;

type Splat = Vec<(ImageRef, Point2D<f32>, Color)>;

/// Smart pointer for a static cached resource.
#[derive(Clone)]
struct Resource<T, K = String> {
    handle: Rc<T>,
    key: K,
}

impl<T: Sized, K> AsRef<T> for Resource<T, K> {
    fn as_ref(&self) -> &T {
        self.handle.as_ref()
    }
}

impl<T: Sized, K> Deref for Resource<T, K> {
    type Target = T;

    #[inline(always)]
    fn deref(&self) -> &T {
        self.handle.deref()
    }
}

impl<T> Serialize for Resource<T> {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        self.key.serialize(serializer)
    }
}

impl<T: ResourceStore> Deserialize for Resource<T> {
    fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        let key: String = try!(Deserialize::deserialize(deserializer));
        Ok(Self::new(key).unwrap())
    }
}

impl<K, T: ResourceStore<K>> Resource<T, K> {
    pub fn new(key: K) -> Option<Self> {
        if let Some(handle) = ResourceStore::get_resource(&key) {
            Some(Resource { handle: handle, key: key })
        } else {
            None
        }
    }
}

/// A value that can be aquired given a resource path.
trait Loadable<K = String> {
    fn load(key: &K) -> Option<Self> where Self: Sized {
        // Default implementation so that types with no load semantics can be used with
        // ResourceCache so that all inserts must be explicit.
        None
    }
}

impl Loadable for image::DynamicImage {
    fn load(key: &String) -> Option<Self> where Self: Sized {
        image::open(key).ok()
    }
}

/// A cache that associates resource values with paths.
///
/// Resources and paths are assumed to be immutable.
struct ResourceCache<T, K = String> {
    cache: HashMap<K, Rc<T>>,
}

impl<K: Eq + Hash + Clone, T: Loadable<K>> ResourceCache<T, K> {
    pub fn new() -> ResourceCache<T, K> {
        ResourceCache {
            cache: HashMap::new()
        }
    }

    pub fn get(&mut self, key: &K) -> Option<Rc<T>> {
        if let Some(v) = self.cache.get(key) {
            return Some(v.clone());
        }

        if let Some(v) = T::load(key) {
            let v = Rc::new(v);
            self.cache.insert(key.clone(), v.clone());
            Some(v)
        } else {
            None
        }
    }

    pub fn insert(&mut self, key: K, value: T) {
        self.cache.insert(key, Rc::new(value));
    }
}

/// A type that implements a singleton resource store.
trait ResourceStore<K = String> {
    fn get_resource(key: &K) -> Option<Rc<Self>> where Self: Sized;
}

thread_local!(static DYNAMIC_IMAGE: RefCell<ResourceCache<image::DynamicImage>> =
              RefCell::new(ResourceCache::new()));

impl ResourceStore for image::DynamicImage {
    fn get_resource(path: &String) -> Option<Rc<Self>> {
        DYNAMIC_IMAGE.with(|t| t.borrow_mut().get(path))
    }
}

struct SubImageSpec {
    image: Resource<image::DynamicImage>,
    bounds: euclid::Rect<u32>,
}

pub fn main() {
    let display = glutin::WindowBuilder::new()
                      .build_glium()
                      .unwrap();

    let mut backend = Backend::new(&display);

    // Construct Vitral context.
    let mut context: backend::Context;
    let mut builder = vitral::Builder::new();

    let r: Resource<image::DynamicImage> = Resource::new("content/assets/blocks.png".to_string()).unwrap();
    let image = builder.add_image(&*r);

    context = builder.build(|img| backend.make_texture(&display, img));

    let font = context.default_font();

    loop {
        context.begin_frame();

        context.draw_text(font, Point2D::new(4.0, 20.0), [1.0, 1.0, 1.0, 1.0], "Hello, world!");

        context.draw_image(image, Point2D::new(50.0, 50.0), [1.0, 1.0, 1.0, 1.0]);

        if !backend.update(&display, &mut context) {
            return;
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate term;

use libc::{c_uint, c_uchar, c_int};
use std::io;

// used in Termios struct
pub const NCCS:uint = 32;

// stdin, stdout, stderr have standard file descriptors
pub const STDIN:c_int = 0;

// constants for control sequences is useful
pub const EOF:char = '\u{4}';
pub const DEL:char = '\u{7f}';

// select termios constants that we use
pub const ICANON:c_uint   = 2;
pub const ECHO:c_uint     = 8;
pub const TCSANOW:c_int   = 0;
pub const TCSADRAIN:c_int = 1;
pub const TCSAFLUSH:c_int = 2;

// types used in Termios struct
#[allow(non_camel_case_types)]
pub type cc_t = c_uchar;
#[allow(non_camel_case_types)]
pub type speed_t = c_uint;
#[allow(non_camel_case_types)]
pub type tcflag_t = c_uint;

#[repr(C)]
#[deriving(Copy)]
#[deriving(Clone)]
pub struct Termios {
    c_iflag: tcflag_t,
    c_oflag: tcflag_t,
    c_cflag: tcflag_t,
    c_lflag: tcflag_t,
    c_line: cc_t,
    c_cc: [c_uchar, ..NCCS],
    c_ispeed: speed_t,
    c_ospeed: speed_t,
}

impl Termios {
    pub fn new() -> Termios {
        Termios {
            c_cc: [0, ..NCCS],
            c_cflag: 0,
            c_iflag: 0,
            c_ispeed: 0,
            c_lflag: 0,
            c_line: 0,
            c_oflag: 0,
            c_ospeed: 0,
        }
    }

    pub fn get_from(&mut self, fd:c_int) -> bool {
        unsafe {
            return tcgetattr(fd, self) == 0;
        }
    }

    pub fn get(&mut self) -> bool {
        self.get_from(STDIN)
    }

    pub fn set_to(&self, fd:c_int) -> bool {
        unsafe {
            return tcsetattr(fd, TCSANOW, self) == 0;
        }
    }

    pub fn set(&self) -> bool {
        self.set_to(STDIN)
    }

    pub fn lenable(&mut self, flag:c_uint) {
        self.c_lflag |= flag;
    }

    pub fn ldisable(&mut self, flag:c_uint) {
        self.c_lflag &= !flag;
    }
}

#[link(name = "c")]
extern {
    pub fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int;
    pub fn tcsetattr(fd: c_int, optional_actions: c_int, termios: *const Termios) -> c_int;
}

fn empty_escape(esc:&mut Iterator<char>) -> String {
    let mut out = String::new();
    loop {
        match esc.next() {
            Some(c) => out.push(c),
            None => break
        }
    }
    return out;
}

fn prepare_terminal() -> Termios {
    let mut tios = Termios::new();
    tios.get();
    let tios_clone = tios.clone();
    // turn off canonical mode
    tios.ldisable(ICANON);
    // turn off echo mode
    tios.ldisable(ECHO);
    // set the terminal mode
    update_terminal(tios);
    // return the old terminal mode
    return tios_clone;
}

fn update_terminal(tios:Termios) -> bool {
    if !tios.set() {
        io::stderr().write_line("Warning: Could not set terminal mode").unwrap_err();
        return false;
    }
    return true;
}

fn main() {
    let old_tios = prepare_terminal();
    let mut stdin = io::stdin();
    loop {
        match stdin.read_char() {
            Ok(EOF) => break,
            Ok(DEL) => {
                print!("{} {}", DEL, DEL);
            },
            Ok(c) => {
                print!("{}", empty_escape(&mut c.escape_default()))
            },
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        }
    }
    // print so we know we've reached this code
    println!("Exiting");
    // restore old term state
    update_terminal(old_tios);
}
<commit_msg>Improve comments<commit_after>extern crate libc;
extern crate term;

use libc::{c_uint, c_uchar, c_int};
use std::io;

// used in Termios struct
pub const NCCS:uint = 32;

// stdin, stdout, stderr have standard file descriptors
pub const STDIN:c_int = 0;

// constants for control sequences is useful
pub const EOF:char = '\u{4}';
pub const DEL:char = '\u{7f}';

// select termios constants that we use
pub const ICANON:c_uint   = 2;
pub const ECHO:c_uint     = 8;
pub const TCSANOW:c_int   = 0;
pub const TCSADRAIN:c_int = 1;
pub const TCSAFLUSH:c_int = 2;

// types used in Termios struct
#[allow(non_camel_case_types)]
pub type cc_t = c_uchar;
#[allow(non_camel_case_types)]
pub type speed_t = c_uint;
#[allow(non_camel_case_types)]
pub type tcflag_t = c_uint;

#[repr(C)]
#[deriving(Copy)]
#[deriving(Clone)]
pub struct Termios {
    c_iflag: tcflag_t,
    c_oflag: tcflag_t,
    c_cflag: tcflag_t,
    c_lflag: tcflag_t,
    c_line: cc_t,
    c_cc: [c_uchar, ..NCCS],
    c_ispeed: speed_t,
    c_ospeed: speed_t,
}

impl Termios {
    pub fn new() -> Termios {
        Termios {
            c_cc: [0, ..NCCS],
            c_cflag: 0,
            c_iflag: 0,
            c_ispeed: 0,
            c_lflag: 0,
            c_line: 0,
            c_oflag: 0,
            c_ospeed: 0,
        }
    }

    pub fn get_from(&mut self, fd:c_int) -> bool {
        unsafe {
            return tcgetattr(fd, self) == 0;
        }
    }

    pub fn get(&mut self) -> bool {
        self.get_from(STDIN)
    }

    pub fn set_to(&self, fd:c_int) -> bool {
        unsafe {
            return tcsetattr(fd, TCSANOW, self) == 0;
        }
    }

    pub fn set(&self) -> bool {
        self.set_to(STDIN)
    }

    pub fn lenable(&mut self, flag:c_uint) {
        self.c_lflag |= flag;
    }

    pub fn ldisable(&mut self, flag:c_uint) {
        self.c_lflag &= !flag;
    }
}

#[link(name = "c")]
extern {
    pub fn tcgetattr(fd: c_int, termios: *mut Termios) -> c_int;
    pub fn tcsetattr(fd: c_int, optional_actions: c_int, termios: *const Termios) -> c_int;
}

fn empty_escape(esc:&mut Iterator<char>) -> String {
    let mut out = String::new();
    loop {
        match esc.next() {
            Some(c) => out.push(c),
            None => break
        }
    }
    return out;
}

fn prepare_terminal() -> Termios {
    // new terminal mode info
    let mut tios = Termios::new();
    // populate terminal info
    tios.get();
    let tios_clone = tios.clone();
    // turn off canonical mode
    tios.ldisable(ICANON);
    // turn off echo mode
    tios.ldisable(ECHO);
    // set the terminal mode
    update_terminal(tios);
    // return the old terminal mode
    return tios_clone;
}

fn update_terminal(tios:Termios) -> bool {
    if !tios.set() {
        io::stderr().write_line("Warning: Could not set terminal mode").unwrap_err();
        return false;
    }
    return true;
}

fn main() {
    let old_tios = prepare_terminal();
    let mut stdin = io::stdin();
    loop {
        // Note: in non-canonical mode
        match stdin.read_char() {
            Ok(EOF) => break,
            Ok(DEL) => {
                print!("{} {}", DEL, DEL);
            },
            Ok(c) => {
                print!("{}", empty_escape(&mut c.escape_default()))
            },
            Err(e) => {
                println!("Error: {}", e);
                break;
            }
        }
    }
    // print so we know we've reached this code
    println!("Exiting");
    // restore old term state
    update_terminal(old_tios);
}
<|endoftext|>


<commit_before>extern crate syscall;

use std::{env, mem, slice, thread};
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::io::{AsRawFd, RawFd};
use std::cell::Cell;

use color::Color;
use event::{Event, EVENT_RESIZE};
use renderer::Renderer;
use WindowFlag;
use Mode;

pub fn get_display_size() -> Result<(u32, u32), String> {
    let display_path = try!(env::var("DISPLAY").or(Err("DISPLAY not set")));
    match File::open(&display_path) {
        Ok(display) => {
            let mut buf: [u8; 4096] = [0; 4096];
            let count = syscall::fpath(display.as_raw_fd() as usize, &mut buf).map_err(|err| format!("{}", err))?;
            let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
            let res = path.split(":").nth(1).unwrap_or("");
            let width = res.split("/").nth(1).unwrap_or("").parse::<u32>().unwrap_or(0);
            let height = res.split("/").nth(2).unwrap_or("").parse::<u32>().unwrap_or(0);
            Ok((width, height))
        },
        Err(err) => Err(format!("{}", err))
    }
}

/// A window
pub struct Window {
    /// The x coordinate of the window
    x: i32,
    /// The y coordinate of the window
    y: i32,
    /// The width of the window
    w: u32,
    /// The height of the window
    h: u32,
    /// The title of the window
    t: String,
    /// True if the window should not wait for events
    async: bool,
    /// True if the window can be resized
    resizable: bool,
    /// Drawing mode
    mode: Cell<Mode>,
    /// The input scheme
    file: File,
    /// Window data
    data: &'static mut [Color],
}

impl Renderer for Window {
    /// Get width
    fn width(&self) -> u32 {
        self.w
    }

    /// Get height
    fn height(&self) -> u32 {
        self.h
    }

    /// Access pixel buffer
    fn data(&self) -> &[Color] {
        &self.data
    }

    /// Access pixel buffer mutably
    fn data_mut(&mut self) -> &mut [Color] {
        &mut self.data
    }

    /// Flip the buffer
    fn sync(&mut self) -> bool {
        self.file.sync_data().is_ok()
    }

    /// Set/get mode
    fn mode(&self) -> &Cell<Mode> {
        &self.mode
    }
}

impl Window {
    /// Create a new window
    pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Option<Self> {
        Window::new_flags(x, y, w, h, title, &[])
    }

    /// Create a new window with flags
    pub fn new_flags(x: i32, y: i32, w: u32, h: u32, title: &str, flags: &[WindowFlag]) -> Option<Self> {
        let mut flag_str = String::new();

        let mut async = false;
        let mut resizable = false;
        for &flag in flags.iter() {
            match flag {
                WindowFlag::Async => {
                    async = true;
                    flag_str.push('a');
                },
                WindowFlag::Back => flag_str.push('b'),
                WindowFlag::Front => flag_str.push('f'),
                WindowFlag::Borderless => flag_str.push('l'),
                WindowFlag::Resizable => {
                    resizable = true;
                    flag_str.push('r');
                },
                WindowFlag::Unclosable => flag_str.push('u'),
            }
        }

        if let Ok(file) = File::open(&format!(
            "orbital:{}/{}/{}/{}/{}/{}",
            flag_str, x, y, w, h, title
        )) {
            if let Ok(address) = unsafe { syscall::fmap(file.as_raw_fd(), 0, (w * h * 4) as usize) } {
                Some(Window {
                    x: x,
                    y: y,
                    w: w,
                    h: h,
                    t: title.to_string(),
                    async: async,
                    resizable: resizable,
                    mode: Cell::new(Mode::Blend),
                    file: file,
                    data: unsafe { slice::from_raw_parts_mut(address as *mut Color, (w * h) as usize) },
                })
            } else {
                None
            }
        } else {
            None
        }
    }

    // TODO: Replace with smarter mechanism, maybe a move event?
    pub fn sync_path(&mut self) {
        let mut buf: [u8; 4096] = [0; 4096];
        if let Ok(count) = syscall::fpath(self.file.as_raw_fd() as usize, &mut buf) {
            let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
            // orbital:/x/y/w/h/t
            let mut parts = path.split('/').skip(1);
            if let Some(x) = parts.next() {
                self.x = x.parse::<i32>().unwrap_or(0);
            }
            if let Some(y) = parts.next() {
                self.y = y.parse::<i32>().unwrap_or(0);
            }
            if let Some(w) = parts.next() {
                self.w = w.parse::<u32>().unwrap_or(0);
            }
            if let Some(h) = parts.next() {
                self.h = h.parse::<u32>().unwrap_or(0);
            }
        }
    }

    /// Get x
    // TODO: Sync with window movements
    pub fn x(&self) -> i32 {
        self.x
    }

    /// Get y
    // TODO: Sync with window movements
    pub fn y(&self) -> i32 {
        self.y
    }

    /// Get title
    pub fn title(&self) -> String {
        self.t.clone()
    }

    // Set position
    pub fn set_pos(&mut self, x: i32, y: i32) {
        let _ = self.file.write(&format!("P,{},{}", x, y).as_bytes());
        self.sync_path();
    }

    // Set size
    pub fn set_size(&mut self, width: u32, height: u32) {
        //TODO: Improve safety and reliability
        unsafe {
            syscall::funmap(self.data.as_ptr() as usize).expect("orbclient: failed to unmap memory in resize");
        }
        let _ = self.file.write(&format!("S,{},{}", width, height).as_bytes());
        self.sync_path();
        unsafe {
            let address = syscall::fmap(self.file.as_raw_fd(), 0, (self.w * self.h * 4) as usize).expect("orbclient: failed to map memory in resize");
            self.data = slice::from_raw_parts_mut(address as *mut Color, (self.w * self.h) as usize);
        }
    }

    /// Set title
    pub fn set_title(&mut self, title: &str) {
        let _ = self.file.write(&format!("T,{}", title).as_bytes());
        self.sync_path();
    }

    /// Blocking iterator over events
    pub fn events(&mut self) -> EventIter {
        let mut iter = EventIter {
            extra: None,
            events: [Event::new(); 16],
            i: 0,
            count: 0
        };

        'blocking: loop {
            if iter.events.len() - iter.count == 0 {
                if iter.extra.is_none() {
                    iter.extra = Some(Vec::with_capacity(32));
                }
                iter.extra.as_mut().unwrap().extend(&iter.events);
                iter.count = 0;
            }
            let bytes = unsafe { slice::from_raw_parts_mut(
                iter.events[iter.count..].as_mut_ptr() as *mut u8,
                iter.events[iter.count..].len() * mem::size_of::<Event>()
            ) };
            match self.file.read(bytes) {
                Ok(0) => if !self.async
                        && iter.extra.is_none()
                        && iter.count == 0 {
                    thread::yield_now();
                } else {
                    break 'blocking;
                },
                Ok(count) => {
                    let count = count / mem::size_of::<Event>();
                    let events = &iter.events[iter.count..][..count];
                    iter.count += count;

                    if self.resizable {
                        let mut resize = None;
                        for event in events {
                            let event = *event;
                            if event.code == EVENT_RESIZE {
                                resize = Some((event.a as u32, event.b as u32));
                            }
                        }
                        if let Some((w, h)) = resize {
                            self.set_size(w, h);
                        }
                    }
                    if !self.async {
                        // Synchronous windows are blocking, can't attempt another read
                        break 'blocking;
                    }
                },
                Err(_) => break 'blocking,
            }
        }

        iter
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        let _ = unsafe { syscall::funmap(self.data.as_ptr() as usize) };
    }
}

impl AsRawFd for Window {
    fn as_raw_fd(&self) -> RawFd {
        self.file.as_raw_fd()
    }
}

/// Event iterator
pub struct EventIter {
    extra: Option<Vec<Event>>,
    events: [Event; 16],
    i: usize,
    count: usize
}

impl Iterator for EventIter {
    type Item = Event;
    fn next(&mut self) -> Option<Event> {
        let mut i = self.i;
        if let Some(ref mut extra) = self.extra {
            if i < extra.len() {
                if let Some(event) = extra.get(i) {
                    self.i += 1;
                    return Some(*event);
                }
            }
            i -= extra.len();
        }
        if i < self.count {
            if let Some(event) = self.events.get(i) {
                self.i += 1;
                return Some(*event);
            }
        }
        None
    }
}
<commit_msg>Tiny cleanup<commit_after>extern crate syscall;

use std::{env, mem, slice, thread};
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::io::{AsRawFd, RawFd};
use std::cell::Cell;

use color::Color;
use event::{Event, EVENT_RESIZE};
use renderer::Renderer;
use WindowFlag;
use Mode;

pub fn get_display_size() -> Result<(u32, u32), String> {
    let display_path = try!(env::var("DISPLAY").or(Err("DISPLAY not set")));
    match File::open(&display_path) {
        Ok(display) => {
            let mut buf: [u8; 4096] = [0; 4096];
            let count = syscall::fpath(display.as_raw_fd() as usize, &mut buf).map_err(|err| format!("{}", err))?;
            let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
            let res = path.split(":").nth(1).unwrap_or("");
            let width = res.split("/").nth(1).unwrap_or("").parse::<u32>().unwrap_or(0);
            let height = res.split("/").nth(2).unwrap_or("").parse::<u32>().unwrap_or(0);
            Ok((width, height))
        },
        Err(err) => Err(format!("{}", err))
    }
}

/// A window
pub struct Window {
    /// The x coordinate of the window
    x: i32,
    /// The y coordinate of the window
    y: i32,
    /// The width of the window
    w: u32,
    /// The height of the window
    h: u32,
    /// The title of the window
    t: String,
    /// True if the window should not wait for events
    async: bool,
    /// True if the window can be resized
    resizable: bool,
    /// Drawing mode
    mode: Cell<Mode>,
    /// The input scheme
    file: File,
    /// Window data
    data: &'static mut [Color],
}

impl Renderer for Window {
    /// Get width
    fn width(&self) -> u32 {
        self.w
    }

    /// Get height
    fn height(&self) -> u32 {
        self.h
    }

    /// Access pixel buffer
    fn data(&self) -> &[Color] {
        &self.data
    }

    /// Access pixel buffer mutably
    fn data_mut(&mut self) -> &mut [Color] {
        &mut self.data
    }

    /// Flip the buffer
    fn sync(&mut self) -> bool {
        self.file.sync_data().is_ok()
    }

    /// Set/get mode
    fn mode(&self) -> &Cell<Mode> {
        &self.mode
    }
}

impl Window {
    /// Create a new window
    pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Option<Self> {
        Window::new_flags(x, y, w, h, title, &[])
    }

    /// Create a new window with flags
    pub fn new_flags(x: i32, y: i32, w: u32, h: u32, title: &str, flags: &[WindowFlag]) -> Option<Self> {
        let mut flag_str = String::new();

        let mut async = false;
        let mut resizable = false;
        for &flag in flags.iter() {
            match flag {
                WindowFlag::Async => {
                    async = true;
                    flag_str.push('a');
                },
                WindowFlag::Back => flag_str.push('b'),
                WindowFlag::Front => flag_str.push('f'),
                WindowFlag::Borderless => flag_str.push('l'),
                WindowFlag::Resizable => {
                    resizable = true;
                    flag_str.push('r');
                },
                WindowFlag::Unclosable => flag_str.push('u'),
            }
        }

        if let Ok(file) = File::open(&format!(
            "orbital:{}/{}/{}/{}/{}/{}",
            flag_str, x, y, w, h, title
        )) {
            if let Ok(address) = unsafe { syscall::fmap(file.as_raw_fd(), 0, (w * h * 4) as usize) } {
                Some(Window {
                    x: x,
                    y: y,
                    w: w,
                    h: h,
                    t: title.to_string(),
                    async: async,
                    resizable: resizable,
                    mode: Cell::new(Mode::Blend),
                    file: file,
                    data: unsafe { slice::from_raw_parts_mut(address as *mut Color, (w * h) as usize) },
                })
            } else {
                None
            }
        } else {
            None
        }
    }

    // TODO: Replace with smarter mechanism, maybe a move event?
    pub fn sync_path(&mut self) {
        let mut buf: [u8; 4096] = [0; 4096];
        if let Ok(count) = syscall::fpath(self.file.as_raw_fd() as usize, &mut buf) {
            let path = unsafe { String::from_utf8_unchecked(Vec::from(&buf[..count])) };
            // orbital:/x/y/w/h/t
            let mut parts = path.split('/').skip(1);
            if let Some(x) = parts.next() {
                self.x = x.parse::<i32>().unwrap_or(0);
            }
            if let Some(y) = parts.next() {
                self.y = y.parse::<i32>().unwrap_or(0);
            }
            if let Some(w) = parts.next() {
                self.w = w.parse::<u32>().unwrap_or(0);
            }
            if let Some(h) = parts.next() {
                self.h = h.parse::<u32>().unwrap_or(0);
            }
        }
    }

    /// Get x
    // TODO: Sync with window movements
    pub fn x(&self) -> i32 {
        self.x
    }

    /// Get y
    // TODO: Sync with window movements
    pub fn y(&self) -> i32 {
        self.y
    }

    /// Get title
    pub fn title(&self) -> String {
        self.t.clone()
    }

    // Set position
    pub fn set_pos(&mut self, x: i32, y: i32) {
        let _ = self.file.write(&format!("P,{},{}", x, y).as_bytes());
        self.sync_path();
    }

    // Set size
    pub fn set_size(&mut self, width: u32, height: u32) {
        //TODO: Improve safety and reliability
        unsafe {
            syscall::funmap(self.data.as_ptr() as usize).expect("orbclient: failed to unmap memory in resize");
        }
        let _ = self.file.write(&format!("S,{},{}", width, height).as_bytes());
        self.sync_path();
        unsafe {
            let address = syscall::fmap(self.file.as_raw_fd(), 0, (self.w * self.h * 4) as usize).expect("orbclient: failed to map memory in resize");
            self.data = slice::from_raw_parts_mut(address as *mut Color, (self.w * self.h) as usize);
        }
    }

    /// Set title
    pub fn set_title(&mut self, title: &str) {
        let _ = self.file.write(&format!("T,{}", title).as_bytes());
        self.sync_path();
    }

    /// Blocking iterator over events
    pub fn events(&mut self) -> EventIter {
        let mut iter = EventIter {
            extra: None,
            events: [Event::new(); 16],
            i: 0,
            count: 0
        };

        'blocking: loop {
            if iter.count == iter.events.len() {
                if iter.extra.is_none() {
                    iter.extra = Some(Vec::with_capacity(32));
                }
                iter.extra.as_mut().unwrap().extend_from_slice(&iter.events);
                iter.count = 0;
            }
            let bytes = unsafe { slice::from_raw_parts_mut(
                iter.events[iter.count..].as_mut_ptr() as *mut u8,
                iter.events[iter.count..].len() * mem::size_of::<Event>()
            ) };
            match self.file.read(bytes) {
                Ok(0) => if !self.async
                        && iter.extra.is_none()
                        && iter.count == 0 {
                    thread::yield_now();
                } else {
                    break 'blocking;
                },
                Ok(count) => {
                    let count = count / mem::size_of::<Event>();
                    let events = &iter.events[iter.count..][..count];
                    iter.count += count;

                    if self.resizable {
                        let mut resize = None;
                        for event in events {
                            let event = *event;
                            if event.code == EVENT_RESIZE {
                                resize = Some((event.a as u32, event.b as u32));
                            }
                        }
                        if let Some((w, h)) = resize {
                            self.set_size(w, h);
                        }
                    }
                    if !self.async {
                        // Synchronous windows are blocking, can't attempt another read
                        break 'blocking;
                    }
                },
                Err(_) => break 'blocking,
            }
        }

        iter
    }
}

impl Drop for Window {
    fn drop(&mut self) {
        let _ = unsafe { syscall::funmap(self.data.as_ptr() as usize) };
    }
}

impl AsRawFd for Window {
    fn as_raw_fd(&self) -> RawFd {
        self.file.as_raw_fd()
    }
}

/// Event iterator
pub struct EventIter {
    extra: Option<Vec<Event>>,
    events: [Event; 16],
    i: usize,
    count: usize
}

impl Iterator for EventIter {
    type Item = Event;
    fn next(&mut self) -> Option<Event> {
        let mut i = self.i;
        if let Some(ref mut extra) = self.extra {
            if i < extra.len() {
                self.i += 1;
                return Some(extra[i]);
            }
            i -= extra.len();
        }
        if i < self.count {
            self.i += 1;
            return Some(self.events[i]);
        }
        None
    }
}
<|endoftext|>


<commit_before>#![doc = r#"The teacher. Queries an SMT-solver to check candidates.

# TO DO

- clean `teach` function, it's a mess and the way it's currently written
  doesn't make sense

"#]

use rsmt2::{ ParseSmt2, Kid } ;

use nom::IResult ;

use common::* ;
use common::data::* ;
use common::msg::* ;
use instance::{ Instance, Val } ;

/// Starts the teaching process.
pub fn start_class(
  instance: & Arc<Instance>, profiler: & Profile
) -> Res< Option<Candidates> > {
  use rsmt2::solver ;
  let instance = instance.clone() ;
  log_debug!{ "starting the learning process\n  launching solver kid..." }
  let mut kid = Kid::mk( conf.solver_conf() ).chain_err(
    || ErrorKind::Z3SpawnError
  ) ? ;
  let res = {
    let solver = solver(& mut kid, Parser).chain_err(
      || "while constructing the teacher's solver"
    ) ? ;
    if let Some(log) = conf.smt_log_file("teacher") ? {
      teach( instance, solver.tee(log), profiler )
    } else {
      teach( instance, solver, profiler )
    }
  } ;

  kid.kill().chain_err(
    || "while killing solver kid"
  ) ? ;
  res
}

/// Teaching to the learners.
fn teach< 'kid, S: Solver<'kid, Parser> >(
  instance: Arc<Instance>, solver: S, profiler: & Profile
) -> Res< Option<Candidates> > {
  log_debug!{ "  creating teacher" }
  let mut teacher = Teacher::mk(solver, instance, profiler) ;

  // if conf.smt_learn {
  //   log_debug!{ "  spawning smt learner..." }
  //   teacher.add_learner( ::learning::smt::Launcher ) ?
  // }
  log_debug!{ "  spawning ice learner..." }
  teacher.add_learner( ::learning::ice::Launcher ) ? ;

  log_debug!{ "  performing initial check..." }
  let cexs = teacher.initial_check() ? ;
  log_debug!{ "  generating data from initial cex..." }
  teacher.instance.cexs_to_data(& mut teacher.data, cexs ) ? ;

  log_debug!{ "  starting teaching loop" }
  'teach: loop {
    log_info!{
      "all learning data:\n{}", teacher.data.string_do(
        & (), |s| s.to_string()
      ) ?
    }

    if conf.step {
      let mut dummy = String::new() ;
      println!("") ;
      println!( "; {} to broadcast data...", conf.emph("press return") ) ;
      let _ = ::std::io::stdin().read_line(& mut dummy) ;
    }

    let one_alive = teacher.broadcast() ;
    if ! one_alive {
      bail!("all learners are dead")
    }

    match teacher.get_candidates() ? {

      // Unsat result, done.
      Some( (_idx, None) ) => {
        log_info!(
          "\ngot unsat result from {} learner", teacher.learners[_idx].1
        ) ;
        teacher.finalize() ? ;
        return Ok(None)
      },

      // Got a candidate.
      Some( ( _idx, Some(candidates) ) ) => {
        if_verb!{
          log_info!(
            "\nCurrent candidates from {} learner:",
            conf.emph( & teacher.learners[_idx].1 )
          ) ;
          for _pred in teacher.instance.preds() {
            log_info!("{}:", _pred.name) ;
            log_info!("  {}", candidates[_pred.idx])
          }
        }
        profile!{ teacher tick "cexs" }
        let cexs = teacher.get_cexs(& candidates) ? ;
        profile!{ teacher mark "cexs" }

        if cexs.is_empty() {
          teacher.finalize() ? ;
          return Ok( Some(candidates) )
        }

        log_info!{
          "\nlearning data before adding cex:\n{}",
          teacher.data.string_do(
            & (), |s| s.to_string()
          ) ?
        }
        profile!{ teacher tick "data", "registration" }
        teacher.instance.cexs_to_data(& mut teacher.data, cexs) ? ;
        profile!{ teacher mark "data", "registration" }
        log_info!{
          "\nlearning data before propagation:\n{}",
          teacher.data.string_do(
            & (), |s| s.to_string()
          ) ?
        }
        profile!{ teacher tick "data", "propagation" }
        teacher.data.propagate() ? ;
        profile!{ teacher mark "data", "propagation" }
      },

      // Channel is dead.
      None => bail!("all learners are dead"),
    }
  }
}

/// Alias type for a counterexample for a clause.
pub type Cex = VarMap<Val> ;
/// Alias type for a counterexample for a sequence of clauses.
pub type Cexs = ClsHMap<Cex> ;

/// The teacher, stores a solver.
pub struct Teacher<'a, S> {
  /// The solver.
  pub solver: S,
  /// The (shared) instance.
  pub instance: Arc<Instance>,
  /// Learning data.
  pub data: Data,
  /// Receiver.
  pub from_learners: Receiver<(LrnIdx, FromLearners)>,
  /// Sender used by learners. Becomes `None` when the learning process starts.
  pub to_teacher: Option< Sender<(LrnIdx, FromLearners)> >,
  /// Learners sender and description.
  pub learners: LrnMap<( Option< Sender<Data> >, String )>,
  /// Profiler.
  pub _profiler: & 'a Profile,
  /// Number of guesses.
  count: usize,
}
impl<'a, 'kid, S: Solver<'kid, Parser>> Teacher<'a, S> {
  /// Constructor.
  pub fn mk(
    solver: S, instance: Arc<Instance>, profiler: & 'a Profile
  ) -> Self {
    let learners = LrnMap::with_capacity( 2 ) ;
    let (to_teacher, from_learners) = from_learners() ;
    let data = Data::mk( instance.clone() ) ;
    Teacher {
      solver, instance, data, from_learners,
      to_teacher: Some(to_teacher), learners,
      _profiler: profiler, count: 0,
    }
  }

  /// Finalizes the run, does nothing in bench mode.
  #[cfg( not(feature = "bench") )]
  pub fn finalize(mut self) -> Res<()> {
    if conf.stats {
      println!("; Done in {} guesses", self.count) ;
      println!("") ;
    }
    for & mut (ref mut sender, _) in self.learners.iter_mut() {
      * sender = None
    }
    while self.get_candidates()?.is_some() {}

    Ok(())
  }
  /// Finalizes the run, does nothing in bench mode.
  #[cfg(feature = "bench")]
  #[inline(always)]
  pub fn finalize(self) -> Res<()> { Ok(()) }

  /// Adds a new learner.
  pub fn add_learner<L: Learner + 'static>(& mut self, learner: L) -> Res<()> {
    if let Some(to_teacher) = self.to_teacher.clone() {
      let index = self.learners.next_index() ;
      let name = learner.description() ;
      let instance = self.instance.clone() ;
      let data = self.data.clone() ;
      let (to_learner, learner_recv) = new_to_learner() ;
      ::std::thread::Builder::new().name( name.clone() ).spawn(
        move || learner.run(
          LearnerCore::mk(index, to_teacher.clone(), learner_recv),
          instance, data
        )
      ).chain_err(
        || format!("while spawning learner `{}`", conf.emph(& name))
      ) ? ;
      self.learners.push( ( Some(to_learner), name ) ) ;
      Ok(())
    } else {
      bail!("trying to add learner after teacher's finalization")
    }
  }

  /// Broadcasts data to the learners. Returns `true` if there's no more
  /// learner left.
  pub fn broadcast(& self) -> bool {
    profile!{ self tick "broadcast" }
    let mut one_alive = false ;
    for & (ref sender, ref name) in self.learners.iter() {
      if let Some(sender) = sender.as_ref() {
        if let Err(_) = sender.send( self.data.clone() ) {
          warn!(
            "learner `{}` is dead...", name
          )
        } else {
          one_alive = true
        }
      }
    }
    profile!{ self mark "broadcast" }
    one_alive
  }

  /// Waits for some candidates.
  ///
  /// Returns `None` when there are no more kids. Otherwise, the second
  /// element of the pair is `None` if a learner concluded `unsat`, and
  /// `Some` of the candidates otherwise.
  pub fn get_candidates(
    & self
  ) -> Res< Option<(LrnIdx, Option<Candidates>)> > {
    profile!{ self tick "waiting" }
    'recv: loop {
      match self.from_learners.recv() {
        Ok( (_idx, FromLearners::Msg(_s)) ) => if_verb!{
          for _line in _s.lines() {
            log_info!("{} > {}", conf.emph( & self.learners[_idx].1 ), _line)
          }
        },
        Ok( (idx, FromLearners::Err(e)) ) => {
          let err: Res<()> = Err(e) ;
          let err: Res<()> = err.chain_err(
            || format!(
              "from {} learner", conf.emph( & self.learners[idx].1 )
            )
          ) ;
          // println!("receiving:") ;
          // for err in e.iter() {
          //   println!("{}", err)
          // }
          print_err( err.unwrap_err() )
        },
        Ok( (idx, FromLearners::Stats(tree, stats)) ) => if conf.stats {
          println!(
            "; received stats from {}", conf.emph( & self.learners[idx].1 )
          ) ;
          tree.print() ;
          if ! stats.is_empty() {
            println!("; stats:") ;
            stats.print()
          }
          println!("")
        },
        Ok( (idx, FromLearners::Cands(cands)) ) => {
          profile!{ self mark "waiting" }
          profile!{ self "candidates" => add 1 }
          return Ok( Some( (idx, Some(cands)) ) )
        },
        Ok( (idx, FromLearners::Unsat) ) => {
          return Ok( Some( (idx, None) ) )
        },
        Err(_) => {
          profile!{ self mark "waiting" }
          return Ok( None )
        },
      }
    }
  }

  /// Initial check, where all candidates are `true`.
  ///
  /// Drops the copy of the `Sender` end of the channel used to communicate
  /// with the teacher (`self.to_teacher`). This entails that attempting to
  /// receive messages will automatically fail if all learners are dead.
  pub fn initial_check(& mut self) -> Res< Cexs > {
    // Drop `to_teacher` sender so that we know when all kids are dead.
    self.to_teacher = None ;

    let mut cands = PrdMap::with_capacity( self.instance.preds().len() ) ;
    for _ in 0..self.instance.preds().len() {
      cands.push( self.instance.bool(true) )
    }
    self.get_cexs(& cands)
  }

  /// Looks for falsifiable clauses given some candidates.
  pub fn get_cexs(& mut self, cands: & Candidates) -> Res< Cexs > {
    self.count += 1 ;
    let mut map = ClsHMap::with_capacity( self.instance.clauses().len() ) ;
    let clauses = ClsRange::zero_to( self.instance.clauses().len() ) ;
    self.solver.comment("looking for counterexamples...") ? ;
    for clause in clauses {
      log_debug!{ "  looking for a cex for clause {}", clause }
      if let Some(cex) = self.get_cex(cands, clause).chain_err(
        || format!("while getting counterexample for clause {}", clause)
      ) ? {
        let prev = map.insert(clause, cex) ;
        debug_assert_eq!(prev, None)
      }
    }
    Ok(map)
  }

  /// Checks if a clause is falsifiable and returns a model if it is.
  pub fn get_cex(
    & mut self, cands: & Candidates, clause_idx: ClsIdx
  ) -> SmtRes< Option<Cex> > {
    self.solver.push(1) ? ;
    let clause = & self.instance[clause_idx] ;
    if_not_bench!{
      if conf.smt_log {
        for lhs in clause.lhs() {
          self.solver.comment(
            & format!("{}\n", lhs)
          ) ?
        }
        self.solver.comment(
          & format!("=> {}", clause.rhs())
        ) ?
      }
    }
    profile!{ self tick "cexs", "prep" }
    for var in clause.vars() {
      self.solver.declare_const(var, & var.typ, & ()) ?
    }
    self.solver.assert( clause, cands ) ? ;
    profile!{ self mark "cexs", "prep" }
    profile!{ self tick "cexs", "check-sat" }
    let sat = self.solver.check_sat() ? ;
    profile!{ self mark "cexs", "check-sat" }
    let res = if sat {
      profile!{ self tick "cexs", "model" }
      log_debug!{ "    sat, getting model..." }
      let model = self.solver.get_model() ? ;
      let mut map: VarMap<_> = clause.vars().iter().map(
        |info| info.typ.default_val()
      ).collect() ;
      for (var,val) in model {
        log_debug!{ "    - v_{} = {}", var, val }
        map[var] = val
      }
      log_debug!{ "    done constructing model" }
      profile!{ self mark "cexs", "model" }
      Ok( Some( map ) )
    } else {
      Ok(None)
    } ;
    self.solver.pop(1) ? ;
    res
  }
}

#[doc = r#"Unit type parsing the output of the SMT solver.

Parses variables of the form `v<int>` and constants. It is designed to parse
models of the falsification of a single clause, where the variables of the
clause are written as `v<index>` in smt2.
"#]
pub struct Parser ;
impl ParseSmt2 for Parser {
  type Ident = VarIdx ;
  type Value = Val ;
  type Expr = () ;
  type Proof = () ;
  type I = () ;

  fn parse_ident<'a>(
    & self, bytes: & 'a [u8]
  ) -> IResult<& 'a [u8], VarIdx> {
    use std::str::FromStr ;
    preceded!(
      bytes,
      char!('v'),
      map!(
        map_res!(
          map_res!(
            re_bytes_find!("^[0-9][0-9]*"),
            ::std::str::from_utf8
          ),
          usize::from_str
        ),
        |i| i.into()
      )
    )
  }

  fn parse_value<'a>(
    & self, bytes: & 'a [u8]
  ) -> IResult<& 'a [u8], Val> {
    fix_error!( bytes, u32, call!(Val::parse) )
  }

  fn parse_expr<'a>(
    & self, _: & 'a [u8], _: & ()
  ) -> IResult<& 'a [u8], ()> {
    panic!("[bug] `parse_expr` of the teacher parser should never be called")
  }

  fn parse_proof<'a>(
    & self, _: & 'a [u8]
  ) -> IResult<& 'a [u8], ()> {
    panic!("[bug] `parse_proof` of the teacher parser should never be called")
  }
}

<commit_msg>Fixed problem when reporting unsat<commit_after>#![doc = r#"The teacher. Queries an SMT-solver to check candidates.

# TO DO

- clean `teach` function, it's a mess and the way it's currently written
  doesn't make sense

"#]

use rsmt2::{ ParseSmt2, Kid } ;

use nom::IResult ;

use common::* ;
use common::data::* ;
use common::msg::* ;
use instance::{ Instance, Val } ;

/// Starts the teaching process.
pub fn start_class(
  instance: & Arc<Instance>, profiler: & Profile
) -> Res< Option<Candidates> > {
  use rsmt2::solver ;
  let instance = instance.clone() ;
  log_debug!{ "starting the learning process\n  launching solver kid..." }
  let mut kid = Kid::mk( conf.solver_conf() ).chain_err(
    || ErrorKind::Z3SpawnError
  ) ? ;
  let res = {
    let solver = solver(& mut kid, Parser).chain_err(
      || "while constructing the teacher's solver"
    ) ? ;
    if let Some(log) = conf.smt_log_file("teacher") ? {
      teach( instance, solver.tee(log), profiler )
    } else {
      teach( instance, solver, profiler )
    }
  } ;

  kid.kill().chain_err(
    || "while killing solver kid"
  ) ? ;
  res
}

/// Teaching to the learners.
fn teach< 'kid, S: Solver<'kid, Parser> >(
  instance: Arc<Instance>, solver: S, profiler: & Profile
) -> Res< Option<Candidates> > {
  log_debug!{ "  creating teacher" }
  let mut teacher = Teacher::mk(solver, instance, profiler) ;

  // if conf.smt_learn {
  //   log_debug!{ "  spawning smt learner..." }
  //   teacher.add_learner( ::learning::smt::Launcher ) ?
  // }
  log_debug!{ "  spawning ice learner..." }
  teacher.add_learner( ::learning::ice::Launcher ) ? ;

  log_debug!{ "  performing initial check..." }
  let cexs = teacher.initial_check() ? ;
  log_debug!{ "  generating data from initial cex..." }
  teacher.instance.cexs_to_data(& mut teacher.data, cexs ) ? ;

  log_debug!{ "  starting teaching loop" }
  'teach: loop {
    // log_info!{
    //   "all learning data:\n{}", teacher.data.string_do(
    //     & (), |s| s.to_string()
    //   ) ?
    // }

    if conf.step {
      let mut dummy = String::new() ;
      println!("") ;
      println!( "; {} to broadcast data...", conf.emph("press return") ) ;
      let _ = ::std::io::stdin().read_line(& mut dummy) ;
    }

    let one_alive = teacher.broadcast() ;
    if ! one_alive {
      bail!("all learners are dead")
    }

    match teacher.get_candidates() ? {

      // Unsat result, done.
      Some( (_idx, None) ) => {
        log_info!(
          "\ngot unsat result from {} learner", teacher.learners[_idx].1
        ) ;
        teacher.finalize() ? ;
        return Ok(None)
      },

      // Got a candidate.
      Some( ( _idx, Some(candidates) ) ) => {
        if_verb!{
          log_info!(
            "\nCurrent candidates from {} learner:",
            conf.emph( & teacher.learners[_idx].1 )
          ) ;
          for _pred in teacher.instance.preds() {
            log_info!("{}:", _pred.name) ;
            log_info!("  {}", candidates[_pred.idx])
          }
        }
        profile!{ teacher tick "cexs" }
        let cexs = teacher.get_cexs(& candidates) ? ;
        profile!{ teacher mark "cexs" }

        if cexs.is_empty() {
          teacher.finalize() ? ;
          return Ok( Some(candidates) )
        }

        // log_info!{
        //   "\nlearning data before adding cex:\n{}",
        //   teacher.data.string_do(
        //     & (), |s| s.to_string()
        //   ) ?
        // }
        profile!{ teacher tick "data", "registration" }
        if let Err(e) = teacher.instance.cexs_to_data(
          & mut teacher.data, cexs
        ) {
          match e.kind() {
            & ErrorKind::Unsat => {
              teacher.finalize() ? ;
              return Ok(None)
            },
            _ => bail!(e),
          }
        }
        profile!{ teacher mark "data", "registration" }
        // log_info!{
        //   "\nlearning data before propagation:\n{}",
        //   teacher.data.string_do(
        //     & (), |s| s.to_string()
        //   ) ?
        // }
        profile!{ teacher tick "data", "propagation" }
        teacher.data.propagate() ? ;
        profile!{ teacher mark "data", "propagation" }
      },

      // Channel is dead.
      None => bail!("all learners are dead"),
    }
  }
}

/// Alias type for a counterexample for a clause.
pub type Cex = VarMap<Val> ;
/// Alias type for a counterexample for a sequence of clauses.
pub type Cexs = ClsHMap<Cex> ;

/// The teacher, stores a solver.
pub struct Teacher<'a, S> {
  /// The solver.
  pub solver: S,
  /// The (shared) instance.
  pub instance: Arc<Instance>,
  /// Learning data.
  pub data: Data,
  /// Receiver.
  pub from_learners: Receiver<(LrnIdx, FromLearners)>,
  /// Sender used by learners. Becomes `None` when the learning process starts.
  pub to_teacher: Option< Sender<(LrnIdx, FromLearners)> >,
  /// Learners sender and description.
  pub learners: LrnMap<( Option< Sender<Data> >, String )>,
  /// Profiler.
  pub _profiler: & 'a Profile,
  /// Number of guesses.
  count: usize,
}
impl<'a, 'kid, S: Solver<'kid, Parser>> Teacher<'a, S> {
  /// Constructor.
  pub fn mk(
    solver: S, instance: Arc<Instance>, profiler: & 'a Profile
  ) -> Self {
    let learners = LrnMap::with_capacity( 2 ) ;
    let (to_teacher, from_learners) = from_learners() ;
    let data = Data::mk( instance.clone() ) ;
    Teacher {
      solver, instance, data, from_learners,
      to_teacher: Some(to_teacher), learners,
      _profiler: profiler, count: 0,
    }
  }

  /// Finalizes the run, does nothing in bench mode.
  #[cfg( not(feature = "bench") )]
  pub fn finalize(mut self) -> Res<()> {
    if conf.stats {
      println!("; Done in {} guesses", self.count) ;
      println!("") ;
    }
    for & mut (ref mut sender, _) in self.learners.iter_mut() {
      * sender = None
    }
    while self.get_candidates()?.is_some() {}

    Ok(())
  }
  /// Finalizes the run, does nothing in bench mode.
  #[cfg(feature = "bench")]
  #[inline(always)]
  pub fn finalize(self) -> Res<()> { Ok(()) }

  /// Adds a new learner.
  pub fn add_learner<L: Learner + 'static>(& mut self, learner: L) -> Res<()> {
    if let Some(to_teacher) = self.to_teacher.clone() {
      let index = self.learners.next_index() ;
      let name = learner.description() ;
      let instance = self.instance.clone() ;
      let data = self.data.clone() ;
      let (to_learner, learner_recv) = new_to_learner() ;
      ::std::thread::Builder::new().name( name.clone() ).spawn(
        move || learner.run(
          LearnerCore::mk(index, to_teacher.clone(), learner_recv),
          instance, data
        )
      ).chain_err(
        || format!("while spawning learner `{}`", conf.emph(& name))
      ) ? ;
      self.learners.push( ( Some(to_learner), name ) ) ;
      Ok(())
    } else {
      bail!("trying to add learner after teacher's finalization")
    }
  }

  /// Broadcasts data to the learners. Returns `true` if there's no more
  /// learner left.
  pub fn broadcast(& self) -> bool {
    profile!{ self tick "broadcast" }
    let mut one_alive = false ;
    for & (ref sender, ref name) in self.learners.iter() {
      if let Some(sender) = sender.as_ref() {
        if let Err(_) = sender.send( self.data.clone() ) {
          warn!(
            "learner `{}` is dead...", name
          )
        } else {
          one_alive = true
        }
      }
    }
    profile!{ self mark "broadcast" }
    one_alive
  }

  /// Waits for some candidates.
  ///
  /// Returns `None` when there are no more kids. Otherwise, the second
  /// element of the pair is `None` if a learner concluded `unsat`, and
  /// `Some` of the candidates otherwise.
  pub fn get_candidates(
    & self
  ) -> Res< Option<(LrnIdx, Option<Candidates>)> > {
    profile!{ self tick "waiting" }
    'recv: loop {
      match self.from_learners.recv() {
        Ok( (_idx, FromLearners::Msg(_s)) ) => if_verb!{
          for _line in _s.lines() {
            log_info!("{} > {}", conf.emph( & self.learners[_idx].1 ), _line)
          }
        },
        Ok( (idx, FromLearners::Err(e)) ) => {
          let err: Res<()> = Err(e) ;
          let err: Res<()> = err.chain_err(
            || format!(
              "from {} learner", conf.emph( & self.learners[idx].1 )
            )
          ) ;
          // println!("receiving:") ;
          // for err in e.iter() {
          //   println!("{}", err)
          // }
          print_err( err.unwrap_err() )
        },
        Ok( (idx, FromLearners::Stats(tree, stats)) ) => if conf.stats {
          println!(
            "; received stats from {}", conf.emph( & self.learners[idx].1 )
          ) ;
          tree.print() ;
          if ! stats.is_empty() {
            println!("; stats:") ;
            stats.print()
          }
          println!("")
        },
        Ok( (idx, FromLearners::Cands(cands)) ) => {
          profile!{ self mark "waiting" }
          profile!{ self "candidates" => add 1 }
          return Ok( Some( (idx, Some(cands)) ) )
        },
        Ok( (idx, FromLearners::Unsat) ) => {
          return Ok( Some( (idx, None) ) )
        },
        Err(_) => {
          profile!{ self mark "waiting" }
          return Ok( None )
        },
      }
    }
  }

  /// Initial check, where all candidates are `true`.
  ///
  /// Drops the copy of the `Sender` end of the channel used to communicate
  /// with the teacher (`self.to_teacher`). This entails that attempting to
  /// receive messages will automatically fail if all learners are dead.
  pub fn initial_check(& mut self) -> Res< Cexs > {
    // Drop `to_teacher` sender so that we know when all kids are dead.
    self.to_teacher = None ;

    let mut cands = PrdMap::with_capacity( self.instance.preds().len() ) ;
    for _ in 0..self.instance.preds().len() {
      cands.push( self.instance.bool(true) )
    }
    self.get_cexs(& cands)
  }

  /// Looks for falsifiable clauses given some candidates.
  pub fn get_cexs(& mut self, cands: & Candidates) -> Res< Cexs > {
    self.count += 1 ;
    let mut map = ClsHMap::with_capacity( self.instance.clauses().len() ) ;
    let clauses = ClsRange::zero_to( self.instance.clauses().len() ) ;
    self.solver.comment("looking for counterexamples...") ? ;
    for clause in clauses {
      log_debug!{ "  looking for a cex for clause {}", clause }
      if let Some(cex) = self.get_cex(cands, clause).chain_err(
        || format!("while getting counterexample for clause {}", clause)
      ) ? {
        let prev = map.insert(clause, cex) ;
        debug_assert_eq!(prev, None)
      }
    }
    Ok(map)
  }

  /// Checks if a clause is falsifiable and returns a model if it is.
  pub fn get_cex(
    & mut self, cands: & Candidates, clause_idx: ClsIdx
  ) -> SmtRes< Option<Cex> > {
    self.solver.push(1) ? ;
    let clause = & self.instance[clause_idx] ;
    if_not_bench!{
      if conf.smt_log {
        for lhs in clause.lhs() {
          self.solver.comment(
            & format!("{}\n", lhs)
          ) ?
        }
        self.solver.comment(
          & format!("=> {}", clause.rhs())
        ) ?
      }
    }
    profile!{ self tick "cexs", "prep" }
    for var in clause.vars() {
      self.solver.declare_const(var, & var.typ, & ()) ?
    }
    self.solver.assert( clause, cands ) ? ;
    profile!{ self mark "cexs", "prep" }
    profile!{ self tick "cexs", "check-sat" }
    let sat = self.solver.check_sat() ? ;
    profile!{ self mark "cexs", "check-sat" }
    let res = if sat {
      profile!{ self tick "cexs", "model" }
      log_debug!{ "    sat, getting model..." }
      let model = self.solver.get_model() ? ;
      let mut map: VarMap<_> = clause.vars().iter().map(
        |info| info.typ.default_val()
      ).collect() ;
      for (var,val) in model {
        log_debug!{ "    - v_{} = {}", var, val }
        map[var] = val
      }
      log_debug!{ "    done constructing model" }
      profile!{ self mark "cexs", "model" }
      Ok( Some( map ) )
    } else {
      Ok(None)
    } ;
    self.solver.pop(1) ? ;
    res
  }
}

#[doc = r#"Unit type parsing the output of the SMT solver.

Parses variables of the form `v<int>` and constants. It is designed to parse
models of the falsification of a single clause, where the variables of the
clause are written as `v<index>` in smt2.
"#]
pub struct Parser ;
impl ParseSmt2 for Parser {
  type Ident = VarIdx ;
  type Value = Val ;
  type Expr = () ;
  type Proof = () ;
  type I = () ;

  fn parse_ident<'a>(
    & self, bytes: & 'a [u8]
  ) -> IResult<& 'a [u8], VarIdx> {
    use std::str::FromStr ;
    preceded!(
      bytes,
      char!('v'),
      map!(
        map_res!(
          map_res!(
            re_bytes_find!("^[0-9][0-9]*"),
            ::std::str::from_utf8
          ),
          usize::from_str
        ),
        |i| i.into()
      )
    )
  }

  fn parse_value<'a>(
    & self, bytes: & 'a [u8]
  ) -> IResult<& 'a [u8], Val> {
    fix_error!( bytes, u32, call!(Val::parse) )
  }

  fn parse_expr<'a>(
    & self, _: & 'a [u8], _: & ()
  ) -> IResult<& 'a [u8], ()> {
    panic!("[bug] `parse_expr` of the teacher parser should never be called")
  }

  fn parse_proof<'a>(
    & self, _: & 'a [u8]
  ) -> IResult<& 'a [u8], ()> {
    panic!("[bug] `parse_proof` of the teacher parser should never be called")
  }
}

<|endoftext|>