oxinum_int/native/
convert.rs1use super::int::BigInt;
13use super::uint::BigUint;
14use crate::OxiNumError;
15use oxinum_core::Sign;
16
17impl From<u8> for BigUint {
22 #[inline]
23 fn from(v: u8) -> Self {
24 BigUint::from_u64(v as u64)
25 }
26}
27
28impl From<u16> for BigUint {
29 #[inline]
30 fn from(v: u16) -> Self {
31 BigUint::from_u64(v as u64)
32 }
33}
34
35impl From<u32> for BigUint {
36 #[inline]
37 fn from(v: u32) -> Self {
38 BigUint::from_u64(v as u64)
39 }
40}
41
42impl From<u64> for BigUint {
43 #[inline]
44 fn from(v: u64) -> Self {
45 BigUint::from_u64(v)
46 }
47}
48
49impl From<u128> for BigUint {
50 #[inline]
51 fn from(v: u128) -> Self {
52 BigUint::from_u128(v)
53 }
54}
55
56impl From<usize> for BigUint {
57 #[inline]
58 fn from(v: usize) -> Self {
59 BigUint::from_u64(v as u64)
60 }
61}
62
63#[inline]
68fn from_signed<T>(value: T) -> BigInt
69where
70 T: SignedPrimitive,
71{
72 if value.is_negative_signed() {
73 BigInt {
74 sign: Sign::Negative,
75 mag: BigUint::from_u128(value.unsigned_abs_u128()),
76 }
77 .normalize_zero()
78 } else {
79 BigInt {
80 sign: Sign::Positive,
81 mag: BigUint::from_u128(value.unsigned_abs_u128()),
82 }
83 .normalize_zero()
84 }
85}
86
87#[inline]
88fn from_unsigned(value: u128) -> BigInt {
89 BigInt {
90 sign: Sign::Positive,
91 mag: BigUint::from_u128(value),
92 }
93 .normalize_zero()
94}
95
96trait SignedPrimitive {
101 fn is_negative_signed(&self) -> bool;
102 fn unsigned_abs_u128(&self) -> u128;
103}
104
105macro_rules! impl_signed_prim {
106 ($($t:ty),*) => {
107 $(
108 impl SignedPrimitive for $t {
109 #[inline]
110 fn is_negative_signed(&self) -> bool {
111 *self < 0
112 }
113 #[inline]
114 fn unsigned_abs_u128(&self) -> u128 {
115 self.unsigned_abs() as u128
116 }
117 }
118 )*
119 };
120}
121
122impl_signed_prim!(i8, i16, i32, i64, isize);
123
124impl SignedPrimitive for i128 {
126 #[inline]
127 fn is_negative_signed(&self) -> bool {
128 *self < 0
129 }
130 #[inline]
131 fn unsigned_abs_u128(&self) -> u128 {
132 self.unsigned_abs()
133 }
134}
135
136impl BigInt {
137 #[inline]
140 fn normalize_zero(mut self) -> Self {
141 if self.mag.is_zero() {
142 self.sign = Sign::Positive;
143 }
144 self
145 }
146}
147
148impl From<u8> for BigInt {
149 #[inline]
150 fn from(v: u8) -> Self {
151 from_unsigned(v as u128)
152 }
153}
154
155impl From<u16> for BigInt {
156 #[inline]
157 fn from(v: u16) -> Self {
158 from_unsigned(v as u128)
159 }
160}
161
162impl From<u32> for BigInt {
163 #[inline]
164 fn from(v: u32) -> Self {
165 from_unsigned(v as u128)
166 }
167}
168
169impl From<u64> for BigInt {
170 #[inline]
171 fn from(v: u64) -> Self {
172 from_unsigned(v as u128)
173 }
174}
175
176impl From<u128> for BigInt {
177 #[inline]
178 fn from(v: u128) -> Self {
179 from_unsigned(v)
180 }
181}
182
183impl From<usize> for BigInt {
184 #[inline]
185 fn from(v: usize) -> Self {
186 from_unsigned(v as u128)
187 }
188}
189
190impl From<i8> for BigInt {
191 #[inline]
192 fn from(v: i8) -> Self {
193 from_signed(v)
194 }
195}
196
197impl From<i16> for BigInt {
198 #[inline]
199 fn from(v: i16) -> Self {
200 from_signed(v)
201 }
202}
203
204impl From<i32> for BigInt {
205 #[inline]
206 fn from(v: i32) -> Self {
207 from_signed(v)
208 }
209}
210
211impl From<i64> for BigInt {
212 #[inline]
213 fn from(v: i64) -> Self {
214 from_signed(v)
215 }
216}
217
218impl From<i128> for BigInt {
219 #[inline]
220 fn from(v: i128) -> Self {
221 from_signed(v)
222 }
223}
224
225impl From<isize> for BigInt {
226 #[inline]
227 fn from(v: isize) -> Self {
228 from_signed(v)
229 }
230}
231
232impl From<BigUint> for BigInt {
233 #[inline]
234 fn from(m: BigUint) -> Self {
235 BigInt::from_parts(Sign::Positive, m)
236 }
237}
238
239impl From<&BigUint> for BigInt {
240 #[inline]
241 fn from(m: &BigUint) -> Self {
242 BigInt::from_parts(Sign::Positive, m.clone())
243 }
244}
245
246#[inline]
251fn biguint_to_u128(value: &BigUint) -> Result<u128, OxiNumError> {
252 let limbs = value.as_limbs();
253 match limbs.len() {
254 0 => Ok(0),
255 1 => Ok(limbs[0] as u128),
256 2 => Ok(((limbs[1] as u128) << 64) | (limbs[0] as u128)),
257 _ => Err(OxiNumError::Overflow(
258 format!("BigUint with {} limbs does not fit in u128", limbs.len()).into(),
259 )),
260 }
261}
262
263macro_rules! impl_try_from_biguint_unsigned {
264 ($($t:ident),*) => {
265 $(
266 impl TryFrom<&BigUint> for $t {
267 type Error = OxiNumError;
268 fn try_from(value: &BigUint) -> Result<Self, Self::Error> {
269 let v = biguint_to_u128(value)?;
270 if v > Self::MAX as u128 {
271 return Err(OxiNumError::Overflow(format!(
272 "BigUint {value} does not fit in {}",
273 stringify!($t)
274 ).into()));
275 }
276 Ok(v as Self)
277 }
278 }
279
280 impl TryFrom<BigUint> for $t {
281 type Error = OxiNumError;
282 fn try_from(value: BigUint) -> Result<Self, Self::Error> {
283 Self::try_from(&value)
284 }
285 }
286 )*
287 };
288}
289
290impl TryFrom<&BigUint> for u128 {
292 type Error = OxiNumError;
293 fn try_from(value: &BigUint) -> Result<Self, Self::Error> {
294 biguint_to_u128(value)
295 }
296}
297
298impl TryFrom<BigUint> for u128 {
299 type Error = OxiNumError;
300 fn try_from(value: BigUint) -> Result<Self, Self::Error> {
301 biguint_to_u128(&value)
302 }
303}
304
305impl_try_from_biguint_unsigned!(u8, u16, u32, u64, usize);
306
307macro_rules! impl_try_from_biguint_signed {
308 ($($t:ident),*) => {
309 $(
310 impl TryFrom<&BigUint> for $t {
311 type Error = OxiNumError;
312 fn try_from(value: &BigUint) -> Result<Self, Self::Error> {
313 let v = biguint_to_u128(value)?;
314 if v > Self::MAX as u128 {
315 return Err(OxiNumError::Overflow(format!(
316 "BigUint {value} does not fit in {}",
317 stringify!($t)
318 ).into()));
319 }
320 Ok(v as Self)
321 }
322 }
323
324 impl TryFrom<BigUint> for $t {
325 type Error = OxiNumError;
326 fn try_from(value: BigUint) -> Result<Self, Self::Error> {
327 Self::try_from(&value)
328 }
329 }
330 )*
331 };
332}
333
334impl TryFrom<&BigUint> for i128 {
336 type Error = OxiNumError;
337 fn try_from(value: &BigUint) -> Result<Self, Self::Error> {
338 let v = biguint_to_u128(value)?;
339 if v > i128::MAX as u128 {
340 return Err(OxiNumError::Overflow(
341 format!("BigUint {value} does not fit in i128").into(),
342 ));
343 }
344 Ok(v as i128)
345 }
346}
347
348impl TryFrom<BigUint> for i128 {
349 type Error = OxiNumError;
350 fn try_from(value: BigUint) -> Result<Self, Self::Error> {
351 Self::try_from(&value)
352 }
353}
354
355impl_try_from_biguint_signed!(i8, i16, i32, i64, isize);
356
357macro_rules! impl_try_from_bigint_unsigned {
362 ($($t:ident),*) => {
363 $(
364 impl TryFrom<&BigInt> for $t {
365 type Error = OxiNumError;
366 fn try_from(value: &BigInt) -> Result<Self, Self::Error> {
367 if value.is_negative() {
368 return Err(OxiNumError::Overflow(format!(
369 "negative BigInt {value} cannot fit in {}",
370 stringify!($t)
371 ).into()));
372 }
373 <$t>::try_from(value.magnitude())
374 }
375 }
376
377 impl TryFrom<BigInt> for $t {
378 type Error = OxiNumError;
379 fn try_from(value: BigInt) -> Result<Self, Self::Error> {
380 Self::try_from(&value)
381 }
382 }
383 )*
384 };
385}
386
387impl_try_from_bigint_unsigned!(u8, u16, u32, u64, u128, usize);
388
389macro_rules! impl_try_from_bigint_signed {
393 ($($t:ident),*) => {
394 $(
395 impl TryFrom<&BigInt> for $t {
396 type Error = OxiNumError;
397 fn try_from(value: &BigInt) -> Result<Self, Self::Error> {
398 let mag_u128 = biguint_to_u128(value.magnitude())?;
399 let max_pos = <$t>::MAX as u128;
400 let min_abs = (<$t>::MAX as u128) + 1; if value.is_negative() {
402 if mag_u128 > min_abs {
403 return Err(OxiNumError::Overflow(format!(
404 "BigInt {value} does not fit in {} (too negative)",
405 stringify!($t)
406 ).into()));
407 }
408 if mag_u128 == min_abs {
409 return Ok(<$t>::MIN);
410 }
411 let pos = mag_u128 as i128;
413 return Ok(-pos as $t);
414 }
415 if mag_u128 > max_pos {
416 return Err(OxiNumError::Overflow(format!(
417 "BigInt {value} does not fit in {} (too positive)",
418 stringify!($t)
419 ).into()));
420 }
421 Ok(mag_u128 as $t)
422 }
423 }
424
425 impl TryFrom<BigInt> for $t {
426 type Error = OxiNumError;
427 fn try_from(value: BigInt) -> Result<Self, Self::Error> {
428 Self::try_from(&value)
429 }
430 }
431 )*
432 };
433}
434
435impl_try_from_bigint_signed!(i8, i16, i32, i64, isize);
436
437impl TryFrom<&BigInt> for i128 {
439 type Error = OxiNumError;
440 fn try_from(value: &BigInt) -> Result<Self, Self::Error> {
441 let mag_u128 = biguint_to_u128(value.magnitude())?;
442 let max_pos = i128::MAX as u128;
443 let min_abs = (i128::MAX as u128) + 1; if value.is_negative() {
445 if mag_u128 > min_abs {
446 return Err(OxiNumError::Overflow(
447 format!("BigInt {value} does not fit in i128 (too negative)").into(),
448 ));
449 }
450 if mag_u128 == min_abs {
451 return Ok(i128::MIN);
452 }
453 return Ok(-(mag_u128 as i128));
454 }
455 if mag_u128 > max_pos {
456 return Err(OxiNumError::Overflow(
457 format!("BigInt {value} does not fit in i128 (too positive)").into(),
458 ));
459 }
460 Ok(mag_u128 as i128)
461 }
462}
463
464impl TryFrom<BigInt> for i128 {
465 type Error = OxiNumError;
466 fn try_from(value: BigInt) -> Result<Self, Self::Error> {
467 Self::try_from(&value)
468 }
469}
470
471#[cfg(test)]
472mod tests {
473 use super::*;
474
475 #[test]
476 fn from_i64_min_roundtrip() {
477 let n = BigInt::from(i64::MIN);
478 assert_eq!(format!("{n}"), format!("{}", i64::MIN));
479 let back: i64 = i64::try_from(&n).expect("i64::MIN fits in i64");
480 assert_eq!(back, i64::MIN);
481 }
482
483 #[test]
484 fn from_i128_min_roundtrip() {
485 let n = BigInt::from(i128::MIN);
486 let back: i128 = i128::try_from(&n).expect("i128::MIN fits in i128");
487 assert_eq!(back, i128::MIN);
488 }
489
490 #[test]
491 fn u64_max_roundtrip() {
492 let n = BigInt::from(u64::MAX);
493 let back: u64 = u64::try_from(&n).expect("u64::MAX fits in u64");
494 assert_eq!(back, u64::MAX);
495 }
496
497 #[test]
498 fn try_from_overflow_signed() {
499 let n = BigInt::from(u64::MAX);
500 assert!(i64::try_from(&n).is_err());
502 }
503
504 #[test]
505 fn try_from_negative_into_unsigned_errors() {
506 let n = BigInt::from(-1i64);
507 assert!(u32::try_from(&n).is_err());
508 }
509}