mirror of
				https://gitlab.com/artofrev/smallint.git
				synced 2025-10-31 06:51:12 -04:00 
			
		
		
		
	karatsuba multiplication: doesn't work yet
This commit is contained in:
		
							parent
							
								
									3510274fce
								
							
						
					
					
						commit
						4d6017b974
					
				
							
								
								
									
										313
									
								
								src/ops.rs
									
									
									
									
									
								
							
							
						
						
									
										313
									
								
								src/ops.rs
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| use crate::SmallUint; | ||||
| use crate::smallint::SmallUintType; | ||||
| use core::ops::Add; | ||||
| use core::ops::{Add, Sub, Mul}; | ||||
| use core::mem::ManuallyDrop; | ||||
| 
 | ||||
| macro_rules! basic_op { | ||||
| @ -83,12 +83,13 @@ fn add_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec<u32> { | ||||
|     if carry { | ||||
|         res.push(1); | ||||
|     } | ||||
|     while res[res.len() - 1] == 0 { | ||||
|     while res.len() != 1 && res[res.len() - 1] == 0 { | ||||
|         res.pop(); | ||||
|     } | ||||
|     res | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fn add(a: &SmallUint, b: &SmallUint) -> SmallUint { | ||||
| 
 | ||||
|     match (&a.0, &b.0) { | ||||
| @ -153,6 +154,103 @@ fn add(a: &SmallUint, b: &SmallUint) -> SmallUint { | ||||
| 
 | ||||
| basic_op!(Add, SmallUint, add); | ||||
| 
 | ||||
| fn sub_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec<u32> { | ||||
|     let b = slice1.len(); | ||||
|     let s = slice2.len(); | ||||
| 
 | ||||
|     if b < s { | ||||
|         panic!("First number is smaller than second."); | ||||
|     } | ||||
| 
 | ||||
|     let mut res = Vec::with_capacity(std::cmp::max(s, b)); | ||||
|     let mut borrow = false; | ||||
| 
 | ||||
|     for i in 0..b { | ||||
|         let mut value1 = slice1[i]; | ||||
| 
 | ||||
|         let value2 = if i < s { | ||||
|             slice2[i] | ||||
|         } else { | ||||
|             0 | ||||
|         }; | ||||
| 
 | ||||
|         if borrow { | ||||
|             let (temp, b) = value1.overflowing_sub(1); | ||||
|             value1 = temp; | ||||
|             borrow = b; | ||||
|         } | ||||
| 
 | ||||
|         if value2 > value1 { | ||||
|             borrow = true; | ||||
|         } | ||||
| 
 | ||||
|         let val = value1.wrapping_sub(value2); | ||||
|         res.push(val); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     if borrow { | ||||
|         panic!("First number is smaller than second. "); | ||||
|     } | ||||
| 
 | ||||
|     res | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| fn sub(a: &SmallUint, b: &SmallUint) -> SmallUint { | ||||
| 
 | ||||
|     match (&a.0, &b.0) { | ||||
|         (&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => { | ||||
|             if let (t, false) = i.overflowing_sub(j) { | ||||
|                 SmallUint(SmallUintType::Inline(t)) | ||||
|             } else { | ||||
|                 panic!("First number is smaller than second. "); | ||||
|             } | ||||
|         }, | ||||
|         (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) => { | ||||
|             let slice1 = unsafe { core::slice::from_raw_parts(r, s) }; | ||||
| 
 | ||||
|             let mut res = [0, 0, 0, 0]; | ||||
| 
 | ||||
|             let mut v = i; | ||||
|             #[allow(clippy::needless_range_loop)] | ||||
|             for r in 0..4 { | ||||
|                 res[r] = v as u32; | ||||
| 
 | ||||
|                 v >>= 32; | ||||
|             } | ||||
| 
 | ||||
|             let result = sub_two_slices(slice1, &res[..]); | ||||
|             let size = result.len(); | ||||
| 
 | ||||
|             let mut slice = ManuallyDrop::new(result.into_boxed_slice()); | ||||
| 
 | ||||
|             SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size))) | ||||
| 
 | ||||
| 
 | ||||
|         }, | ||||
|         (&SmallUintType::Inline(_), &SmallUintType::Heap((_, _))) => { | ||||
|             panic!("First number is smaller than second. "); | ||||
| 
 | ||||
|         }, | ||||
|         (&SmallUintType::Heap((r, s)), &SmallUintType::Heap((i, j))) => { | ||||
| 
 | ||||
|             let slice1 = unsafe { core::slice::from_raw_parts(r, s) }; | ||||
|             let slice2 = unsafe { core::slice::from_raw_parts(i, j) }; | ||||
| 
 | ||||
|             let res = sub_two_slices(slice1, slice2); | ||||
|             let size = res.len(); | ||||
| 
 | ||||
|             let mut slice = ManuallyDrop::new(res.into_boxed_slice()); | ||||
| 
 | ||||
|             SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size))) | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| basic_op!(Sub, SmallUint, sub); | ||||
| 
 | ||||
| // Taken from https://github.com/rust-lang/rust/issues/85532#issuecomment-916309635. Credit to
 | ||||
| // AaronKutch.
 | ||||
| const fn carrying_mul_u128(lhs: u128, rhs: u128, carry: u128) -> (u128, u128) { | ||||
| @ -189,55 +287,162 @@ const fn carrying_mul_u128(lhs: u128, rhs: u128, carry: u128) -> (u128, u128) { | ||||
|     (sum0, sum1) | ||||
| } | ||||
| 
 | ||||
| // fn mul_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec<u32> {
 | ||||
| // 
 | ||||
| //     let l1 = slice1.len();
 | ||||
| //     let l2 = slice2.len();
 | ||||
| // 
 | ||||
| // 
 | ||||
| // }
 | ||||
| // 
 | ||||
| // 
 | ||||
| // fn mul(a: &SmallUint, b: &SmallUint) -> SmallUint {
 | ||||
| //     match (&a.0, &b.0) {
 | ||||
| //         (&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => {
 | ||||
| //             match carrying_mul_u128(i, j, 0) {
 | ||||
| //                 (t, 0) => SmallUint(SmallUintType::Inline(t)),
 | ||||
| //                 (t, o) => {
 | ||||
| //                     let mut res = Vec::with_capacity(8);
 | ||||
| // 
 | ||||
| //                     let mut v = t;
 | ||||
| //                     #[allow(clippy::needless_range_loop)]
 | ||||
| //                     for _ in 0..4 {
 | ||||
| //                         res.push(v as u32);
 | ||||
| // 
 | ||||
| //                         v >>= 32;
 | ||||
| //                     }
 | ||||
| // 
 | ||||
| //                     let mut v = o;
 | ||||
| //                     for _ in 4..8 {
 | ||||
| //                         res.push(v as u32);
 | ||||
| // 
 | ||||
| //                         v >>= 32;
 | ||||
| //                     }
 | ||||
| // 
 | ||||
| //                     while res[res.len() - 1] == 0 {
 | ||||
| //                         res.pop();
 | ||||
| //                     }
 | ||||
| // 
 | ||||
| //                     let size = res.len();
 | ||||
| // 
 | ||||
| //                     let mut slice = ManuallyDrop::new(res.into_boxed_slice());
 | ||||
| // 
 | ||||
| //                     SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size)))
 | ||||
| // 
 | ||||
| //                 }
 | ||||
| //             }
 | ||||
| //         },
 | ||||
| //         (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) | (&SmallUintType::Inline(i), &SmallUintType::Heap((r, s))) => {
 | ||||
| // 
 | ||||
| //         }
 | ||||
| //     }
 | ||||
| // }
 | ||||
| // 
 | ||||
| // 
 | ||||
| fn mul_two_slices(slice1: &[u32], slice2: &[u32]) -> Vec<u32> { | ||||
| 
 | ||||
|     // https://en.wikipedia.org/wiki/Karatsuba_algorithm
 | ||||
| 
 | ||||
|     let l1 = slice1.len(); | ||||
|     let l2 = slice2.len(); | ||||
| 
 | ||||
| 
 | ||||
|     if l1 == 0 || l2 == 0 { | ||||
|         return vec![]; | ||||
|     } else if l1 == 1 { | ||||
|         let mut overflow = 0; | ||||
|         let mut res: Vec<u32> = Vec::with_capacity(l2 + 1); | ||||
| 
 | ||||
|         #[allow(clippy::needless_range_loop)] | ||||
|         for i in 0..l2 { | ||||
|             let mut r = (slice2[i] as u64) * (slice1[0] as u64); | ||||
|             r += overflow as u64; | ||||
|             let m = r as u32; | ||||
|             overflow = (r >> 32) as u32; | ||||
|             res.push(m); | ||||
| 
 | ||||
|         } 
 | ||||
| 
 | ||||
|         if overflow != 0 { | ||||
|             res.push(overflow); | ||||
|         } | ||||
| 
 | ||||
|         return res; | ||||
|     } else if l2 == 1 { | ||||
| 
 | ||||
|         let mut overflow = 0; | ||||
|         let mut res: Vec<u32> = Vec::with_capacity(l2 + 1); | ||||
| 
 | ||||
|         #[allow(clippy::needless_range_loop)] | ||||
|         for i in 0..l1 { | ||||
|             let mut r = (slice1[i] as u64) * (slice2[0] as u64); | ||||
|             r += overflow as u64; | ||||
|             let m = r as u32; | ||||
|             overflow = (r >> 32) as u32; | ||||
|             res.push(m); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|         if overflow != 0 { | ||||
|             res.push(overflow); | ||||
|         } | ||||
| 
 | ||||
|         return res; | ||||
|     } | ||||
| 
 | ||||
|     let m = std::cmp::min(l1, l2); | ||||
|     let m2 = (m as u32) / 2; | ||||
| 
 | ||||
|     let (low1, high1) = slice1.split_at(m2 as usize); | ||||
|     let (low2, high2) = slice2.split_at(m2 as usize); | ||||
| 
 | ||||
|     println!("z0: {:?}, {:?}", low1, low2); | ||||
|     println!("z1: {:?}, {:?}", &add_two_slices(low1, high1), &add_two_slices(low2, high2)); | ||||
|     println!("z2: {:?}, {:?}", high1, high2); | ||||
| 
 | ||||
|     let z0 = mul_two_slices(low1, low2); | ||||
|     let z1 = mul_two_slices(&add_two_slices(low1, high1), &add_two_slices(low2, high2)); | ||||
|     let z2 = mul_two_slices(high1, high2); | ||||
| 
 | ||||
|     let mut op0 = z0.clone(); | ||||
| 
 | ||||
|     for _ in 0..(m2 * 2) { | ||||
|         op0.insert(0, 0); | ||||
|     } | ||||
| 
 | ||||
|     let mut op1 = sub_two_slices(&sub_two_slices(&z1, &z2), &z0); | ||||
|     
 | ||||
|     for _ in 0..m2 { | ||||
|         op1.insert(0, 0); | ||||
|     } | ||||
| 
 | ||||
|     add_two_slices(&add_two_slices(&op0, &op1), &z0) | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| fn mul(a: &SmallUint, b: &SmallUint) -> SmallUint { | ||||
|     match (&a.0, &b.0) { | ||||
|         (&SmallUintType::Inline(i), &SmallUintType::Inline(j)) => { | ||||
|             match carrying_mul_u128(i, j, 0) { | ||||
|                 (t, 0) => SmallUint(SmallUintType::Inline(t)), | ||||
|                 (t, o) => { | ||||
|                     let mut res = Vec::with_capacity(8); | ||||
| 
 | ||||
|                     let mut v = t; | ||||
|                     #[allow(clippy::needless_range_loop)] | ||||
|                     for _ in 0..4 { | ||||
|                         res.push(v as u32); | ||||
| 
 | ||||
|                         v >>= 32; | ||||
|                     } | ||||
| 
 | ||||
|                     let mut v = o; | ||||
|                     for _ in 4..8 { | ||||
|                         res.push(v as u32); | ||||
| 
 | ||||
|                         v >>= 32; | ||||
|                     } | ||||
| 
 | ||||
|                     while res[res.len() - 1] == 0 { | ||||
|                         res.pop(); | ||||
|                     } | ||||
| 
 | ||||
|                     let size = res.len(); | ||||
| 
 | ||||
|                     let mut slice = ManuallyDrop::new(res.into_boxed_slice()); | ||||
| 
 | ||||
|                     SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size))) | ||||
| 
 | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
| 
 | ||||
|         (&SmallUintType::Heap((r, s)), &SmallUintType::Inline(i)) | (&SmallUintType::Inline(i), &SmallUintType::Heap((r, s))) => { | ||||
|             let slice1 = unsafe { core::slice::from_raw_parts(r, s) }; | ||||
| 
 | ||||
|             let mut res = [0, 0, 0, 0]; | ||||
| 
 | ||||
|             let mut v = i; | ||||
|             #[allow(clippy::needless_range_loop)] | ||||
|             for r in 0..4 { | ||||
|                 res[r] = v as u32; | ||||
| 
 | ||||
|                 v >>= 32; | ||||
|             } | ||||
| 
 | ||||
|             let result = mul_two_slices(slice1, &res[..]); | ||||
|             let size = result.len(); | ||||
| 
 | ||||
|             let mut slice = ManuallyDrop::new(result.into_boxed_slice()); | ||||
| 
 | ||||
|             SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size))) | ||||
| 
 | ||||
| 
 | ||||
|         }, | ||||
|         (&SmallUintType::Heap((r, s)), &SmallUintType::Heap((i, j))) => { | ||||
| 
 | ||||
|             let slice1 = unsafe { core::slice::from_raw_parts(r, s) }; | ||||
|             let slice2 = unsafe { core::slice::from_raw_parts(i, j) }; | ||||
| 
 | ||||
|             let res = mul_two_slices(slice1, slice2); | ||||
|             let size = res.len(); | ||||
| 
 | ||||
|             let mut slice = ManuallyDrop::new(res.into_boxed_slice()); | ||||
| 
 | ||||
|             SmallUint(SmallUintType::Heap((slice.as_mut_ptr(), size))) | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| basic_op!(Mul, SmallUint, mul); | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										46
									
								
								src/tests.rs
									
									
									
									
									
								
							
							
						
						
									
										46
									
								
								src/tests.rs
									
									
									
									
									
								
							| @ -45,12 +45,54 @@ fn test_op_add_u_u() { | ||||
|     let q = i + k; | ||||
|     assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) + u128::MAX); | ||||
| 
 | ||||
|     let i = SmallUint::from(&BigUint::new(vec![3, 9, 8])); | ||||
|     let i = SmallUint::from(&BigUint::new(vec![3, 9, 8, 3, 1])); | ||||
|     let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); | ||||
|     let q = i + k; | ||||
|     assert_eq!(BigUint::from(&q), BigUint::new(vec![3, 9, 8]) + BigUint::new(vec![5, 4, 9, 3, 1, 81])); | ||||
|     assert_eq!(BigUint::from(&q), BigUint::new(vec![3, 9, 8, 3, 1]) + BigUint::new(vec![5, 4, 9, 3, 1, 81])); | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| #[cfg(feature = "num-bigint")] | ||||
| fn test_op_mul_u_u() { | ||||
|     let i = SmallUint::from(u128::MAX); | ||||
|     let k = SmallUint::from(u128::MAX); | ||||
|     let q = i * k; | ||||
|     assert_eq!(BigUint::from(&q), BigUint::from(u128::MAX) * u128::MAX); | ||||
| 
 | ||||
| 
 | ||||
|     let i = SmallUint::from(u32::MAX); | ||||
|     let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); | ||||
|     let q = i * k; | ||||
|     assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) * u32::MAX); | ||||
| 
 | ||||
|     let i = SmallUint::from(&BigUint::new(vec![3, 9, 8, 3, 1])); | ||||
|     let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); | ||||
|     let q = i * k; | ||||
|     assert_eq!(BigUint::from(&q), BigUint::new(vec![3, 9, 8, 3, 1]) * BigUint::new(vec![5, 4, 9, 3, 1, 81])); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #[test] | ||||
| #[cfg(feature = "num-bigint")] | ||||
| fn test_op_sub_u_u() { | ||||
|     let i = SmallUint::from(u128::MAX); | ||||
|     let k = SmallUint::from(u128::MAX); | ||||
|     let q = i - k; | ||||
|     assert_eq!(BigUint::from(&q), BigUint::from(u128::MAX) - u128::MAX); | ||||
| 
 | ||||
|     let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); | ||||
|     let i = SmallUint::from(u128::MAX); | ||||
|     let q = k - i; | ||||
|     assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) - u128::MAX); | ||||
| 
 | ||||
|     let k = SmallUint::from(&BigUint::new(vec![5, 4, 9, 3, 1, 81])); | ||||
|     let i = SmallUint::from(&BigUint::new(vec![3, 9, 8, 3, 1])); | ||||
|     let q = k - i; | ||||
|     assert_eq!(BigUint::from(&q), BigUint::new(vec![5, 4, 9, 3, 1, 81]) - BigUint::new(vec![3, 9, 8, 3, 1])); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[test] | ||||
| #[cfg(feature = "num-bigint")] | ||||
| fn test_bigint() { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user