use crate::natural::arithmetic::add::{
limbs_slice_add_greater_in_place_left, limbs_slice_add_limb_in_place,
};
use crate::natural::arithmetic::mul::{
limbs_mul_greater_to_out, limbs_mul_greater_to_out_scratch_len,
};
use crate::natural::arithmetic::shl::limbs_slice_shl_in_place;
use crate::natural::arithmetic::square::{limbs_square_to_out, limbs_square_to_out_scratch_len};
use crate::natural::arithmetic::sub::{limbs_sub_greater_in_place_left, limbs_sub_limb_in_place};
use crate::natural::logic::not::{limbs_not_in_place, limbs_not_to_out};
use crate::natural::{
HALF_LIMB_HIGH_BIT, HALF_WIDTH, LIMB_HIGH_BIT, WIDTH_MINUS_1, bit_to_limb_count_ceiling,
bit_to_limb_count_floor, limb_to_bit_count,
};
use crate::platform::Limb;
use malachite_base::num::arithmetic::traits::{ModPowerOf2, PowerOf2, Square, XMulYToZZ};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::conversion::traits::ExactFrom;
const T1: [u16; 384] = [
2040, 2033, 2025, 2017, 2009, 2002, 1994, 1987, 1980, 1972, 1965, 1958, 1951, 1944, 1938, 1931,
1925, 1918, 1912, 1905, 1899, 1892, 1886, 1880, 1874, 1867, 1861, 1855, 1849, 1844, 1838, 1832,
1827, 1821, 1815, 1810, 1804, 1799, 1793, 1788, 1783, 1777, 1772, 1767, 1762, 1757, 1752, 1747,
1742, 1737, 1733, 1728, 1723, 1718, 1713, 1709, 1704, 1699, 1695, 1690, 1686, 1681, 1677, 1673,
1669, 1664, 1660, 1656, 1652, 1647, 1643, 1639, 1635, 1631, 1627, 1623, 1619, 1615, 1611, 1607,
1603, 1600, 1596, 1592, 1588, 1585, 1581, 1577, 1574, 1570, 1566, 1563, 1559, 1556, 1552, 1549,
1545, 1542, 1538, 1535, 1532, 1528, 1525, 1522, 1518, 1515, 1512, 1509, 1505, 1502, 1499, 1496,
1493, 1490, 1487, 1484, 1481, 1478, 1475, 1472, 1469, 1466, 1463, 1460, 1457, 1454, 1451, 1449,
1446, 1443, 1440, 1438, 1435, 1432, 1429, 1427, 1424, 1421, 1419, 1416, 1413, 1411, 1408, 1405,
1403, 1400, 1398, 1395, 1393, 1390, 1388, 1385, 1383, 1380, 1378, 1375, 1373, 1371, 1368, 1366,
1363, 1360, 1358, 1356, 1353, 1351, 1349, 1346, 1344, 1342, 1340, 1337, 1335, 1333, 1331, 1329,
1327, 1325, 1323, 1321, 1319, 1316, 1314, 1312, 1310, 1308, 1306, 1304, 1302, 1300, 1298, 1296,
1294, 1292, 1290, 1288, 1286, 1284, 1282, 1280, 1278, 1276, 1274, 1272, 1270, 1268, 1266, 1265,
1263, 1261, 1259, 1257, 1255, 1253, 1251, 1250, 1248, 1246, 1244, 1242, 1241, 1239, 1237, 1235,
1234, 1232, 1230, 1229, 1227, 1225, 1223, 1222, 1220, 1218, 1217, 1215, 1213, 1212, 1210, 1208,
1206, 1204, 1203, 1201, 1199, 1198, 1196, 1195, 1193, 1191, 1190, 1188, 1187, 1185, 1184, 1182,
1181, 1180, 1178, 1177, 1175, 1174, 1172, 1171, 1169, 1168, 1166, 1165, 1163, 1162, 1160, 1159,
1157, 1156, 1154, 1153, 1151, 1150, 1149, 1147, 1146, 1144, 1143, 1142, 1140, 1139, 1137, 1136,
1135, 1133, 1132, 1131, 1129, 1128, 1127, 1125, 1124, 1123, 1121, 1120, 1119, 1117, 1116, 1115,
1114, 1113, 1111, 1110, 1109, 1108, 1106, 1105, 1104, 1103, 1101, 1100, 1099, 1098, 1096, 1095,
1093, 1092, 1091, 1090, 1089, 1087, 1086, 1085, 1084, 1083, 1081, 1080, 1079, 1078, 1077, 1076,
1075, 1073, 1072, 1071, 1070, 1069, 1068, 1067, 1065, 1064, 1063, 1062, 1061, 1060, 1059, 1058,
1057, 1056, 1055, 1054, 1052, 1051, 1050, 1049, 1048, 1047, 1046, 1045, 1044, 1043, 1042, 1041,
1040, 1039, 1038, 1037, 1036, 1035, 1034, 1033, 1032, 1031, 1030, 1029, 1028, 1027, 1026, 1025,
];
const T2: [u8; 384] = [
7, 7, 6, 6, 5, 5, 4, 4, 4, 3, 3, 2, 2, 1, 1, 0, 6, 5, 5, 5, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 0, 0,
5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 4, 4, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0,
3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0,
3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
1, 1, 1, 1, 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,
];
pub fn limbs_reciprocal_sqrt(
out: &mut [Limb],
out_prec: u64,
mut xs: &[Limb],
x_prec: u64,
parity: bool,
) {
let out_len = bit_to_limb_count_ceiling(out_prec);
let out = &mut out[..out_len];
let mut xs_len = bit_to_limb_count_ceiling(x_prec);
assert!(xs[xs_len - 1].get_highest_bit());
assert!(out_prec >= 11);
if xs_len > out_len {
xs = &xs[xs_len - out_len..];
xs_len = out_len;
}
if out_prec == 11 {
let i =
usize::exact_from(xs[xs_len - 1] >> (const { Limb::WIDTH - 12 } - u64::from(parity)));
let ab = i >> 4;
let ac = (ab & 0x3f0) | i.mod_power_of_2(4);
let t = T1[ab - 0x80] + u16::from(T2[ac - 0x80]); out[0] = Limb::from(t) << (Limb::WIDTH - out_prec);
} else {
let h = if out_prec < 18 {
11
} else {
(out_prec >> 1) + 2
};
let xs_rec_len = bit_to_limb_count_ceiling(h);
let rn = bit_to_limb_count_ceiling(h << 1);
let ln = out_len - xs_rec_len; let hp = h + 1 + u64::from(parity);
let th = bit_to_limb_count_floor(hp - 3);
let mut ts_len = bit_to_limb_count_ceiling(hp + h);
let mut ahn = bit_to_limb_count_ceiling(hp);
if ahn > xs_len {
ahn = xs_len;
}
let (out_lo, out_hi) = out.split_at_mut(ln);
limbs_reciprocal_sqrt(
out_hi,
h,
&xs[xs_len - ahn..],
limb_to_bit_count(ahn),
parity,
);
let mut us_len = xs_rec_len + (ts_len - th);
assert!(xs_rec_len << 1 <= us_len);
let sn = xs_len + rn;
let mut scratch = vec![0; (us_len << 1) + sn];
split_into_chunks_mut!(scratch, us_len, [us, rs], ss);
let mut rs = rs;
if h << 1 <= Limb::WIDTH {
assert_eq!(ln, 0);
let cy = out_hi[0] >> HALF_WIDTH;
rs = &mut rs[1..];
rs[0] = cy.square();
} else if xs_rec_len == 1 {
(rs[1], rs[0]) = Limb::x_mul_y_to_zz(out_hi[0], out_hi[0]);
} else {
let mut scratch = vec![0; limbs_square_to_out_scratch_len(xs_rec_len)];
limbs_square_to_out(rs, out_hi, &mut scratch);
if rn < xs_rec_len << 1 {
rs = &mut rs[1..];
}
}
if rn == 1 {
assert_eq!(xs_len, 1);
(ss[1], ss[0]) = Limb::x_mul_y_to_zz(rs[0], xs[0]);
} else {
assert!(rn <= out_len + 1);
assert!(xs_len <= rn);
let mut scratch = vec![0; limbs_mul_greater_to_out_scratch_len(rn, xs_len)];
limbs_mul_greater_to_out(ss, &rs[..rn], xs, &mut scratch);
}
let ts = &mut ss[sn - ts_len..]; let bit = if parity {
HALF_LIMB_HIGH_BIT
} else {
LIMB_HIGH_BIT
};
let ts_last = ts.last_mut().unwrap();
let neg = *ts_last & bit;
if neg == 0 {
*ts_last |= LIMB_HIGH_BIT | bit;
limbs_not_in_place(ts);
} else {
*ts_last ^= neg;
limbs_slice_add_limb_in_place(ts, 1);
}
ts_len -= th;
assert_ne!(ts_len, 0);
assert!(ts_len >= xs_rec_len);
if ts_len == 1 {
(us[1], us[0]) = Limb::x_mul_y_to_zz(ts[0], out_hi[0]);
} else {
let mut scratch = vec![0; limbs_mul_greater_to_out_scratch_len(ts_len, xs_rec_len)];
limbs_mul_greater_to_out(us, &ts[..ts_len], out_hi, &mut scratch);
}
us_len = out_len - th;
assert_ne!(us_len, 0);
let u_offset = ts_len + xs_rec_len - us_len;
assert_ne!(u_offset, 0); if parity {
limbs_slice_shl_in_place(&mut us[u_offset - 1..u_offset + us_len], 1);
}
let shift_bit = Limb::power_of_2(limb_to_bit_count(out_len) - out_prec);
let (us_head, us_tail) = us[u_offset - 1..].split_first_mut().unwrap();
let us_tail = &mut us_tail[..us_len];
let cu = if shift_bit == 1 {
limbs_slice_add_limb_in_place(us_tail, *us_head >> WIDTH_MINUS_1)
} else {
let uu = us_tail[0];
let cu = limbs_slice_add_limb_in_place(us_tail, uu & (shift_bit >> 1));
us_tail[0] &= !(shift_bit - 1);
cu
};
assert!(!cu);
assert!(us_len > ln);
assert!(us_len == ln + 1 || us_len == ln + 2);
let mut carry;
let (us_lo, us_hi) = us_tail.split_at_mut(ln);
if neg == 0 {
if ln != 0 {
out_lo.copy_from_slice(us_lo);
}
carry = limbs_slice_add_greater_in_place_left(out_hi, us_hi);
} else {
carry = limbs_sub_greater_in_place_left(out_hi, us_hi);
assert!(!carry);
if ln != 0 {
limbs_not_to_out(out, us_lo);
carry = limbs_slice_add_limb_in_place(out, 1);
if limbs_sub_limb_in_place(&mut out[ln..], 1) {
assert!(carry);
carry = false;
}
}
}
if carry {
assert!(limbs_sub_limb_in_place(out, shift_bit));
}
}
}