cryspglib 0.1.0

A pure-Rust port of spglib — not a replacement, but a dependency-free alternative for Rust projects that need crystallographic symmetry routines without bundling a C toolchain.
Documentation
//! 整数算术与晶体学对称操作的组合规则。
//!
//! 提供对称操作(3x3 整数矩阵 + 平移向量)的等价性判断,以及
//! 算术晶体类映射表(将空间群编号映射到点群对称性)。

/// 算术晶体类别映射表 (索引对应空间群编号 0-230)
/// 优化:使用 u8 节省内存,因为最大值为 73
static ARITHMETIC_CRYSTAL_CLASSES: [u8; 231] = [
    0, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 7, 7, 8, 9, 9, 9, 9, 10, 10, 11, 12, 12, 13, 13, 13, 13,
    13, 13, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 15, 16, 16, 17, 17, 17, 18, 18, 18, 18, 18, 18,
    18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 20, 20, 21, 21, 21, 21, 22, 22,
    22, 22, 23, 23, 24, 25, 26, 26, 26, 26, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 30, 30,
    30, 30, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 35, 35, 36, 36,
    36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 41,
    42, 43, 42, 43, 42, 43, 44, 45, 46, 45, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, 51, 51, 51, 51,
    51, 52, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 57, 57, 56, 56, 58, 58, 58, 58, 59, 60,
    61, 59, 61, 62, 62, 63, 63, 64, 62, 64, 65, 65, 66, 66, 67, 65, 65, 67, 68, 69, 70, 68, 69, 70,
    71, 71, 71, 71, 72, 72, 72, 72, 73, 73,
];

/// 算术晶体类别符号表
/// 注意:这里的字符串已经包含了尾部空格,我们在返回时切片即可
static ARITHMETIC_CRYSTAL_CLASS_SYMBOLS: [&str; 74] = [
    "      ", /*  0 */
    "1P    ", /*  1 */
    "-1P   ", /*  2 */
    "2P    ", /*  3 */
    "2C    ", /*  4 */
    "mP    ", /*  5 */
    "mC    ", /*  6 */
    "2/mP  ", /*  7 */
    "2/mC  ", /*  8 */
    "222P  ", /*  9 */
    "222C  ", /* 10 */
    "222F  ", /* 11 */
    "222I  ", /* 12 */
    "mm2P  ", /* 13 */
    "mm2C  ", /* 14 */
    "2mmC  ", /* 15 */
    "mm2F  ", /* 16 */
    "mm2I  ", /* 17 */
    "mmmP  ", /* 18 */
    "mmmC  ", /* 19 */
    "mmmF  ", /* 20 */
    "mmmI  ", /* 21 */
    "4P    ", /* 22 */
    "4I    ", /* 23 */
    "-4P   ", /* 24 */
    "-4I   ", /* 25 */
    "4/mP  ", /* 26 */
    "4/mI  ", /* 27 */
    "422P  ", /* 28 */
    "422I  ", /* 29 */
    "4mmP  ", /* 30 */
    "4mmI  ", /* 31 */
    "-42mP ", /* 32 */
    "-4m2P ", /* 33 */
    "-4m2I ", /* 34 */
    "-42mI ", /* 35 */
    "4/mmmP", /* 36 */
    "4/mmmI", /* 37 */
    "3P    ", /* 38 */
    "3R    ", /* 39 */
    "-3P   ", /* 40 */
    "-3R   ", /* 41 */
    "312P  ", /* 42 */
    "321P  ", /* 43 */
    "32R   ", /* 44 */
    "3m1P  ", /* 45 */
    "31mP  ", /* 46 */
    "3mR   ", /* 47 */
    "-31mP ", /* 48 */
    "-3m1P ", /* 49 */
    "-3mR  ", /* 50 */
    "6P    ", /* 51 */
    "-6P   ", /* 52 */
    "6/mP  ", /* 53 */
    "622P  ", /* 54 */
    "6mmP  ", /* 55 */
    "-62mP ", /* 56 */
    "-6m2P ", /* 57 */
    "6/mmmP", /* 58 */
    "23P   ", /* 59 */
    "23F   ", /* 60 */
    "23I   ", /* 61 */
    "m-3P  ", /* 62 */
    "m-3F  ", /* 63 */
    "m-3I  ", /* 64 */
    "432P  ", /* 65 */
    "432F  ", /* 66 */
    "432I  ", /* 67 */
    "-43mP ", /* 68 */
    "-43mF ", /* 69 */
    "-43mI ", /* 70 */
    "m-3mP ", /* 71 */
    "m-3mF ", /* 72 */
    "m-3mI ", /* 73 */
];

/// 获取算术晶体类别符号
///
/// # Arguments
/// * `spgroup_number`: 空间群编号 (1-230)
///
/// # Returns
/// * `Option<(u8, &'static str)>`: 如果输入有效,返回 (算术类别编号, 符号切片);否则返回 None。
///   使用 Option 是比返回 (0, "") 更 Rust 的做法。
pub fn arth_get_symbol(spgroup_number: i32) -> Option<(u8, &'static str)> {
    // 使用 get 进行安全的边界检查
    // spgroup_number 是 1-based,所以需要减 1 才能对应 0-based 索引吗?
    // 不,数组大小是 231,索引 0 是占位符,索引 1 对应空间群 1。
    // 所以直接用 spgroup_number 作为索引。
    let arth_number = *ARITHMETIC_CRYSTAL_CLASSES.get(spgroup_number as usize)?;
    
    if arth_number == 0 {
        return None;
    }

    // 获取原始字符串
    let symbol_raw = ARITHMETIC_CRYSTAL_CLASS_SYMBOLS.get(arth_number as usize)?;

    // 使用 trim_end() 返回一个 slice,不进行内存分配
    // 生命周期为 'static,因为源数据是 static 的
    let symbol = symbol_raw.trim_end();

    Some((arth_number, symbol))
}

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

    #[test]
    fn test_arth_get_symbol() {
        // Test case 1: Space group 1 (P1) -> Arithmetic class 1 (1P)
        let result = arth_get_symbol(1);
        assert_eq!(result, Some((1, "1P")));

        // Test case 2: Space group 225 (Fm-3m) -> Arithmetic class 72 (m-3mF)
        let result = arth_get_symbol(225);
        assert_eq!(result, Some((72, "m-3mF")));

        // Test case 3: Invalid input
        assert_eq!(arth_get_symbol(0), None);
        assert_eq!(arth_get_symbol(231), None);
        assert_eq!(arth_get_symbol(-5), None);
    }
}