【Rust】挿入ソート

挿入ソートも n * (n – 1) / 2 の計算量になる。
対象データを一時領域に確保しておき、値が大きかどうかを判定して、大きい場合は入れ替えるという作業を繰り返す。
usizeは-にならないため、 j== 0の時は判定しています。

fn main() {
    let mut data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    for i in 1..data.len() {
        let temp = data[i];
        let mut j = i - 1;
  
        while (j >= 0) && (data[j] > temp) {
            data[j + 1] = data[j];
            if j == 0 {
                break;
            }
            j -= 1;
        }
        if j == 0 && (data[j] > temp) {
            data[0] = temp;
        } else {
            data[j + 1] = temp;
        }
        println!("sorted data:{:?}", data);
    }
    println!("sorted data:{:?}", data);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
Running `target/debug/rust`
sorted data:[6, 15, 4, 2, 8, 5, 11, 9, 7, 13]
sorted data:[4, 6, 15, 2, 8, 5, 11, 9, 7, 13]
sorted data:[2, 4, 6, 15, 8, 5, 11, 9, 7, 13]
sorted data:[2, 4, 6, 8, 15, 5, 11, 9, 7, 13]
sorted data:[2, 4, 5, 6, 8, 15, 11, 9, 7, 13]
sorted data:[2, 4, 5, 6, 8, 11, 15, 9, 7, 13]
sorted data:[2, 4, 5, 6, 8, 9, 11, 15, 7, 13]
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 15, 13]
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]

途中経過を見ると美しいが、これも計算量が多くなる模様。

【Rust】選択ソート

1件ずつ調べていく手法です。最小値の抽出は以下の通り。

fn main() {
    let data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    let mut min = 0;
    for i in 0..data.len() {
        if data[min] > data[i] {
            min = i;
        }
    } 
    println!("minimal num:{}", data[min]);
}

### 選択ソート
n * (n – 1) / 2 の計算量が必要になる。もっとも小さいものを左から順番に並べていく。
mem::swap(&mut data[min], &mut data[i]);は使えない。

use std::mem;

fn main() {
    let mut data = [6, 15, 4, 2, 8, 5, 11, 9, 7, 13];

    for i in 0..data.len() {
        let mut min = i;
        let k = i + 1; 
        for j in k..data.len() {
            if data[min] > data[j] {
                min = j;
            }
        }
        // mem::swap(&mut data[min], &mut data[i]);
        data.swap(i, min);
    }   
    println!("sorted data:{:?}", data);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.44s
Running `target/debug/rust`
sorted data:[2, 4, 5, 6, 7, 8, 9, 11, 13, 15]

なるほど、普段ソートとか考えたことなかったが、こうなってるのか。。面白いね。

【Rust】axumでファイルのアップロードを受け取る

HTML側

<form action="/upload" method="post" enctype="multipart/form-data">
    <div>
        <p>name</p>
        <input type="text" name="name"><br><br>
        <input type="file" name="testfile" onchange="previewFile(this);">
    </div>
    <div>
        <input type="submit" value="送信する">
    </div>
</form>
<p>プレビュー</p>
<img id="preview">

<script>
function previewFile(file) {
    var fileData = new FileReader();
    fileData.onload = (function() {
        document.getElementById('preview').setAttribute("style","width:100px;height:100px");
        document.getElementById('preview').src = fileData.result;
        
    });
    fileData.readAsDataURL(file.files[0]);
}
</script>

axum側
axum = { version=”0.8.1″, features = [“multipart”] }
tokio = { version = “1.25”, features = [“full”] }

use tower_http::services::{ServeDir, ServeFile};
use axum::{
    extract::Multipart,
    extract::DefaultBodyLimit,
    routing::post,
    routing::get,
    Router,
};
use tokio::fs::File;
use tokio::io::AsyncWriteExt;


#[tokio::main]
async fn main() {

    let serve_dir = ServeDir::new("static").not_found_service(ServeFile::new("static"));

    let app = Router::new()
        .route("/", get(handle_index))
        .route("/upload", post(handle_upload))
        .layer(DefaultBodyLimit::max(1024 * 1024 * 1024))
        .nest_service("/static", serve_dir.clone())
        .fallback_service(serve_dir);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn handle_upload(mut multipart: Multipart)-> axum::response::Html<String> {

    while let Some(field) = multipart.next_field().await.unwrap(){
        let param_name = field.name().unwrap().to_string();
        match param_name.as_str() {
            "name" => {
                let name = field.text().await.unwrap();
                println!("tags: {}", name);
            }
            "testfile" => {
                let file_name = match field.file_name() {
                    Some(name) => name.to_owned(),
                    None => panic!("file_name is None"),
                };
                match field.bytes().await {
                    Ok(data) => {
                        println!("Length of `{}` is {} bytes", param_name, data.len());
                        let mut file = File::create(format!("./tmp/{}", file_name)).await.unwrap();
                        file.write_all(&data).await.unwrap();
                    }
                    Err(e) => {
                        eprintln!("Error reading `{}`: {}", param_name, e);
                        // return;
                    }
                }

            }
            _ => {
                println!("unknown param_name: {}", param_name);
            }
        }
    }

    let tera = tera::Tera::new("templates/*").unwrap();

    let mut context = tera::Context::new();
    context.insert("title", "Index page");

    let output = tera.render("test.html", &context);
    axum::response::Html(output.unwrap())
}

なるほど、なかなか面白い。

【Rust】S3のbucketへ画像アップロード

use dotenv::dotenv;
use std::env;
use tokio::io::AsyncReadExt;
use rusoto_s3::*;
use rusoto_core::*;

#[tokio::main]
async fn main() {
    dotenv();
    let aws_access_key = env::var("AWS_ACCESS_KEY_ID").unwrap();
    let aws_secret_key = env::var("AWS_SECRET_ACCESS_KEY").unwrap();

    std::env::set_var("AWS_ACCESS_KEY_ID", aws_access_key);
    std::env::set_var("AWS_SECRET_ACCESS_KEY", aws_secret_key);

    s3_upload().await;
}

async fn s3_upload() -> Result<(), Box<dyn std::error::Error>> {
    let s3_client = S3Client::new("ap-northeast-1".parse().unwrap());
    let mut file = tokio::fs::File::open("./data/test.png").await?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer).await?;

    let result = s3_client.put_object(PutObjectRequest {
        bucket: String::from("hpscript"),
        key: "test.png".to_string(),
        body: Some(StreamingBody::from(buffer)),
        ..Default::default()
    }).await?;
    // do thing with result
    Ok(())
}

データがうまくアップロードされているのが分かります。なるほどね〜

【Rust】base64で画像を文字列に変換

想像以上に文字列のデータ量が多すぎる…

use std::fs::File;
use std::io::Write;
use std::path::Path;

static FILE_NAME: &'static str = "test";

fn main() {

    let path = "./data/img1.png";
    let base64 = image_base64::to_base64(path);
    println!("{:?}", base64);
    
    let img = image_base64::from_base64(base64);

    let file_type = "png";
    let mut output = File::create(&Path::new(&format!("./data/{}.{}", FILE_NAME, file_type))).unwrap();
    output.write_all(img.as_slice()).unwrap();

}


+mQS0RoL4XWkGRlzADREjATCW58eL2T9dzP754iZx5OBzfBHPz51wNvO7sxlwI3lWnIa/An5fSMLuH42HOzfZiABLKqp+KD7hAPsQB9L4eP1gFPJgwPDTUympfvNM9TWq/Z7iX4pAK4463hDa3MXJ4veh2slWxtqowOOXgnUoyHp4H24dllHc5QPw85K0EzUDsE7MjIY5YWH4WMe2ynw7WP0+Het1k3Xn6ReoDw9mo6EY5sccKOU2GADLZaDiBOZF38EDiSclHU0Imzpp7KCNqs9jUGmtGD3drKFe0HEcAGtKAjBmxnFJHgFg2QqCQFHPz6RzMLwzEMP8kUjwkRhB4EWQ7RGUlUzycFSDHCUF/uqs0bdbrPcs4NlPF3JuH46pOBAFj7DHJ5IAP9+fzYLO9dcreXgl8Kj67drmF5dzfzqfA6D9tiQdNfw7BXHgSJ/7NOjkRxsP53jvT/fcnbr6s6RVmMmzI3Y5ZtNujliCEQVbI5duj16GL36atHJ3yuq96R4Hs7xAFyrdGVi+Jxy/7xdFifhxIHwBpH+5tAmleNhSgpv2n2tbMFwIkiQMfgBUA6fx/gCSy/eG38mPwdf/fSEbb5HPYgdDnX61VJjxK3OHIhfkoKQq4WE2SD1gpdPl3WGoQ4D4/bAwvuIAoFdGa87SA/CycQ0D4GBn43A3U2UlBf4NaK7emUxiqOu2HaFlpdlVUH9eUpocIyphAiT5DQbAchnY88L7xj5pGbcO8jgyGNNNDO3qtoMewrODJp9gXQiBG2UDAXjeSALA1ValS7tlF4CpSAlSHMDwq2tbgHPhnu5GEhjDZXuKBFfQwa3hIPCjiFAStkVbKSsB2ou2bBDd/1j0bA/QDpCDWisQF/ofJJolHwfsTlud4jfXf9m0ZTNt3ScNmzDC0MpQC8Kkzp3aKyO5eaMHkvMO7dr069XFRK//mMH6zhMsF7par1s0JdprxvbY5WhtAnQfFCYA+4HKeJ0AaeTW+Cf2BH/e/Ai7hGv7I3JDFy12tzbRU4fdtzQ/FL/IUOMBH86d8K+45XeOxIDejOfBhXEzP6ZCHrJeIQCfAQD71gDANg0AYJwW5mrq52goEOKPJEbQ9QCwZrcOrfkOlMrt20SJTmBAS+ALGV2mWDAAbp6BKnSE6LjPVzsFVegB3dvXXcvCt4DQMJ4VrCM+dK54QwB4/kjtGgD481AZB2AKw1xR+ngygOHl5dyznwaGeriNMNMBXDUc4wC6ipgDsfEDR6SewDDRg4+fnUrBCNuEDXPWvmfvaGsO6OrbQ40IohpxVKI+H/gV6Rah2i6hcc+PrL575w56mr3GDTNYOt02eu1MZNXwzEJXGLYYotvbRc/3ir7eI3r0GX477CGKtqzfGb8yaeMcoHjIKpew1W7xPrNBZt6d5nHqI1/sbzC/D0D+6urmH89nodSM2jvS65syf1XUAcD4vf53AIZoGPbRgjd/gmHvmUPrYm+4D9aYat5XUH82f28iqz/LezAAltcIeHGAVqEVqqrQ+B96Ra6DpXWjpKsJDH0aqqaIm2GxYBQ
….

うーん、これはちょっと無理ですね。。

【Rust】○✖️ゲーム(minmax)

use rand::Rng;
use std::cmp::Ordering;

static goal:[i32; 8] = [ 0b111000000, 0b000111000, 0b000000111, 0b100100100,
            0b010010010, 0b001001001, 0b100010001, 0b001010100];

fn check(player: i32) -> bool {
    for mask in goal {
        if player & mask == mask {
            return true
        }
    }
    return false 
}

fn minmax(p1: i32, p2: i32, turn: bool) -> i32 {
    if check(p2) {
        if turn {
            return 1
        } else {
            return -1
        }
    }
    let board: i32 = p1 | p2;
    if board == 0b111111111{
        return 0
    }

    let mut w = Vec::new();
    for i in 0..9 {
        if board & (1 << i) == 0 {
            w.push(i);
        }
    }

    let mut k = Vec::new();
    if turn {
        for i in w {
           k.push(minmax(p2, p1 | (1 << i), !turn))
        }
        return *k.iter().min().unwrap();
    } else {
        for i in w {
            k.push(minmax(p2, p1 | (1 << i), !turn))
        }
        return *k.iter().max().unwrap();
    }
    

}

fn play(p1: i32, p2: i32, turn: bool) {
    if check(p2) {
        println!("{:09b}, {:09b}", p1, p2);
        return
    } 

    let board: i32 = p1 | p2;
    if board == 0b111111111{
        println!("{:09b}, {:09b}", p1, p2);
        return
    }
    let mut w = Vec::new();
    for i in 0..9 {
        if board & (1 << i) == 0 {
            w.push(i);
        }
    }

    let mut r  = Vec::new();
    for i in w.clone() {
        r.push(minmax(p2, p1 | (1 << i), true))
    }
    let n = r.iter()
        .enumerate()
        .max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Equal))
        .map(|(index, _)| index).unwrap();
    let j = w[n];
    play(p2, p1 | (1 << j), !turn);
}

fn main() {
    play(0, 0, true);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.32s
Running `target/debug/rust`
001110010, 110001101

う、なんか違うな…

    let mut rnd = rand::thread_rng();
    let k = rnd.gen_range(1..n);
    let j = w[k];

このようにすると、ランダム性が出る。。

【Rust】○✖️ゲーム

マスを0と1で表現して、2進法の加算で判定する。

fn main() {

    println!("{:09b}", 0b111000000 & 0b000111111);
    println!("{:09b}", 0b111000000 & 0b101100010);
    println!("{:09b}", 0b111000000 & 0b111000010);
}

000000000
101000000
111000000

ちなみに、0b111000000など2進数の型はi32

use rand::Rng;

static goal:[i32; 8] = [ 0b111000000, 0b000111000, 0b000000111, 0b100100100,
            0b010010010, 0b001001001, 0b100010001, 0b001010100];

fn check(player: i32) -> bool {
    for mask in goal {
        if player & mask == mask {
            return true
        }
    }
    return false 
}

fn play(p1: i32, p2: i32) {
    if check(p2) {
        println!("{:09b}, {:09b}", p1, p2);
        return
    } 

    let board: i32 = p1 | p2;
    if board == 0b111111111{
        println!("{:09b}, {:09b}", p1, p2);
        return
    }
    let mut w = Vec::new();
    for i in 0..9 {
        if board & (1 << i) == 0 {
            w.push(i);
        }
    }
    let n = w.len() + 1;
    let mut rnd = rand::thread_rng();
    let r = rnd.gen_range(1..n);
    play(p2, p1 | (1 << r));
}

fn main() {
    play(0, 0);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/rust`
010001000, 001111000

これはさっぱりわからん…

【Rust】指定パスのファイル、ディレクトリを取得(depth & linear)

ファイル、フォルダの取得
fs::read_dir()がファイルの一覧取得

use std::error::Error;
use std::fs;
use std::path;

pub fn read_dir(path: &str) -> Result<Vec<path::PathBuf>, Box<dyn Error>>{
    let dir = fs::read_dir(path)?;
    let mut files: Vec<path::PathBuf> = Vec::new();
    for item in dir.into_iter() {
        files.push(item?.path());
    }
    Ok(files)
}


fn main() {
    let files = read_dir("./");
    println!("{:?}", files);
}

Compiling rust v0.1.0 (/home/vagrant/dev/algorithm/rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.43s
Running `target/debug/rust`
Ok([“./target”, “./.gitignore”, “./Cargo.lock”, “./Cargo.toml”, “./.git”, “./src”])

### 再帰的にディレクトリ内のファイルを取得

use std::error::Error;
use std::fs;
use std::path;

pub fn read_dir(path: &path::PathBuf) {
    let dir = fs::read_dir(path).unwrap();
    let mut files: Vec<path::PathBuf> = Vec::new();
    for item in dir.into_iter() {
        
        let name = item.unwrap().path();
        if name.is_dir() {
            read_dir(&name);
        } else {
            files.push(name);
        }
    }
    println!("{:?}", files);
}


fn main() {
    let path = path::PathBuf::from("./src");
    read_dir(&path);
}

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.20s
     Running `target/debug/rust`
["./src/test/test.rs"]
["./src/binary_search.rs", "./src/linear_search.rs", "./src/fibonacci.rs", "./src/tree_search.rs", "./src/main.rs", "./src/maze.rs", "./src/cardinal.rs", "./src/vending_machine.rs", "./src/fizzbuzz.rs", "./src/8queen.rs", "./src/prime.rs"]

これは感覚的に理解しやすいですね。

### 幅優先探索
再帰で検索するのではなく、ディレクトリをvectorに追加していき、各ディレクトリのファイルをそれぞれ取得していく。答えは同じだが、深さで検索するのではなく、対象を一つ一つ処理していくところが面白い。

fn main() {
    let mut queue: VecDeque<path::PathBuf> = VecDeque::new();
    queue.push_back("./src".into());
    let mut files: Vec<path::PathBuf> = Vec::new();

    while queue.len() > 0 {
        let path = queue.pop_front();
        let dir = fs::read_dir(path.unwrap()).unwrap();
        for item in dir.into_iter() {
            let name = item.unwrap().path();
            if name.is_dir() {
                queue.push_back(name);
            } else {
                files.push(name);
            }
        }
    }
    println!("{:?}", files);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/rust`
["./src/binary_search.rs", "./src/linear_search.rs", "./src/fibonacci.rs", "./src/tree_search.rs", "./src/main.rs", "./src/maze.rs", "./src/cardinal.rs", "./src/vending_machine.rs", "./src/fizzbuzz.rs", "./src/read_dir.rs", "./src/8queen.rs", "./src/prime.rs", "./src/test/test.rs"]

【Rust】ハノイの塔

再起的なので、対象枚数が増えるほど、指数関数的に計算数が増えていく。

use std::env;

fn hanoi(n: i32, src: String, dist: String, via: String) { // n, 移動元、移動先、経由
    if n > 1 {
        hanoi(n - 1, src.clone(), via.clone(), dist.clone());
        println!("{} -> {}", src.clone(), dist.clone());
        hanoi(n - 1, via.clone(), dist.clone(), src.clone());
    } else {
        println!("{} -> {}", src.clone(), dist.clone());
    }

}

fn main() {
    let args: Vec<String> = env::args().collect();
    let n = args[1].parse::<i32>().unwrap();
    println!("入力値:{}", n);

    hanoi(n, "a".to_string(), "b".to_string(), "c".to_string());
}

Compiling rust v0.1.0 (/home/vagrant/dev/algorithm/rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
Running `target/debug/rust 3`
入力値:3
a -> b
a -> c
b -> c
a -> b
c -> a
c -> b
a -> b

### 考察
3 a b c (a->b)
2 a c b (a->c)
1 a b c (a->b)
1 b c a (b->c)
3 a b c (a->b)
2 ….

【Rust】簡易的なBloomfilter

ブルームフィルタを更新していくロジックがいまいちわからんが、なんとなく

use std::hash::{DefaultHasher, Hash, Hasher};

#[derive(Debug, PartialEq, Clone)]
struct BloomFilter {
    filter: [i32; 10],
}

impl BloomFilter {
    fn set_v(&mut self, val: String) {
        let list: Vec<u8> = self.n_hash(val);
        for l in list {
            let i = usize::from(l);
            if self.filter[i] == 0 {
                self.filter[i] = 1
            } else {
                self.filter[i] = 2 }
        }    
    }

    fn n_hash(&self, val: String) -> Vec<u8>{
        let hashed = siphash(val);
        let list: Vec<u8> = s_digit(hashed);
        return list
    }

    fn check_v(self, val: String) -> bool {
        let list: Vec<u8> = self.n_hash(val);
        let mut c_bf = BloomFilter { filter: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]};
        for l in list {
            let i = usize::from(l);
            if c_bf.filter[i] == 0 {
                c_bf.filter[i] = 1
            } else {
                c_bf.filter[i] = 2 }
        }
        return self == c_bf
    }
}

fn siphash(s: String) -> u64 {
    let mut siphash = DefaultHasher::new();
    s.hash(&mut siphash);
    return siphash.finish()
}

fn s_digit(n: u64) -> Vec<u8> {
    n.to_string()
        .chars()
        .into_iter()
        .map(|char| char.to_digit(10).unwrap() as u8)
        .collect()
}

fn main(){
    let mut bf = BloomFilter { filter: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]};
    bf.set_v("hello world".to_string());
    println!("{:?}", bf);
    println!("{}", bf.clone().check_v("hello world!".to_string()));
    println!("{}", bf.clone().check_v("hello world".to_string()));
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.33s
Running `target/debug/wallet`
BloomFilter { filter: [2, 2, 0, 1, 2, 1, 1, 2, 2, 2] }
false
true