-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Karatsuba added, 2 bugs are fixed #328
Changes from 4 commits
1716b7f
d17a3ac
3ad7f32
2da0648
74d1497
9184bd5
b4e4749
ed86a31
ded0125
1e71f29
8443fac
1e94538
c204747
a299003
bdb9ce6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -250,6 +250,22 @@ public interface Ring<T> : Group<T>, RingOperations<T> { | |
* neutral operation for multiplication | ||
*/ | ||
public val one: T | ||
|
||
public fun T.pow(exponent: ULong): T = when { | ||
this == zero && exponent > 0UL -> zero | ||
this == one -> this | ||
this == -one -> powWithoutOptimization(exponent % 2UL) | ||
else -> powWithoutOptimization(exponent) | ||
} | ||
|
||
private fun T.powWithoutOptimization(exponent: ULong): T = when (exponent) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The implementation details should not be present In generic algebra. Probably better to use extensions instead. Like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I completely agree with @altavir. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, it should be placed there (as an extension). Thank you! I'll fix it. |
||
0UL -> one | ||
1UL -> this | ||
else -> { | ||
val pre = powWithoutOptimization(exponent shr 1).let { it * it } | ||
if (exponent and 1UL == 0UL) pre else pre * this | ||
} | ||
} | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,8 +56,7 @@ public class BigInt internal constructor( | |
else -> sign * compareMagnitudes(magnitude, other.magnitude) | ||
} | ||
|
||
public override fun equals(other: Any?): Boolean = | ||
if (other is BigInt) compareTo(other) == 0 else error("Can't compare KBigInteger to a different type") | ||
public override fun equals(other: Any?): Boolean = other is BigInt && compareTo(other) == 0 | ||
|
||
public override fun hashCode(): Int = magnitude.hashCode() + sign | ||
|
||
|
@@ -87,20 +86,23 @@ public class BigInt internal constructor( | |
public operator fun times(b: BigInt): BigInt = when { | ||
this.sign == 0.toByte() -> ZERO | ||
b.sign == 0.toByte() -> ZERO | ||
// TODO: Karatsuba | ||
b.magnitude.size == 1 -> this * b.magnitude[0] * b.sign.toInt() | ||
this.magnitude.size == 1 -> b * this.magnitude[0] * this.sign.toInt() | ||
else -> BigInt((this.sign * b.sign).toByte(), multiplyMagnitudes(this.magnitude, b.magnitude)) | ||
} | ||
|
||
public operator fun times(other: UInt): BigInt = when { | ||
sign == 0.toByte() -> ZERO | ||
other == 0U -> ZERO | ||
other == 1U -> this | ||
else -> BigInt(sign, multiplyMagnitudeByUInt(magnitude, other)) | ||
} | ||
|
||
public operator fun times(other: Int): BigInt = if (other > 0) | ||
this * kotlin.math.abs(other).toUInt() | ||
else | ||
-this * kotlin.math.abs(other).toUInt() | ||
public operator fun times(other: Int): BigInt = when { | ||
other > 0 -> this * kotlin.math.abs(other).toUInt() | ||
other != Int.MIN_VALUE -> -this * kotlin.math.abs(other).toUInt() | ||
else -> times(other.toBigInt()) | ||
} | ||
|
||
public operator fun div(other: UInt): BigInt = BigInt(this.sign, divideMagnitudeByUInt(this.magnitude, other)) | ||
|
||
|
@@ -238,6 +240,7 @@ public class BigInt internal constructor( | |
public const val BASE_SIZE: Int = 32 | ||
public val ZERO: BigInt = BigInt(0, uintArrayOf()) | ||
public val ONE: BigInt = BigInt(1, uintArrayOf(1u)) | ||
private const val KARATSUBA_THRESHOLD = 80 | ||
|
||
private val hexMapping: HashMap<UInt, String> = hashMapOf( | ||
0U to "0", 1U to "1", 2U to "2", 3U to "3", | ||
|
@@ -318,7 +321,16 @@ public class BigInt internal constructor( | |
return stripLeadingZeros(result) | ||
} | ||
|
||
private fun multiplyMagnitudes(mag1: Magnitude, mag2: Magnitude): Magnitude { | ||
internal fun multiplyMagnitudes(mag1: Magnitude, mag2: Magnitude): Magnitude = when { | ||
mag1.size + mag2.size < KARATSUBA_THRESHOLD || mag1.isEmpty() || mag2.isEmpty() -> naiveMultiplyMagnitudes( | ||
mag1, | ||
mag2 | ||
) | ||
// TODO implement Fourier | ||
else -> karatsubaMultiplyMagnitudes(mag1, mag2) | ||
} | ||
|
||
internal fun naiveMultiplyMagnitudes(mag1: Magnitude, mag2: Magnitude): Magnitude { | ||
val resultLength = mag1.size + mag2.size | ||
val result = Magnitude(resultLength) | ||
|
||
|
@@ -337,6 +349,21 @@ public class BigInt internal constructor( | |
return stripLeadingZeros(result) | ||
} | ||
|
||
internal fun karatsubaMultiplyMagnitudes(mag1: Magnitude, mag2: Magnitude): Magnitude { | ||
//https://en.wikipedia.org/wiki/Karatsuba_algorithm | ||
val halfSize = min(mag1.size, mag2.size) / 2 | ||
val x0 = mag1.sliceArray(0 until halfSize).toBigInt(1) | ||
val x1 = mag1.sliceArray(halfSize until mag1.size).toBigInt(1) | ||
val y0 = mag2.sliceArray(0 until halfSize).toBigInt(1) | ||
val y1 = mag2.sliceArray(halfSize until mag2.size).toBigInt(1) | ||
|
||
val z0 = x0 * y0 | ||
val z2 = x1 * y1 | ||
val z1 = (x0 + x1) * (y1 + y0) - z0 - z2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will it be more optimal to use the other formula for z1, namely There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed it. |
||
|
||
return (z2.shl(2 * halfSize * BASE_SIZE) + z1.shl(halfSize * BASE_SIZE) + z0).magnitude | ||
} | ||
|
||
private fun divideMagnitudeByUInt(mag: Magnitude, x: UInt): Magnitude { | ||
val resultLength = mag.size | ||
val result = Magnitude(resultLength) | ||
|
@@ -427,7 +454,7 @@ private val hexChToInt: MutableMap<Char, Int> = hashMapOf( | |
public fun String.parseBigInteger(): BigInt? { | ||
val sign: Int | ||
val sPositive: String | ||
|
||
//TODO substring = O(n). Can be replaced by some drop that is O(1) | ||
when { | ||
this[0] == '+' -> { | ||
sign = +1 | ||
|
@@ -447,8 +474,10 @@ public fun String.parseBigInteger(): BigInt? { | |
var digitValue = BigInt.ONE | ||
val sPositiveUpper = sPositive.uppercase() | ||
|
||
if (sPositiveUpper.startsWith("0X")) { // hex representation | ||
if (sPositiveUpper.startsWith("0X")) { | ||
// hex representation | ||
val sHex = sPositiveUpper.substring(2) | ||
// TODO optimize O(n2) -> O(n) | ||
|
||
for (ch in sHex.reversed()) { | ||
if (ch == '_') continue | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there any real use-cases for
Long
power?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean negative exponent usage? If so, no. It is impossible to implement.
Or do you mean big range? If so, yes for some non-
bigint
rings. As we don't know what the rings are, we cannot say anything about the usage of exponents ofUInt.MAX_VALUE.toULong().inc()..ULog.MAX_VALUE