use crate::functions::mul_192_hi::mul_192_hi;
use crate::functions::roundodd::roundodd;
use crate::records::decimal::Decimal;
use luaur_common::macros::luau_assert::LUAU_ASSERT;
const K_POW_10_TABLE_MIN: i32 = -292;
const K_POW_10_TABLE_MAX: i32 = 324;
const K_POW_5_TABLE: [u64; 16] = [
0x8000000000000000,
0xa000000000000000,
0xc800000000000000,
0xfa00000000000000,
0x9c40000000000000,
0xc350000000000000,
0xf424000000000000,
0x9896800000000000,
0xbebc200000000000,
0xee6b280000000000,
0x9502f90000000000,
0xba43b74000000000,
0xe8d4a51000000000,
0x9184e72a00000000,
0xb5e620f480000000,
0xe35fa931a0000000,
];
const K_POW_10_TABLE: [[u64; 3]; 39] = [
[0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b, 0x333443443333443b],
[0x8dd01fad907ffc3b, 0xae3da7d97f6792e4, 0xbbb3ab3cb3ba3cbc],
[0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa, 0x4ba4bc4bb4bb4bcc],
[0xaecc49914078536d, 0x58fae9f773886e19, 0x3ba3bc33b43b43bb],
[0xc21094364dfb5636, 0x985915fc12f542e5, 0x33b43b43a33b33cb],
[0xd77485cb25823ac7, 0x7d633293366b828c, 0x34b44c444343443c],
[0xef340a98172aace4, 0x86fb897116c87c35, 0x333343333343334b],
[0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074, 0xccaccbbcbcbb4bbc],
[0x936b9fcebb25c995, 0xcab10dd900beec35, 0x3ab3ab3ab3bb3bbb],
[0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb, 0x4cc3dc4db4db4dbb],
[0xb5b5ada8aaff80b8, 0x0d819992132456bb, 0x33b33a34c33b34ab],
[0xc9bcff6034c13052, 0xfc89b393dd02f0b6, 0x33c33b44b43c34bc],
[0xdff9772470297ebd, 0x59787e2b93bc56f8, 0x43b444444443434c],
[0xf8a95fcf88747d94, 0x75a44c6397ce912b, 0x443334343443343b],
[0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900, 0xbbabab3aa3ab4ccc],
[0x993fe2c6d07b7fab, 0xe546a8038efe402a, 0x4cb4bc4db4db4bcc],
[0xaa242499697392d2, 0xdde50bd1d5d0b9ea, 0x3ba3ba3bb33b33bc],
[0xbce5086492111aea, 0x88f4bb1ca6bcf585, 0x44b44c44c44c43cb],
[0xd1b71758e219652b, 0xd3c36113404ea4a9, 0x44c44c44c444443b],
[0xe8d4a51000000000, 0x0000000000000000, 0x444444444444444c],
[0x813f3978f8940984, 0x4000000000000000, 0xcccccccccccccccc],
[0x8f7e32ce7bea5c6f, 0xe4820023a2000000, 0xbba3bc4cc4cc4ccc],
[0x9f4f2726179a2245, 0x01d762422c946591, 0x4aa3bb3aa3ba3bab],
[0xb0de65388cc8ada8, 0x3b25a55f43294bcc, 0x3ca33b33b44b43bc],
[0xc45d1df942711d9a, 0x3ba5d0bd324f8395, 0x44c44c34c44b44cb],
[0xda01ee641a708de9, 0xe80e6f4820cc9496, 0x33b33b343333333c],
[0xf209787bb47d6b84, 0xc0678c5dbd23a49b, 0x443444444443443b],
[0x865b86925b9bc5c2, 0x0b8a2392ba45a9b3, 0xdbccbcccb4cb3bbb],
[0x952ab45cfa97a0b2, 0xdd945a747bf26184, 0x3bc4bb4ab3ca3cbc],
[0xa59bc234db398c25, 0x43fab9837e699096, 0x3bb3ac3ab3bb33ac],
[0xb7dcbf5354e9bece, 0x0c11ed6d538aeb30, 0x33b43b43b34c34dc],
[0xcc20ce9bd35c78a5, 0x31ec038df7b441f5, 0x34c44c43c44b44cb],
[0xe2a0b5dc971f303a, 0x2e44ae64840fd61e, 0x333333333333333c],
[0xfb9b7cd9a4a7443c, 0x169840ef017da3b2, 0x433344443333344c],
[0x8bab8eefb6409c1a, 0x1ad089b6c2f7548f, 0xdcbdcc3cc4cc4bcb],
[0x9b10a4e5e9913128, 0xca7cf2b4191c8327, 0x3ab3cb3bc3bb4bbb],
[0xac2820d9623bf429, 0x546345fa9fbdcd45, 0x3bb3cc43c43c43cb],
[0xbf21e44003acdd2c, 0xe0470a63e6bd56c4, 0x44b34a43b44c44bc],
[0xd433179d9c8cb841, 0x5fa60692a46151ec, 0x43a33a33a333333c],
];
#[allow(non_snake_case)]
pub fn schubfach(exponent: i32, fraction: u64) -> Decimal {
let mut c: u64 = fraction;
let mut q: i32 = exponent - 1023 - 51;
if exponent != 0 {
c |= 1u64 << 52;
q -= 1;
}
if (-(q as i64) as u64) < 53 && (c & ((1u64 << (-(q as i64) as u32)) - 1)) == 0 {
return Decimal {
s: c >> (-(q as i64) as u32),
k: 0,
};
}
let irr: i32 = if c == (1u64 << 52) && q != -1074 {
1
} else {
0
};
let out: i32 = (c & 1) as i32;
let cbl: u64 = 4u64
.wrapping_mul(c)
.wrapping_sub(2)
.wrapping_add(irr as u64);
let cb: u64 = 4u64.wrapping_mul(c);
let cbr: u64 = 4u64.wrapping_mul(c).wrapping_add(2);
const Q: i32 = 20;
const C: i32 = 315652; const A: i32 = -131008; const C2: i32 = 3483294;
let k: i32 = ((q * C + if irr != 0 { A } else { 0 }) >> Q) as i32;
let h: i32 = q + ((-k * C2) >> Q) + 1;
LUAU_ASSERT!(-k >= K_POW_10_TABLE_MIN && -k <= K_POW_10_TABLE_MAX);
let gtoff: i32 = -k - K_POW_10_TABLE_MIN;
let gt_index = (gtoff >> 4) as usize;
let gt = &K_POW_10_TABLE[gt_index];
let mut ghi: u64 = 0;
let glo: u64 = mul_192_hi(gt[0], gt[1], K_POW_5_TABLE[(gtoff & 15) as usize], &mut ghi);
let gterr: i32 = ((gt[2] >> (((gtoff & 15) as u32) * 4)) & 15) as i32;
let gtscale: i32 = gterr >> 3;
let mut ghi_shifted = ghi.wrapping_shl(gtscale as u32);
ghi_shifted = ghi_shifted.wrapping_add(((glo >> 63) & (gtscale as u64)) as u64);
let mut glo_shifted = glo.wrapping_shl(gtscale as u32);
glo_shifted = glo_shifted.wrapping_sub(((gterr & 7) - 4) as u64);
let vbl: u64 = roundodd(ghi_shifted, glo_shifted, cbl << h);
let vb: u64 = roundodd(ghi_shifted, glo_shifted, cb << h);
let vbr: u64 = roundodd(ghi_shifted, glo_shifted, cbr << h);
let s: u64 = vb / 4;
if s >= 10 {
let sp: u64 = s / 10;
let upin: bool = vbl + (out as u64) <= 40u64.wrapping_mul(sp);
let wpin: bool = vbr >= 40u64.wrapping_mul(sp) + 40u64 + (out as u64);
if upin != wpin {
return Decimal {
s: sp + if wpin { 1 } else { 0 },
k: k + 1,
};
}
}
let uin: bool = vbl + (out as u64) <= 4u64.wrapping_mul(s);
let win: bool = 4u64.wrapping_mul(s) + 4u64 + (out as u64) <= vbr;
let rup: bool = vb >= 4u64.wrapping_mul(s) + 2u64 + 1u64 - ((s & 1) as u64);
Decimal {
s: s + if uin != win {
if win {
1
} else {
0
}
} else {
if rup {
1
} else {
0
}
},
k,
}
}