shoalart 0.0.100

(WIP) Powerful ASCII Art generator, but not yet easy to use.
use rustdct::algorithm::type2and3_butterflies::{Type2And3Butterfly4, Type2And3Butterfly8};

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

    #[test]
    fn test_dct_8x8() {
        let b = &mut [
            [1., 1., 1., 1., 0., 0., 0., 0.],
            [1., 1., 1., 1., 0., 0., 0., 0.],
            [1., 1., 1., 1., 0., 0., 0., 0.],
            [1., 1., 1., 1., 0., 0., 0., 0.],
            [0., 0., 0., 0., 1., 1., 1., 1.],
            [0., 0., 0., 0., 1., 1., 1., 1.],
            [0., 0., 0., 0., 1., 1., 1., 1.],
            [0., 0., 0., 0., 1., 1., 1., 1.],
        ];
        dct_8x8(b);
        assert_eq!(b, &[
            [32., 0.,        0., 0.,        0., 0.,         0., 0.        ],
            [ 0., 13.137071, 0.,-4.613126,  0., 3.0823913,  0.,-2.613126  ],
            [ 0., 0.,        0., 0.,        0., 0.,         0., 0.        ],
            [ 0.,-4.6131253, 0., 1.6199145, 0.,-1.0823917,  0., 0.91760784],
            [ 0., 0.,        0., 0.,        0., 0.,         0., 0.        ],
            [ 0., 3.082391,  0.,-1.0823922, 0., 0.7232312,  0.,-0.61312586],
            [ 0., 0.,        0., 0.,        0., 0.,         0., 0.        ],
            [ 0.,-2.6131256, 0., 0.9176079, 0.,-0.61312574, 0., 0.5197831 ],
        ]);
    }
    #[test]
    fn test_idct_8x8() {
        let b = &mut [
            [32., 0.,        0., 0.,        0., 0.,         0., 0.        ],
            [ 0., 13.137071, 0.,-4.613126,  0., 3.0823913,  0.,-2.613126  ],
            [ 0., 0.,        0., 0.,        0., 0.,         0., 0.        ],
            [ 0.,-4.6131253, 0., 1.6199145, 0.,-1.0823917,  0., 0.91760784],
            [ 0., 0.,        0., 0.,        0., 0.,         0., 0.        ],
            [ 0., 3.082391,  0.,-1.0823922, 0., 0.7232312,  0.,-0.61312586],
            [ 0., 0.,        0., 0.,        0., 0.,         0., 0.        ],
            [ 0.,-2.6131256, 0., 0.9176079, 0.,-0.61312574, 0., 0.5197831 ],
        ];
        idct_8x8(b);
        assert_eq!(b, &[
            [1.,                1.,                1.,                0.99999994,        0.000000059604645, 0.,                0.,                0.               ],
            [1.,                1.,                1.,                0.99999994,        0.000000059604645, 0.,               -0.000000059604645, 0.               ],
            [0.99999994,        1.,                1.,                0.9999999,         0.00000008940697,  0.000000029802322,-0.000000059604645, 0.000000059604645],
            [0.9999999,         0.99999994,        0.99999994,        0.99999994,        0.000000059604645, 0.000000059604645, 0.000000059604645, 0.00000014901161 ],
            [0.00000014901161,  0.000000059604645, 0.000000059604645, 0.000000059604645, 0.99999994,        0.99999994,        0.99999994,        0.9999999        ],
            [0.000000059604645,-0.000000059604645, 0.000000029802322, 0.00000008940697,  0.9999999,         1.,                1.,                0.99999994       ],
            [0.,               -0.000000059604645, 0.,                0.000000059604645, 0.99999994,        1.,                1.,                1.               ],
            [0.,                0.,                0.,                0.000000059604645, 0.99999994,        1.,                1.,                1.               ],
        ]);
    }
    #[test]
    fn test_idct_8x8_distort() {
        let b = &mut [
            [32., 0.,       0., 0., 0., 0., 0., 0.],
            [ 0.,13.137071, 0., 0., 0., 0., 0., 0.],
            [ 0., 0.,       0., 0., 0., 0., 0., 0.],
            [ 0., 0.,       0., 0., 0., 0., 0., 0.],
            [ 0., 0.,       0., 0., 0., 0., 0., 0.],
            [ 0., 0.,       0., 0., 0., 0., 0., 0.],
            [ 0., 0.,       0., 0., 0., 0., 0., 0.],
            [ 0., 0.,       0., 0., 0., 0., 0., 0.],
        ];
        idct_8x8(b);
        assert_eq!(b, &[
            [ 1.2898169,   1.1695745,  0.9473953,   0.6571044,  0.34289566, 0.052604675,-0.16957444,-0.28981692 ],
            [ 1.1695745,   1.0676378,  0.8792834,   0.63318664, 0.36681336, 0.12071654, -0.0676378, -0.16957444 ],
            [ 0.9473953,   0.8792834,  0.7534291,   0.5889925,  0.41100752, 0.24657089,  0.12071654, 0.052604645],
            [ 0.6571044,   0.63318664, 0.5889925,   0.53125,    0.46875,    0.41100752,  0.36681336, 0.34289563 ],
            [ 0.34289563,  0.36681336, 0.41100752,  0.46875,    0.53125,    0.5889925,   0.63318664, 0.6571044  ],
            [ 0.052604645, 0.12071654, 0.24657089,  0.41100752, 0.5889925,  0.7534291,   0.8792834,  0.9473953  ],
            [-0.16957444, -0.0676378,  0.12071654,  0.36681336, 0.63318664, 0.8792834,   1.0676378,  1.1695745  ],
            [-0.28981692, -0.16957444, 0.052604675, 0.34289566, 0.6571044,  0.9473953,   1.1695745,  1.2898169  ],
        ]);
    }

    #[test]
    fn test_dct_4x8() {
        let b = &mut [
            [1., 1., 1., 1.,                0., 0., 0., 0.],
            [1., 1., 1., 1., /* The     */  0., 0., 0., 0.],
            [1., 1., 1., 1., /* right   */  0., 0., 0., 0.],
            [1., 1., 1., 1., /* side    */  0., 0., 0., 0.],
            [0., 0., 0., 0., /* should  */  0., 0., 0., 0.],
            [0., 0., 0., 0., /* be      */  0., 0., 0., 0.],
            [0., 0., 0., 0., /* ignored */  0., 0., 0., 0.],
            [0., 0., 0., 0.,                0., 0., 0., 0.],
        ];
        dct_4x8(b);
        assert_eq!(b, &[
            [16.0,       0., 0., 0., 0., 0., 0., 0.],
            [10.251661,  0., 0., 0., 0., 0., 0., 0.],
            [ 0.,       0., 0., 0., 0., 0., 0., 0.],
            [-3.599905,  0., 0., 0., 0., 0., 0., 0.],
            [ 0.,       0., 0., 0., 0., 0., 0., 0.],
            [ 2.4053788, 0., 0., 0., 0., 0., 0., 0.],
            [ 0.,       0., 0., 0., 0., 0., 0., 0.],
            [-2.0391824, 0., 0., 0., 0., 0., 0., 0.],
        ]);
    }
    #[test]
    fn test_idct_4x8() {
        let b = &mut [
            [16.0,       0., 0., 0., 0., 0., 0., 0.],
            [10.251661,  0., 0., 0., 0., 0., 0., 0.],
            [ 0.,        0., 0., 0., 0., 0., 0., 0.],
            [-3.599905,  0., 0., 0., 0., 0., 0., 0.],
            [ 0.,        0., 0., 0., 0., 0., 0., 0.],
            [ 2.4053788, 0., 0., 0., 0., 0., 0., 0.],
            [ 0.,        0., 0., 0., 0., 0., 0., 0.],
            [-2.0391824, 0., 0., 0., 0., 0., 0., 0.],
        ];
        idct_4x8(b);
        assert_eq!(b, &[
            [ 1.438622,    1.3291135,   1.0172594,   0.5505368,   0., 0., 0., 0.],
            [ 1.5111153,   1.3960885,   1.0685198,   0.5782788,   0., 0., 0., 0.],
            [ 1.2016646,   1.1101933,   0.8497052,   0.45985717,  0., 0., 0., 0.],
            [ 1.084887,    1.0023048,   0.767131,    0.41516823,  0., 0., 0., 0.],
            [-0.10147545, -0.09375111, -0.07175396, -0.038832966, 0., 0., 0., 0.],
            [-0.21844524, -0.20181715, -0.15446416, -0.08359535,  0., 0., 0., 0.],
            [-0.0330514,  -0.03053552, -0.023370862,-0.01264821,  0., 0., 0., 0.],
            [-0.040344596,-0.037273556,-0.028527945,-0.015439197, 0., 0., 0., 0.],
        ]);
    }
    #[test]
    fn test_idct_4x8_distort() {
        let b = &mut [
            [16.0,       0., 0., 0., 0., 0., 0., 0.],
            [10.251661,  0., 0., 0., 0., 0., 0., 0.],
            [ 0.,        0., 0., 0., 0., 0., 0., 0.],
            [-3.599905,  0., 0., 0., 0., 0., 0., 0.],
            [ 0.,        0., 0., 0., 0., 0., 0., 0.],
            [ 0.,        0., 0., 0., 0., 0., 0., 0.],
            [ 0.,        0., 0., 0., 0., 0., 0., 0.],
            [ 0.,        0., 0., 0., 0., 0., 0., 0.],
        ];
        idct_4x8(b);
        assert_eq!(b, &[
            [1.4157348,   1.3079684,   1.0010756,   0.5417782,   0.0, 0.0, 0.0, 0.0],
            [1.4696376,   1.357768,    1.0391908,   0.56240594,  0.0, 0.0, 0.0, 0.0],
            [1.3769432,   1.2721297,   0.97364587,  0.5269334,   0.0, 0.0, 0.0, 0.0],
            [0.83146954,  0.7681776,   0.5879378,   0.31818965,  0.0, 0.0, 0.0, 0.0],
            [0.09494825,  0.08772074,  0.06713856,  0.036335126, 0.0, 0.0, 0.0, 0.0],
            [-0.25992286,-0.24013743, -0.18379325, -0.09946818,  0.0, 0.0, 0.0, 0.0],
            [-0.12317133,-0.11379549, -0.08709529, -0.047135606, 0.0, 0.0, 0.0, 0.0],
            [0.059791297, 0.055239946, 0.042278826, 0.02288115,  0.0, 0.0, 0.0, 0.0],
        ]);
    }

    #[test]
    fn test_swapaxes() {
        let mut b = &mut [
            [1, 1, 1, 0, 0, 1, 1, 1],
            [1, 0, 1, 0, 3, 1, 1, 1],
            [1, 1, 1, 0, 0, 1, 1, 1],
            [0, 0, 0, 0, 0, 0, 4, 0],
            [0, 0, 0, 0, 0, 0, 0, 0],
            [1, 1, 1, 0, 0, 0, 0, 0],
            [1, 2, 1, 0, 0, 0, 0, 0],
            [1, 1, 1, 0, 0, 0, 0, 0],
        ];
        swapaxes_8x8(&mut b);
        assert_eq!(b, &[
            [1, 1, 1, 0, 0, 1, 1, 1],
            [1, 0, 1, 0, 0, 1, 2, 1],
            [1, 1, 1, 0, 0, 1, 1, 1],
            [0, 0, 0, 0, 0, 0, 0, 0],
            [0, 3, 0, 0, 0, 0, 0, 0],
            [1, 1, 1, 0, 0, 0, 0, 0],
            [1, 1, 1, 4, 0, 0, 0, 0],
            [1, 1, 1, 0, 0, 0, 0, 0],
        ]);
    }
}

pub fn dct_8x8(b: &mut [[f32; 8]; 8]) {
    let algo = Type2And3Butterfly8::<f32>::new();
    unsafe {
        b.iter_mut().for_each(|v| algo.process_inplace_dct2(v));
        swapaxes_8x8(b);
    }
    unsafe {
        b.iter_mut().for_each(|v| algo.process_inplace_dct2(v));
        swapaxes_8x8(b);
    }
}

/// For reference.
#[allow(dead_code)]
pub fn idct_8x8(b: &mut [[f32; 8]; 8]) {
    let algo = Type2And3Butterfly8::<f32>::new();
    unsafe {
        b.iter_mut().for_each(|v| algo.process_inplace_dct3(v));
        swapaxes_8x8(b);
    }
    b.iter_mut().flatten().for_each(|n| *n /= 4.);
    unsafe {
        b.iter_mut().for_each(|v| algo.process_inplace_dct3(v));
        swapaxes_8x8(b);
    }
    b.iter_mut().flatten().for_each(|n| *n /= 4.);
}

pub fn dct_4x8(b: &mut [[f32; 8]; 8]) {
    let algo = Type2And3Butterfly4::<f32>::new();
    unsafe {
        b.iter_mut().for_each(|v| algo.process_inplace_dct2(v));
        swapaxes_8x8(b);
    }
    let algo = Type2And3Butterfly8::<f32>::new();
    unsafe {
        b.iter_mut().for_each(|v| algo.process_inplace_dct2(v));
        swapaxes_8x8(b);
    }
}

/// For reference.
#[allow(dead_code)]
pub fn idct_4x8(b: &mut [[f32; 8]; 8]) {
    let algo = Type2And3Butterfly4::<f32>::new();
    unsafe {
        b.iter_mut().for_each(|v| algo.process_inplace_dct2(v));
        swapaxes_8x8(b);
    }
    b.iter_mut().flatten().for_each(|n| *n /= 4.);
    let algo = Type2And3Butterfly8::<f32>::new();
    unsafe {
        b.iter_mut().for_each(|v| algo.process_inplace_dct2(v));
        swapaxes_8x8(b);
    }
    b.iter_mut().flatten().for_each(|n| *n /= 4.);
}

fn swapaxes_8x8<T>(b: &mut [[T; 8]; 8]) {
    for y in 1..8 {
        for x in 0..y {
            unsafe { std::ptr::swap(&mut b[y][x], &mut b[x][y]) };
        }
    }
}

pub fn dct_8x8_feature(b: &[[f32; 8]; 8]) -> [f32; 10] {
    let mut b = b.clone();
    dct_8x8(&mut b);
    return extract(&b);
}

pub fn dct_4x8_feature(b: &[[f32; 8]; 8]) -> [f32; 10] {
    let mut b = b.clone();
    dct_4x8(&mut b);
    return extract(&b);
}

#[rustfmt::skip]
pub fn extract(b: &[[f32; 8]; 8]) -> [f32; 10] {
    return [b[0][0], b[1][0], b[0][1], b[0][2], b[1][1], b[2][0], b[3][0], b[2][1], b[1][2], b[0][3]];
}

#[rustfmt::skip]
pub fn similarity(f: &[f32; 10], f2: &[f32; 10]) -> f32 {
    // 也许需要一些偏移?
      (f[0] - f2[0]).abs()
    + (f[1] - f2[1]).abs()
    + (f[2] - f2[2]).abs()
    + (f[3] - f2[3]).abs()
    + (f[4] - f2[4]).abs()
    + (f[5] - f2[5]).abs()
    + (f[6] - f2[6]).abs()
    + (f[7] - f2[7]).abs()
    + (f[8] - f2[8]).abs()
    + (f[9] - f2[9]).abs()
}

// 更加通用的参考实现。
//
// pub fn similarity(v: &[[f32; 8]; 8], s: &[f32; 10]) -> f32 {
//     let mut ans = 0f32;
//     let mut exp = 0u8;
//     let mut tgt = 0u8;
//     let mut lev = 1u16;
//     let mut x = 0;
//     let mut y = 0;
//     let mut d: u8 = 0;
//     // "↓, ↗, →, ↙" = "0, 1, 2, 3"
//     // “为什么我下意识的选择这个顺序?因为另一种看着让我不安。”
//     for &b in s {
//         let a = v[y][x];
//         ans += if a > b { a - b } else { b - a } / lev as f32;
//         if exp < tgt {
//             exp += 1;
//         } else {
//             lev <<= 1;
//             tgt += 1;
//             exp = 0;
//         }
//         match d {   // 移动
//             1 => if y > 0 { x += 1; y -= 1 }    // ↗
//             3 => if x > 0 { x -= 1; y += 1 }    // ↙
//             0 =>                    y += 1,     // ↓
//             2 =>            x += 1,             // →
//             _ => (),
//         }
//         match d {   // 转向
//             1 => if y == 0 { d += 1 }
//             3 => if x == 0 { d =  0 }
//             _ =>             d += 1,
//         }
//     }
//     return ans;
// }