You are a helpful assistant.
system
Tạo một chương trình Rust tính toán đường đi ngắn nhất giữa hai nút trong biểu đồ có trọng số, vô hướng bằng thuật toán Dijkstra, với các giới hạn về độ phức tạp thời gian O(V^2) và bộ nhớ tối đa 1MB. Biểu đồ phải được biểu diễn dưới dạng ma trận kề được đọc từ tệp văn bản. Chương trình sẽ nhận các đối số dòng lệnh cho đường dẫn tệp đầu vào cũng như ID nút nguồn và đích, đồng thời xuất ra đường dẫn ngắn nhất cùng với tổng trọng số của nó, kèm theo xử lý lỗi cho các đầu vào không hợp lệ và các bài kiểm tra đơn vị để xác thực chức năng, đồng thời sử dụng thư viện Rust như `petgraph` để cải thiện hiệu suất.
user
Để tạo một chương trình Rust tính toán đường đi ngắn nhất giữa hai nút trong biểu đồ có trọng số, vô hướng bằng thuật toán Dijkstra, chúng ta sẽ thực hiện các bước sau:

1. Đọc ma trận kề từ tệp văn bản.
2. Sử dụng thuật toán Dijkstra để tìm đường đi ngắn nhất.
3. Xử lý các lỗi đầu vào không hợp lệ.
4. Viết các bài kiểm tra đơn vị để xác thực chức năng.

Dưới đây là mã nguồn cho chương trình:

```rust
use std::env;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::Path;
use std::usize::MAX;

fn read_adjacency_matrix(filename: &str) -> io::Result<Vec<Vec<u32>>> {
    let file = File::open(filename)?;
    let reader = io::BufReader::new(file);
    let mut matrix = Vec::new();

    for line in reader.lines() {
        let line = line?;
        let row: Vec<u32> = line
            .split_whitespace()
            .map(|s| s.parse().unwrap_or(MAX as u32))
            .collect();
        matrix.push(row);
    }

    Ok(matrix)
}

fn dijkstra(matrix: &Vec<Vec<u32>>, start: usize, end: usize) -> (Vec<usize>, u32) {
    let n = matrix.len();
    let mut dist = vec![MAX as u32; n];
    let mut prev = vec![None; n];
    let mut visited = vec![false; n];

    dist[start] = 0;

    for _ in 0..n {
        let mut u = None;

        for i in 0..n {
            if !visited[i] && (u.is_none() || dist[i] < dist[u.unwrap()]) {
                u = Some(i);
            }
        }

        let u = match u {
            Some(u) => u,
            None => break,
        };

        if dist[u] == MAX as u32 {
            break;
        }

        visited[u] = true;

        for v in 0..n {
            if matrix[u][v] != MAX as u32 && !visited[v] {
                let alt = dist[u].saturating_add(matrix[u][v]);
                if alt < dist[v] {
                    dist[v] = alt;
                    prev[v] = Some(u);
                }
            }
        }
    }

    let mut path = Vec::new();
    let mut u = end;

    while let Some(p) = prev[u] {
        path.push(u);
        u = p;
    }

    if dist[end] != MAX as u32 {
        path.push(start);
        path.reverse();
    }

    (path, dist[end])
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() != 4 {
        eprintln!("Usage: {} <filename> <start_node> <end_node>", args[0]);
        return;
    }

    let filename = &args[1];
    let start_node: usize = args[2].parse().expect("Invalid start node");
    let end_node: usize = args[3].parse().expect("Invalid end node");

    let matrix = read_adjacency_matrix(filename).expect("Failed to read adjacency matrix");

    if start_node >= matrix.len() || end_node >= matrix.len() {
        eprintln!("Node index out of bounds");
        return;
    }

    let (path, total_weight) = dijkstra(&matrix, start_node, end_node);

    if path.is_empty() {
        println!("No path found from node {} to node {}", start_node, end_node);
    } else {
        println!("Shortest path: {:?}", path);
        println!("Total weight: {}", total_weight);
    }
}

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

    #[test]
    fn test_dijkstra() {
        let matrix = vec![
            vec![0, 1, MAX as u32, 4],
            vec![1, 0, 2, MAX as u32],
            vec![MAX as u32, 2, 0, 1],
            vec![4, MAX as u32, 1, 0],
        ];

        let (path, total_weight) = dijkstra(&matrix, 0, 2);
        assert_eq!(path, vec![0, 1, 2]);
        assert_eq!(total_weight, 3);
    }
}
```

### Giải thích:

- **Đọc ma trận kề**: Hàm `read_adjacency_matrix` đọc ma trận kề từ tệp văn bản. Mỗi dòng trong tệp đại diện cho một hàng của ma trận, và các giá trị được phân tách bằng khoảng trắng.

- **Thuật toán Dijkstra**: Hàm `dijkstra` thực hiện thuật toán Dijkstra để tìm đường đi ngắn nhất từ nút `start` đến nút `end`. Nó trả về đường đi và tổng trọng số.

- **Xử lý lỗi**: Chương trình kiểm tra số lượng đối số dòng lệnh và xác thực chỉ số nút.

- **Kiểm tra đơn vị**: Một bài kiểm tra đơn vị được viết để xác minh rằng thuật toán Dijkstra hoạt động chính xác với một ma trận kề mẫu.

Chương trình này không sử dụng thư viện `petgraph` vì yêu cầu về độ phức tạp thời gian O(V^2) và bộ nhớ tối đa 1MB, do đó việc sử dụng ma trận kề là phù hợp hơn.
assistant