urbit-ob 0.2.0

Utilities for formatting and parsing Urbit's @p and @q data types (patp/patq).
Documentation
use ibig::{ubig, UBig};
use murmur3::murmur3_32;
use once_cell::sync::Lazy;
use std::io::Cursor;

fn muk(syd: u32, key: u64) -> u32 {
    let bytes = key.to_le_bytes();
    let lo = bytes.get(0).or(Some(&0)).unwrap();
    let hi = bytes.get(1).or(Some(&0)).unwrap();
    murmur3_32(&mut Cursor::new([*lo, *hi]), syd).unwrap()
}

#[test]
fn test_muk() {
    assert_eq!(muk(0, 0x101), 0x42081a9b);
    assert_eq!(muk(0, 0x201), 0x64c7667e);
    assert_eq!(muk(0, 0x4812), 0xa30782dc);
}

static UX_1_0000: Lazy<UBig> = Lazy::new(|| ubig!(0x10000));
static UX_FFFF_FFFF: Lazy<UBig> = Lazy::new(|| ubig!(0xFFFFFFFF));
static UX_1_0000_0000: Lazy<UBig> = Lazy::new(|| ubig!(0x1_00000000));
static UX_FFFF_FFFF_FFFF_FFFF: Lazy<UBig> = Lazy::new(|| ubig!(0xFFFFFFFF_FFFFFFFF));
static LO_MASK: Lazy<UBig> = Lazy::new(|| ubig!(0x00000000_FFFFFFFF));
static HI_MASK: Lazy<UBig> = Lazy::new(|| ubig!(0xFFFFFFFF_00000000));

#[allow(non_snake_case)]
fn F(j: usize, arg: u64) -> u64 {
    let raku: [u32; 4] = [0xb76d5eed, 0xee281300, 0x85bcae01, 0x4b387af7];
    muk(*raku.get(j).unwrap(), arg) as u64
}

type FType = fn(usize, u64) -> u64;

pub fn fein(arg: &UBig) -> UBig {
    let lo = &*LO_MASK & arg;
    let hi = &*HI_MASK & arg;

    if arg >= &UX_1_0000 && arg <= &UX_FFFF_FFFF {
        let arg_64: u64 = arg.try_into().unwrap();
        UBig::from(0x10000 + feis(arg_64 - 0x10000))
    } else if arg >= &UX_1_0000_0000 && arg <= &UX_FFFF_FFFF_FFFF_FFFF {
        hi | fein(&lo)
    } else {
        arg.clone()
    }
}

pub fn fynd(arg: &UBig) -> UBig {
    let lo = &*LO_MASK & arg;
    let hi = &*HI_MASK & arg;

    if arg >= &UX_1_0000 && arg <= &UX_FFFF_FFFF {
        let arg_64: u64 = arg.try_into().unwrap();
        UBig::from(0x10000 + tail(arg_64 - 0x10000))
    } else if arg >= &UX_1_0000_0000 && arg <= &UX_FFFF_FFFF_FFFF_FFFF {
        hi | fynd(&lo)
    } else {
        arg.clone()
    }
}

fn feis(arg: u64) -> u64 {
    Fe(4, 65535, 65536, 0xFFFF_FFFF, F, arg)
}

#[allow(non_snake_case)]
fn Fe(r: usize, a: u64, b: u64, k: u64, f: FType, m: u64) -> u64 {
    let c = fe(r, a, b, f, m);
    if c < k {
        c
    } else {
        fe(r, a, b, f, c)
    }
}

#[allow(non_snake_case)]
fn fe(r: usize, a: u64, b: u64, f: FType, m: u64) -> u64 {
    fn fe_loop(r: usize, a: u64, b: u64, f: FType, m: u64, j: usize, ell: u64, arr: u64) -> u64 {
        if j > r {
            return if r % 2 != 0 {
                a * arr + ell
            } else if arr == a {
                a * arr + ell
            } else {
                a * ell + arr
            };
        } else {
            let eff = f(j - 1, arr);
            let tmp = if j % 2 != 0 {
                (ell + eff) % a
            } else {
                (ell + eff) % b
            };
            fe_loop(r, a, b, f, m, j + 1, arr, tmp)
        }
    }

    let L = m % a;
    let R = m / a;

    fe_loop(r, a, b, f, m, 1, L, R)
}

fn tail(arg: u64) -> u64 {
    Fen(4, 65535, 65536, 0xFFFF_FFFF, F, arg)
}

#[allow(non_snake_case)]
fn Fen(r: usize, a: u64, b: u64, k: u64, f: FType, m: u64) -> u64 {
    let c = fen(r, a, b, f, m);
    if c < k {
        c
    } else {
        fen(r, a, b, f, c)
    }
}

#[allow(non_snake_case)]
fn fen(r: usize, a: u64, b: u64, f: FType, m: u64) -> u64 {
    fn fe_loop(r: usize, a: u64, b: u64, f: FType, m: u64, j: usize, ell: u64, arr: u64) -> u64 {
        if j < 1 {
            a * arr + ell
        } else {
            let eff = f(j - 1, ell);
            let tmp = if j % 2 != 0 {
                (arr + a - eff % a) % a
            } else {
                (arr + b - eff % b) % b
            };
            fe_loop(r, a, b, f, m, j - 1, tmp, ell)
        }
    }

    let ahh = if r % 2 != 0 { m / a } else { m % a };

    let ale = if r % 2 != 0 { m % a } else { m / a };

    let L = if ale == a { ahh } else { ale };

    let R = if ale == a { ale } else { ahh };

    fe_loop(r, a, b, f, m, r, L, R)
}