【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

【Rust】8クイーン問題

fn check()は逆順にして、左下もしくは右下にあるかチェックしてる?
なんとなくわかるが、完全に理解していないのが辛いところ。。

use std::collections::VecDeque;

static N: usize = 8;

fn check(x: usize, mut col: VecDeque<usize>)-> bool {
    col.make_contiguous().reverse();

    let mut i = 0;
    for row in col {
        if ((x + i + 1) == row) || ((x as i32- i as i32 - 1) == row.try_into().unwrap()) {
            return false
        }
        i = i + 1;
    }
    return true
}

fn search(mut col: VecDeque<usize>){
    if col.clone().len() == N {
        println!("{:?}", &col);
    }

    for i in 0..N {
        if !col.contains(&i) {
            // println!("{:?}", &col);
            // println!("{}", i);
            if check(i, col.clone()) == true {
                col.push_back(i);
                search(col.clone());
                col.pop_front();
            }
        }
    }
}

fn main() {
    let col: VecDeque<usize> = VecDeque::new();
    search(col);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.23s
Running `target/debug/rust`
[4, 1, 3, 5, 7, 2, 0, 6]
[1, 3, 5, 7, 2, 0, 6, 4]
[5, 2, 4, 6, 0, 3, 1, 7]
[2, 4, 6, 0, 3, 1, 7, 5]
[1, 3, 5, 7, 2, 0, 6, 4]

【Rust】迷路で進行方向からの右手手法

これのエラー処理に丸一日かかりました。。

fn main() {

    let mut maze = [
        [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
        [9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 9],
        [9, 0, 9, 0, 0, 0, 9, 9, 0, 9, 9, 9],
        [9, 0, 9, 9, 0, 9, 0, 0, 0, 9, 0, 9],
        [9, 0, 0, 0, 9, 0, 0, 9, 9, 0, 9, 9],
        [9, 9, 9, 0, 0, 9, 0, 9, 0, 0, 0, 9],
        [9, 0, 0, 0, 9, 0, 9, 0, 0, 9, 1, 9],
        [9, 0, 9, 0, 0, 0, 0, 9, 0, 0, 9, 9],
        [9, 0, 0, 9, 0, 9, 0, 0, 9, 0, 0, 9],
        [9, 0, 9, 0, 9, 0, 9, 0, 0, 9, 0, 9],
        [9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 9],
        [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
    ];

    let dir: [[i32; 2]; 4] = [[1, 0], [0, 1], [-1, 0], [0, -1]];

    let mut p:[i32; 4] = [1, 1, 0, 0]; // x, y, depth, d

    'main: while maze[p[0] as usize][p[1] as usize] != 1 {

        maze[p[0] as usize][p[1] as usize] = 2;

        'sub: for i in 0..4 {
            let mut j: i32 = 3;
            if i != 0 {
                j = (p[3] + i - 1) % 4
            } else if (p[3] - 1) < 0{
                j = (p[3] - 1 + 4) % 4
            } else {
                j = (p[3] - 1) % 4
            }
            println!("j: {}", j);
            println!("p0: {}", p[0]);
            println!("p1: {}", p[1]);
            println!("p2: {}", p[2]);
            println!("p3: {}", p[3]);
            if maze[(p[0] + dir[j as usize][0])as usize][(p[1] + dir[j as usize][1]) as usize] < 2 {
                p[0] += dir[j as usize][0]; // x
                p[1] += dir[j as usize][1]; // y
                p[3] = j as i32;   // d
                p[2] += 1;  // depth
                println!("yes: {}", j);
                println!("< 2 p0: {}", p[0]);
                println!("< 2 p1: {}", p[1]);
                println!("< 2 p2: {}", p[2]);
                println!("< 2 p3: {}", p[3]);
                break 'sub;
            } else if maze[(p[0] + dir[j as usize][0]) as usize][(p[1] + dir[j as usize][1]) as usize] == 2 {
                p[0] += dir[j as usize][0];
                p[1] += dir[j as usize][1];
                p[3] = j as i32;
                p[2] -= 1;
                println!("yes: {}", j);
                println!("= 2 p0: {}", p[0]);
                println!("= 2 p1: {}", p[1]);
                println!("= 2 p2: {}", p[2]);
                println!("= 2 p3: {}", p[3]);
                break 'sub;
            }
        }
    }
    println!("最終: {}", p[2]);
}

// 省略
最終: 28

【Rust】迷路のゴールを深さ優先探索(再帰処理)で探す

mutexで見にくいが、やってることは単純で、上下左右ではなく、行けるだけ上、下、左、右に移動して、行けるところがなくなったら、元に戻るというロジック。これも素晴らしいね。

use std::collections::VecDeque;
use std::sync::Mutex;

static maze: Mutex<[[usize; 12]; 12]> = Mutex::new([
    [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
    [9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 9],
    [9, 0, 9, 0, 0, 0, 9, 9, 0, 9, 9, 9],
    [9, 0, 9, 9, 0, 9, 0, 0, 0, 9, 0, 9],
    [9, 0, 0, 0, 9, 0, 0, 9, 9, 0, 9, 9],
    [9, 9, 9, 0, 0, 9, 0, 9, 0, 0, 0, 9],
    [9, 0, 0, 0, 9, 0, 9, 0, 0, 9, 1, 9],
    [9, 0, 9, 0, 0, 0, 0, 9, 0, 0, 9, 9],
    [9, 0, 0, 9, 0, 9, 0, 0, 9, 0, 0, 9],
    [9, 0, 9, 0, 9, 0, 9, 0, 0, 9, 0, 9],
    [9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 9],
    [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
]);

fn search(p:[usize; 3]) {

    if maze.lock().unwrap()[p[0]][p[1]] == 1 {
        println!("ゴール:{}-{}", p[0], p[1]); 
        println!("移動回数:{}", p[2]); // z
        return;
    }
    maze.lock().unwrap()[p[0]][p[1]] = 2;

    if maze.lock().unwrap()[p[0] - 1][p[1]] < 2 {
        search([p[0]- 1, p[1], p[2] + 1]);
    }
    if maze.lock().unwrap()[p[0] + 1][p[1]] < 2 {
        search([p[0]+1, p[1], p[2] + 1]);
    }
    if maze.lock().unwrap()[p[0]][p[1] - 1] < 2 {
        search([p[0], p[1] -1, p[2] + 1]);
    }
    if maze.lock().unwrap()[p[0]][p[1] + 1] < 2 {
        search([p[0], p[1] + 1, p[2] + 1]);
    }
    maze.lock().unwrap()[p[0]][p[1]] = 0;
}

fn main() {
    search([1, 1, 0])
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.41s
Running `target/debug/rust`
ゴール:6-10
移動回数:28

【Rust】迷路のゴールを幅優先探索で探す

壁は9, 既に行ったところは2、行ったことない場所は0、ゴールは1 とする問題設定が素晴らしいね。上下左右のマスをこれから行く場所として追加していくロジックも面白い。

use std::collections::VecDeque;

fn main() {
    let mut maze = [
        [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9],
        [9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 9],
        [9, 0, 9, 0, 0, 0, 9, 9, 0, 9, 9, 9],
        [9, 0, 9, 9, 0, 9, 0, 0, 0, 9, 0, 9],
        [9, 0, 0, 0, 9, 0, 0, 9, 9, 0, 9, 9],
        [9, 9, 9, 0, 0, 9, 0, 9, 0, 0, 0, 9],
        [9, 0, 0, 0, 9, 0, 9, 0, 0, 9, 1, 9],
        [9, 0, 9, 0, 0, 0, 0, 9, 0, 0, 9, 9],
        [9, 0, 0, 9, 0, 9, 0, 0, 9, 0, 0, 9],
        [9, 0, 9, 0, 9, 0, 9, 0, 0, 9, 0, 9],
        [9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 9],
        [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
    ];

    let mut pos: VecDeque<[usize; 3]> = vec![[1, 1, 0]].into();

    while pos.len() > 0 {
        let p = pos.pop_front().unwrap(); // x, y, depth

        if maze[p[0]][p[1]] == 1 {
            println!("ゴール:{}-{}", p[0], p[1]); 
            println!("移動回数:{}", p[2]); // z
            break;
        }
        maze[p[0]][p[1]] = 2;

        if maze[p[0] - 1][p[1]] < 2 {
            pos.push_back([p[0]- 1, p[1], p[2] + 1]);
        }
        if maze[p[0] + 1][p[1]] < 2 {
            pos.push_back([p[0]+1, p[1], p[2] + 1]);
        }
        if maze[p[0]][p[1] - 1] < 2 {
            pos.push_back([p[0], p[1] -1, p[2] + 1]);
        }
        if maze[p[0]][p[1] + 1] < 2 {
            pos.push_back([p[0], p[1] + 1, p[2] + 1]);
        }
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
Running `target/debug/rust`
ゴール:6-10
移動回数:28

【Rust】HD_WALLET

hd-wallet= {version = “0.6.1”, features = [“slip10”, “curve-secp256k1”]}

fn main(){

    let seed = b"16-64 bytes of high entropy".as_slice();
    let master_key = slip10::derive_master_key::<Secp256k1>(seed).unwrap();
    let master_key_pair = hd_wallet::ExtendedKeyPair::from(master_key.clone());

    let child_key_pair = slip10::derive_child_key_pair_with_path(
        &master_key_pair,
        [1 + hd_wallet::H, 10],
    );
    println!("{:?}", master_key.clone());
    println!("{:?}", master_key_pair);
    println!("{:?}", child_key_pair);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.40s
Running `target/debug/wallet`
ExtendedSecretKey { secret_key: SecretScalar, chain_code: [23, 173, 212, 211, 8, 246, 142, 67, 143, 93, 79, 203, 0, 30, 52, 69, 214, 224, 93, 10, 56, 224, 214, 213, 121, 161, 164, 145, 108, 174, 89, 36] }
ExtendedKeyPair { public_key: ExtendedPublicKey { public_key: Point { curve: “secp256k1”, value: “…” }, chain_code: [23, 173, 212, 211, 8, 246, 142, 67, 143, 93, 79, 203, 0, 30, 52, 69, 214, 224, 93, 10, 56, 224, 214, 213, 121, 161, 164, 145, 108, 174, 89, 36] }, secret_key: ExtendedSecretKey { secret_key: SecretScalar, chain_code: [23, 173, 212, 211, 8, 246, 142, 67, 143, 93, 79, 203, 0, 30, 52, 69, 214, 224, 93, 10, 56, 224, 214, 213, 121, 161, 164, 145, 108, 174, 89, 36] } }
ExtendedKeyPair { public_key: ExtendedPublicKey { public_key: Point { curve: “secp256k1”, value: “…” }, chain_code: [17, 99, 196, 6, 250, 193, 242, 28, 104, 2, 74, 97, 184, 68, 251, 28, 33, 167, 163, 11, 173, 44, 118, 177, 219, 183, 145, 105, 192, 135, 11, 200] }, secret_key: ExtendedSecretKey { secret_key: SecretScalar, chain_code: [17, 99, 196, 6, 250, 193, 242, 28, 104, 2, 74, 97, 184, 68, 251, 28, 33, 167, 163, 11, 173, 44, 118, 177, 219, 183, 145, 105, 192, 135, 11, 200] } }

ちょっとよくわからん…

【Rust】木構造(tree)の深さ優先探索(depth-first search)

### 行きがけ順(上から左下へ)
下のような木構造の場合、左から順に 0 -> 1 -> 3 -> 4 -> 2 -> 5 -> 6 と探索していく。
0
1 2
3 4 5 6
これは、再帰処理を行う。

struct Tree {
    node: Vec<[i32; 2]>,
}

fn search(pos: usize) {
    let tree = Tree { node: vec![[1, 2], [3, 4], [5, 6], [7, 8],[9, 10], [11, 12], [13, 14], [0, 0], [0, 0], [0, 0], [0, 0],[0, 0],[0, 0],[0, 0],[0, 0]]};
    println!("{}", pos);
    for i in tree.node[pos] {
        if i != 0 {
            search(i.try_into().unwrap());
        }
    }
}

fn main() {
    search(0);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/rust`
0
1
3
7
8
4
9
10
2
5
11
12
6
13
14

### 帰りがけ順(子ノード優先して上へ)
下のような木構造の場合、左から順に 3 -> 4 -> 1 -> 5 -> 6 -> 2 -> 0 と子ノードから順番に探索していく。一番上が一番最後になる。
0
1 2
3 4 5 6

fn search(pos: usize) {
    let tree = Tree { node: vec![[1, 2], [3, 4], [5, 6], [7, 8],[9, 10], [11, 12], [13, 14], [0, 0], [0, 0], [0, 0], [0, 0],[0, 0],[0, 0],[0, 0],[0, 0]]};
    for i in tree.node[pos] {
        if i != 0 {
            search(i.try_into().unwrap());
        }
    }
    println!("{}", pos);
}

fn main() {
    search(0);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.68s
Running `target/debug/rust`
7
8
3
9
10
4
1
11
12
5
13
14
6
2
0

### 通りがけ順(子ノードから上下上下)
下のような木構造の場合、左から順に 3 -> 1 -> 4 -> 0 -> 5 -> 2 -> 6 と子ノードから順番に探索していく。一番右の子ノードが一番最後になる。
0
1 2
3 4 5 6

fn search(pos: usize) {
    let tree = Tree { node: vec![[1, 2], [3, 4], [5, 6], [7, 8],[9, 10], [11, 12], [13, 14], [0, 0], [0, 0], [0, 0], [0, 0],[0, 0],[0, 0],[0, 0],[0, 0]]};
    if tree.node[pos][0] != 0 && tree.node[pos][1] != 0 {
        search(tree.node[pos][0].try_into().unwrap());
        println!("{}", pos);
        search(tree.node[pos][1].try_into().unwrap());
    } else if tree.node[pos][0] != 0 || tree.node[pos][1] != 0 {
        search(tree.node[pos][0].try_into().unwrap());
        println!("{}", pos);
    } else {
        println!("{}", pos);
    }
}

fn main() {
    search(0);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.26s
Running `target/debug/rust`
7
3
8
1
9
4
10
0
11
5
12
2
13
6
14

最初のif文では子ノードがあれば左を再帰処理、自信をprintして右を再帰処理
2つ目のif文ではどちらかに子ノードがあれば左を再帰処理
2つ目のif文では子ノードがないので自身をprint

帰りがけは完全に子ノードからボトムアップだが、通りがけは左からツリーを作っていくような順番だ。

Treeの中から一つだけ探したい場合は、幅優先探索が最も早い。
全ての答えを見つける時、決められた深さまで探索する時は、深さ優先探索がよく使われる。

基本ロジックはわかったので、応用編を試してみたいですな。

【Rust】木構造(tree)の幅優先探索(breadth-first search)

幅優先探索は、木構造で横に順番に探索していく。
木構造をプログラムで表現するには、各ノードの下のノードの番号を配列で持つようにする。
例えば 下のような木構造があった場合、
0
1 2
3 4 5 6

0は1, 2を子ノードとして持つので、[1, 2]
1は3, 4を子ノードとして持つので、[3, 4]
2は5, 6を子ノードとして持つので、[5, 6] となる。

use std::collections::VecDeque;

struct Tree {
    node: Vec<[i32; 2]>,
}

fn main() {
    let tree = Tree { node: vec![[1, 2], [3, 4], [5, 6], [7, 8],[9, 10], [11, 12], [13, 14], [0, 0], [0, 0], [0, 0], [0, 0],[0, 0],[0, 0],[0, 0],[0, 0]]};
    
    let mut data: VecDeque<i32> = [0].into();

    while data.len() > 0 {
        let pos = data.pop_front();
        println!("{:?} ", pos.unwrap());
        for i in tree.node[pos.unwrap() as usize] {
            if i != 0 {
                data.push_back(i);
            }
        }
    }
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/rust`
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14

ノードのデータの表現の仕方が勉強になりますね。

【Rust】AxumでCSS, JSなどstaticファイルを使いたい時

tower-http = { version = “0.6.2”, features = [“fs”] }

staticフォルダにcssファイルを置きます。
static/styles.css

h1 {
    color:red;
}

template/test.html

<head>
    <title>title</title>
    <link rel="stylesheet" href="styles.css">
</head>
<h1>Hello world</h1>

main.rs

use tower_http::services::{ServeDir, ServeFile};
use axum::{
    routing::get,
    Router,
};

#[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))
        .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_index()-> axum::response::Html<String> {
    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())
}

なるほど、これでCSSもjsも自由にコーディングできますね。

【Rust】QRコードの生成

qrcode = “0.14.1”
image = “0.25.5”

use qrcode::QrCode;
use image::Luma;

fn main() {
    let code = QrCode::new(b"https://google.com").unwrap();
    let image = code.render::<Luma<u8>>().build();

    image.save("./data/qrcode.png").unwrap();

    let string = code.render()
        .light_color(' ')
        .dark_color('#')
        .build();
    println!("{}", string);
}

うーん、なるほど…