predict_f32

Function predict_f32 

Source
pub fn predict_f32(input: &mut [u8], output: &mut [u8], samples: usize)
Expand description

Reverse floating point prediction

floating point prediction first shuffles the bytes and then uses horizontal differencing
also performs byte-order conversion if needed.

let samples = 1;
let input = [42.0f32, 43.0];
let in_be_bytes: Vec<u8> = input.iter().flat_map(|f| f.to_be_bytes()).collect();
assert_eq!(vec![0x42,0x28,0,0,0x42,0x2c,0,0],in_be_bytes);
//               0   1   2 3   4  5   6 7-- OG
//               0   2         1  3
//                       0 2          1 3
//             --0   4   1 5   2  6   3 7-- shuffled
//               0       1     2      3
//                   0     1      2     3
//               0   1   2 3   4  5   6 7-- deshuffled
let mut in_shuffled = vec![0x42,0x42,0x28,0x2c,0,0,0,0];
let n_floats = in_be_bytes.len() / 4;
for (i, chunk) in in_shuffled.chunks_mut(n_floats).enumerate() {
    for (j, c) in chunk.iter_mut().enumerate() {
        *c = in_be_bytes[j * 4 + i]
    }
}
println!("shuffled: {in_shuffled:?}");
assert_eq!(vec![0x42,0x42,0x28,0x2c,0,0,0,0], in_shuffled);
let mut in_diffed = vec![0x42u8,0,230,4,212,0,0,0];
// let's see if we can do horizontal differencing without an extra copy...
let prev = in_diffed[..samples].clone();
// [1 2 3 4 5 6 7 8] samples = 2
// [1 2]| |
//   \--2 |          i=0; i=-2%2=0; in[i].wrapping_sub(prev[i%samples])
//   [3 2]|          prev[i%samples]=in[i]
//      \-2          i=1; i-samples%samples=-1%2=1;
// this seems horribly inefficient...
// maybe with a double-sized buffer...
// [0 1 2 3 4 5 6 7] samples = 2; let d_samp = samples*2=4;
// [0 1 2 3]         prev = in[..dsamp].clone();
//   \-[2]3 4 5 6 7  i=0; in[i]=2; in[i] = in[i].wrapping_sub(prev[i%dsamp])
// [4 1 2 3]         prev[(i+samples) % dsamp] = in[i+samples]
//     \2[2]4 5 6 7  i=1; in[i] = 3-1 = 2
// [4(5)2 3]         prev[1%4]=prev[1]=in[1+2]=in[3]=3
//      2\2[2]5 6 7  i=2; in[i]=  4-2 = 2
// [4 5 6 3]         prev[2%4]=prev[2]=in[2+2]=in[4]=4
//      2 2\2[2]6 7  i=3; in[i]=  5-3 = 2
//         [4 5 6 7] prev[3%4]=prev[3]=in[3+2]=in[5]=5
//      -------------remainder
//      2 2 2 2[2]7
//      2 2 2 2 2[2]
for i in samples..in_diffed.len() {
    let p = in_diffed[i];
    in_diffed[i] = in_shuffled[i].wrapping_sub(prev[i%samples]);
    prev[i%samples] = p
}
println!("diffed: {in_diffed:?}");
assert_eq!(vec![0x42u8,0,230,4,212,0,0,0], in_diffed);
//assert_eq!(vec![42,42,0x28,0x2c,0,0,0,0], in_shuffled);
//              0  1  2    3   4 5 6 7 len=8 8/4=2
//              0     1        2   3   : 0 2 4 6
//                 0       1     2   3 : 1 3 5 7
let mut output = vec![0;8];
for (i, chunk) in output.chunks_mut(4).enumerate() {
    chunk.copy_from_slice(&u32::to_be_bytes( // don't convert to native-endian
        // preserve original byte-order
        u32::from_be_bytes([
            in_shuffled[i],
            in_shuffled[in_shuffled.len() / 4 + i],
            in_shuffled[in_shuffled.len() / 4 * 2 + i],
            in_shuffled[in_shuffled.len() / 4 * 3 + i],
        ])));
}
assert_eq!(output, in_be_bytes);
let mut in_diffed = vec![0x42u8,0,230,4,212,0,0,0];
for i in samples..in_diffed.len() {
  in_diffed[i] = in_diffed[i].wrapping_add(in_diffed[i-samples]);
}
assert_eq!(in_diffed, in_shuffled);