Расширенная реализация алгоритма GCD Лемера
Делая мою собственную реализацию BigInteger, я застрял с расширенным алгоритмом GCD, который является фундаментальным для нахождения модульного мультипликативного обратного. Поскольку хорошо известный евклидов подход подходит слишком медленно, а гибридные и бинарные алгоритмы только в 5-10 раз быстрее, выбор был за модификацией Лемера к классическому алгоритму. Но трудность заключается в том, что, когда дело доходит до описания Лемера, все книги, которые я нашел (Кнут, Руководство по прикладной криптографии, Интернет и т. Д.), Имеют те же недостатки:
- Объяснение основано на нескольких приемах:
- вводимые числа всегда имеют одинаковую длину;
- абстрактный ЦП имеет подписанные регистры, которые могут содержать как цифру, так и знак;
- абстрактный ЦП имеет полуограниченные регистры, т.е. он никогда не переполняется.
- Предоставляется только основной алгоритм GCD, без фокусировки на обратные кофакторы.
Что касается первой проблемы, я был изначально удивлен тем, что не смог найти какую-либо реальную реализацию (не указывайте мне библиотеку GNU MP - это не источник для изучения), но, наконец, вдохновился декомпиляцией реализации Microsoft из.Net 4.0, которая, очевидно, основана на идеях из статьи " Двузначный алгоритм Лемера-Евклида для нахождения ГКД длинных целых чисел " Джебелиана. Результирующая функция большая, выглядит страшно, но работает просто отлично.
Но библиотека Microsoft предоставляет только базовую функциональность, кофакторы не вычисляются. Ну, если быть точным, некоторые кофакторы вычисляются на этапе сокращения, и на самом первом этапе эти кофакторы просто являются исходными, но после выполнения этапа сокращения они больше не совпадают. Мое текущее решение состоит в том, чтобы обновлять "настоящие" кофакторы параллельно с "замещающими" (кроме самого первого шага), но при этом производительность падает ниже нуля: функция теперь завершается только на 25-50 % быстрее, чем двоичный метод в основном режиме. Таким образом, проблема заключается в том, что, хотя входные числа полностью обновляются только во время этапных шагов, кофакторы также обновляются при каждой итерации каждого сокращенного шага, тем самым уничтожая практически любую выгоду от подхода Лемера.
Чтобы немного ускорить процесс, я реализовал функцию "плавного умножения-сложения", потому что "плавное умножение-умножение-вычитание" действительно помогает обновить входные числа, но на этот раз влияние было незначительным. Другое усовершенствование основано на том факте, что обычно необходим только один кофактор, поэтому другой может вообще не вычисляться. Это должно вдвое сократить накладные расходы (или даже больше, поскольку второе число обычно значительно меньше первого), однако на практике накладные расходы уменьшаются только на 25-50 % от ожидаемого.
Следовательно, мои вопросы сводятся к этому:
- Есть ли полномасштабное объяснение алгоритма Лемера, связанное с практической реализацией на реальном оборудовании (с неподписанными словами ограниченного размера)?
- То же, что и выше, но в отношении расширенного вычисления GCD.
Итак, насколько я доволен производительностью базового алгоритма, обратное относится к расширенному режиму работы, который является основным в моем случае.
1 ответ
Наконец, я проконсультировался с математиком, и он быстро нашел правильные формулы - очень похожие на те, которые я пробовал сам, но немного по-другому. Это позволило обновлять кофакторы только на длинных шагах, в то время как входные номера полностью обновляются.
Однако, к моему большому удивлению, одна эта мера оказала незначительное влияние на производительность. Только когда я повторно реализовал его как "сплавленный (A×X + B×Y)", улучшение скорости стало заметным. При вычислении обоих кофакторов он теперь работает на 56 % для 5-значных чисел и 34 % для 32-значных чисел по сравнению с чистым алгоритмом Lehmer GCD; для одного кофактора ставка составляет 70% и 52% соответственно. В предыдущих реализациях оно составляло всего лишь 33–7% для обоих кофакторов и 47–14% для одного кофактора, поэтому моя неудовлетворенность была очевидной.
Что касается написания статьи, рекомендованной andy256, чтобы у других разработчиков не было таких же проблем, это будет нелегко. Я уже написал "небольшую" статью, когда объяснял свою текущую реализацию математикам, и она довольно быстро превысила три листа формата А4 - хотя содержала только основные идеи, без подробных объяснений, проверок переполнения, ветвления, развертывания и т. Д. Теперь я частично понять, почему Кнут и другие использовали грязные уловки, чтобы сделать историю короткой. В настоящее время я понятия не имею, как достичь того же уровня простоты, не теряя при этом тщательности; возможно, для этого потребуется несколько больших блок-схем в сочетании с комментариями.
Обновление Похоже, я не смогу завершить и опубликовать библиотеку в ближайшем будущем (все еще не повезло в понимании деления Ньютона - Рафсона и сокращения Монтгомери), поэтому я просто опубликую полученную функцию для тех, кто заинтересован.
Код не включает в себя очевидные функции, такие как ComputeGCD_Euclid
и процедуры общего назначения, такие как ComputeDivisionLonghand
, В коде также отсутствуют какие-либо комментарии (за редким исключением) - вы должны быть уже знакомы с идеей Лемера в целом и двузначной техникой, упомянутой выше, если вы хотите понять ее и интегрировать в свою собственную библиотеку.
Обзор представления чисел в моей библиотеке. Цифры представляют собой 32-разрядные целые числа без знака, поэтому при необходимости можно использовать 64-разрядную арифметику без знака и со знаком. Цифры хранятся в простом массиве (ValueDigits
) от наименее до наиболее значимого (LSB), фактический размер хранится в явном виде (ValueLength
), т.е. функции пытаются предсказать размер результата, но не оптимизируют потребление памяти после вычисления. Объекты имеют тип значения (struct
в.Net), но они ссылаются на массивы цифр; следовательно, объекты инвариантны, т.е. a = a + 1
создает новый объект вместо того, чтобы изменять существующий.
Public Shared Function ComputeGCD(ByVal uLeft As BigUInteger, ByVal uRight As BigUInteger,
ByRef uLeftInverse As BigUInteger, ByRef uRightInverse As BigUInteger, ByVal fComputeLeftInverse As Boolean, ByVal fComputeRightInverse As Boolean) As BigUInteger
Dim fSwap As Boolean = False
Select Case uLeft.CompareTo(uRight)
Case 0
uLeftInverse = Instance.Zero : uRightInverse = Instance.One : Return uRight
Case Is < 0
fSwap = fComputeLeftInverse : fComputeLeftInverse = fComputeRightInverse : fComputeRightInverse = fSwap
fSwap = True : Swap(uLeft, uRight)
End Select
Dim uResult As BigUInteger
If (uLeft.ValueLength = 1) AndAlso (uRight.ValueLength = 1) Then
Dim wLeftInverse As UInt32, wRightInverse As UInt32
uResult = ComputeGCD_Euclid(uLeft.DigitLowest, uRight.DigitLowest, wLeftInverse, wRightInverse)
uLeftInverse = wLeftInverse : uRightInverse = wRightInverse
ElseIf uLeft.ValueLength <= 2 Then
uResult = ComputeGCD_Euclid(uLeft, uRight, uLeftInverse, uRightInverse)
Else
uResult = ComputeGCD_Lehmer(uLeft, uRight, uLeftInverse, uRightInverse, fComputeLeftInverse, fComputeRightInverse)
End If
If fSwap Then Swap(uLeftInverse, uRightInverse)
Return uResult
End Function
Private Shared Function ComputeGCD_Lehmer(ByVal uLeft As BigUInteger, ByVal uRight As BigUInteger,
ByRef uLeftInverse As BigUInteger, ByRef uRightInverse As BigUInteger, ByVal fComputeLeftInverse As Boolean, ByVal fComputeRightInverse As Boolean) As BigUInteger
Dim uLeftCur As BigUInteger = uLeft, uRightCur As BigUInteger = uRight
Dim uLeftInvPrev As BigUInteger = Instance.One, uRightInvPrev As BigUInteger = Instance.Zero,
uLeftInvCur As BigUInteger = uRightInvPrev, uRightInvCur As BigUInteger = uLeftInvPrev,
fInvInit As Boolean = False, fIterationIsEven As Boolean = True
Dim dwLeftCur, dwRightCur As UInt64
Dim wLeftInvPrev, wRightInvPrev, wLeftInvCur, wRightInvCur As UInt32
Dim dwNumeratorMore, dwNumeratorLess, dwDenominatorMore, dwDenominatorLess, dwQuotientMore, dwQuotientLess As UInt64,
wQuotient As UInt32
Const nSubtractionThresholdBits As Byte = (5 - 1)
Dim ndxDigitMax As Integer, fRightIsShorter As Boolean
Dim fResultFound As Boolean = False
Dim uRemainder As BigUInteger = uRightCur, uQuotient As BigUInteger
Dim uTemp As BigUInteger = Nothing, dwTemp, dwTemp2 As UInt64
Do While uLeftCur.ValueLength > 2
ndxDigitMax = uLeftCur.ValueLength - 1 : fRightIsShorter = (uRightCur.ValueLength < uLeftCur.ValueLength)
Dim fShorthandStep As Boolean = True, fShorthandIterationIsEven As Boolean
If fRightIsShorter AndAlso (uLeftCur.ValueLength - uRightCur.ValueLength > 1) Then fShorthandStep = False
If fShorthandStep Then
dwLeftCur = uLeftCur.ValueDigits(ndxDigitMax - 1) Or (CULng(uLeftCur.ValueDigits(ndxDigitMax)) << DigitSize.Bits)
dwRightCur = uRightCur.ValueDigits(ndxDigitMax - 1) Or If(fRightIsShorter, DigitValue.Zero, CULng(uRightCur.ValueDigits(ndxDigitMax)) << DigitSize.Bits)
If ndxDigitMax >= 2 Then
Dim nNormHead As Byte = GetNormalizationHead(uLeftCur.ValueDigits(ndxDigitMax))
If nNormHead <> ByteValue.Zero Then
dwLeftCur = (dwLeftCur << nNormHead) Or (uLeftCur.ValueDigits(ndxDigitMax - 2) >> (DigitSize.Bits - nNormHead))
dwRightCur = (dwRightCur << nNormHead) Or (uRightCur.ValueDigits(ndxDigitMax - 2) >> (DigitSize.Bits - nNormHead))
End If
End If
If CUInt(dwRightCur >> DigitSize.Bits) = DigitValue.Zero Then fShorthandStep = False
End If
If fShorthandStep Then
' First iteration, where overflow may occur in general formulae.
If dwLeftCur = dwRightCur Then
fShorthandStep = False
Else
If dwLeftCur = DoubleValue.Full Then dwLeftCur >>= 1 : dwRightCur >>= 1
dwDenominatorMore = dwRightCur : dwDenominatorLess = dwRightCur + DigitValue.One
dwNumeratorMore = dwLeftCur + DigitValue.One : dwNumeratorLess = dwLeftCur
If (dwNumeratorMore >> nSubtractionThresholdBits) <= dwDenominatorMore Then
wQuotient = DigitValue.Zero
Do
wQuotient += DigitValue.One : dwNumeratorMore -= dwDenominatorMore
Loop While dwNumeratorMore >= dwDenominatorMore
dwQuotientMore = wQuotient
Else
dwQuotientMore = dwNumeratorMore \ dwDenominatorMore
If dwQuotientMore >= DigitValue.BitHi Then fShorthandStep = False
wQuotient = CUInt(dwQuotientMore)
End If
If fShorthandStep Then
If (dwNumeratorLess >> nSubtractionThresholdBits) <= dwDenominatorLess Then
wQuotient = DigitValue.Zero
Do
wQuotient += DigitValue.One : dwNumeratorLess -= dwDenominatorLess
Loop While dwNumeratorLess >= dwDenominatorLess
dwQuotientLess = wQuotient
Else
dwQuotientLess = dwNumeratorLess \ dwDenominatorLess
End If
If dwQuotientMore <> dwQuotientLess Then fShorthandStep = False
End If
End If
End If
If fShorthandStep Then
' Prepare for the second iteration.
wLeftInvPrev = DigitValue.Zero : wLeftInvCur = DigitValue.One
wRightInvPrev = DigitValue.One : wRightInvCur = wQuotient
dwTemp = dwLeftCur - wQuotient * dwRightCur : dwLeftCur = dwRightCur : dwRightCur = dwTemp
fShorthandIterationIsEven = True
fIterationIsEven = Not fIterationIsEven
' Other iterations, no overflow possible(?).
Do
If fShorthandIterationIsEven Then
If dwRightCur = wRightInvCur Then Exit Do
dwDenominatorMore = dwRightCur - wRightInvCur : dwDenominatorLess = dwRightCur + wLeftInvCur
dwNumeratorMore = dwLeftCur + wRightInvPrev : dwNumeratorLess = dwLeftCur - wLeftInvPrev
Else
If dwRightCur = wLeftInvCur Then Exit Do
dwDenominatorMore = dwRightCur - wLeftInvCur : dwDenominatorLess = dwRightCur + wRightInvCur
dwNumeratorMore = dwLeftCur + wLeftInvPrev : dwNumeratorLess = dwLeftCur - wRightInvPrev
End If
If (dwNumeratorMore >> nSubtractionThresholdBits) <= dwDenominatorMore Then
wQuotient = DigitValue.Zero
Do
wQuotient += DigitValue.One : dwNumeratorMore -= dwDenominatorMore
Loop While dwNumeratorMore >= dwDenominatorMore
dwQuotientMore = wQuotient
Else
dwQuotientMore = dwNumeratorMore \ dwDenominatorMore
If dwQuotientMore >= DigitValue.BitHi Then Exit Do
wQuotient = CUInt(dwQuotientMore)
End If
If (dwNumeratorLess >> nSubtractionThresholdBits) <= dwDenominatorLess Then
wQuotient = DigitValue.Zero
Do
wQuotient += DigitValue.One : dwNumeratorLess -= dwDenominatorLess
Loop While dwNumeratorLess >= dwDenominatorLess
dwQuotientLess = wQuotient
Else
dwQuotientLess = dwNumeratorLess \ dwDenominatorLess
End If
If dwQuotientMore <> dwQuotientLess Then Exit Do
dwTemp = wLeftInvPrev + wQuotient * wLeftInvCur : dwTemp2 = wRightInvPrev + wQuotient * wRightInvCur
If (dwTemp >= DigitValue.BitHi) OrElse (dwTemp2 >= DigitValue.BitHi) Then Exit Do
wLeftInvPrev = wLeftInvCur : wLeftInvCur = CUInt(dwTemp)
wRightInvPrev = wRightInvCur : wRightInvCur = CUInt(dwTemp2)
dwTemp = dwLeftCur - wQuotient * dwRightCur : dwLeftCur = dwRightCur : dwRightCur = dwTemp
fShorthandIterationIsEven = Not fShorthandIterationIsEven
fIterationIsEven = Not fIterationIsEven
Loop
End If
If (Not fShorthandStep) OrElse (wRightInvPrev = DigitValue.Zero) Then
' Longhand step.
uQuotient = ComputeDivisionLonghand(uLeftCur, uRightCur, uTemp) : If uTemp.IsZero Then fResultFound = True : Exit Do
uRemainder = uTemp
fIterationIsEven = Not fIterationIsEven
If fComputeLeftInverse Then
uTemp = uLeftInvPrev + uQuotient * uLeftInvCur : uLeftInvPrev = uLeftInvCur : uLeftInvCur = uTemp
End If
If fComputeRightInverse Then
uTemp = uRightInvPrev + uQuotient * uRightInvCur : uRightInvPrev = uRightInvCur : uRightInvCur = uTemp
End If
fInvInit = True
uLeftCur = uRightCur : uRightCur = uRemainder
Else
' Shorthand step finalization.
If Not fInvInit Then
If fComputeLeftInverse Then uLeftInvPrev = wLeftInvPrev : uLeftInvCur = wLeftInvCur
If fComputeRightInverse Then uRightInvPrev = wRightInvPrev : uRightInvCur = wRightInvCur
fInvInit = True
Else
If fComputeLeftInverse Then ComputeFusedMulMulAdd(uLeftInvPrev, uLeftInvCur, wLeftInvPrev, wLeftInvCur, wRightInvPrev, wRightInvCur)
If fComputeRightInverse Then ComputeFusedMulMulAdd(uRightInvPrev, uRightInvCur, wLeftInvPrev, wLeftInvCur, wRightInvPrev, wRightInvCur)
End If
ComputeFusedMulMulSub(uLeftCur, uRightCur, wLeftInvPrev, wLeftInvCur, wRightInvPrev, wRightInvCur, fShorthandIterationIsEven)
End If
Loop
' Final rounds: numbers are quite short now.
If Not fResultFound Then
ndxDigitMax = uLeftCur.ValueLength - 1 : fRightIsShorter = (uRightCur.ValueLength < uLeftCur.ValueLength)
If ndxDigitMax = 0 Then
dwLeftCur = uLeftCur.ValueDigits(0)
dwRightCur = uRightCur.ValueDigits(0)
Else
dwLeftCur = uLeftCur.ValueDigits(0) Or (CULng(uLeftCur.ValueDigits(1)) << DigitSize.Bits)
dwRightCur = uRightCur.ValueDigits(0) Or If(fRightIsShorter, DigitValue.Zero, CULng(uRightCur.ValueDigits(1)) << DigitSize.Bits)
End If
Do While dwLeftCur >= DigitValue.BitHi
Dim dwRemainder As UInt64 = dwLeftCur
If (dwRemainder >> nSubtractionThresholdBits) <= dwRightCur Then
wQuotient = DigitValue.Zero
Do
wQuotient += DigitValue.One : dwRemainder -= dwRightCur
Loop While dwRemainder >= dwRightCur
dwQuotientMore = wQuotient
Else
dwQuotientMore = dwLeftCur \ dwRightCur
dwRemainder = dwLeftCur - dwQuotientMore * dwRightCur
End If
If dwRemainder = DigitValue.Zero Then fResultFound = True : Exit Do
fIterationIsEven = Not fIterationIsEven
If dwQuotientMore < DigitValue.BitHi Then
wQuotient = CUInt(dwQuotientMore)
If fComputeLeftInverse Then ComputeFusedMulAdd(uLeftInvPrev, uLeftInvCur, wQuotient)
If fComputeRightInverse Then ComputeFusedMulAdd(uRightInvPrev, uRightInvCur, wQuotient)
Else
If fComputeLeftInverse Then
uTemp = uLeftInvPrev + dwQuotientMore * uLeftInvCur : uLeftInvPrev = uLeftInvCur : uLeftInvCur = uTemp
End If
If fComputeRightInverse Then
uTemp = uRightInvPrev + dwQuotientMore * uRightInvCur : uRightInvPrev = uRightInvCur : uRightInvCur = uTemp
End If
End If
dwLeftCur = dwRightCur : dwRightCur = dwRemainder
Loop
If fResultFound Then
uRightCur = dwRightCur
Else
' Final rounds: both numbers have only one digit now, and this digit has MS-bit unset.
Dim wLeftCur As UInt32 = CUInt(dwLeftCur), wRightCur As UInt32 = CUInt(dwRightCur)
Do
Dim wRemainder As UInt32 = wLeftCur
If (wRemainder >> nSubtractionThresholdBits) <= wRightCur Then
wQuotient = DigitValue.Zero
Do
wQuotient += DigitValue.One : wRemainder -= wRightCur
Loop While wRemainder >= wRightCur
Else
wQuotient = wLeftCur \ wRightCur
wRemainder = wLeftCur - wQuotient * wRightCur
End If
If wRemainder = DigitValue.Zero Then fResultFound = True : Exit Do
fIterationIsEven = Not fIterationIsEven
If fComputeLeftInverse Then ComputeFusedMulAdd(uLeftInvPrev, uLeftInvCur, wQuotient)
If fComputeRightInverse Then ComputeFusedMulAdd(uRightInvPrev, uRightInvCur, wQuotient)
wLeftCur = wRightCur : wRightCur = wRemainder
Loop
uRightCur = wRightCur
End If
End If
If fComputeLeftInverse Then
uLeftInverse = If(fIterationIsEven, uRight - uLeftInvCur, uLeftInvCur)
End If
If fComputeRightInverse Then
uRightInverse = If(fIterationIsEven, uRightInvCur, uLeft - uRightInvCur)
End If
Return uRightCur
End Function
''' <remarks>All word-sized parameters must have their most-significant bit unset.</remarks>
Private Shared Sub ComputeFusedMulMulAdd(
ByRef uLeftInvPrev As BigUInteger, ByRef uLeftInvCur As BigUInteger,
ByVal wLeftInvPrev As UInt32, ByVal wLeftInvCur As UInt32, ByVal wRightInvPrev As UInt32, ByVal wRightInvCur As UInt32)
Dim ndxDigitMaxPrev As Integer = uLeftInvPrev.ValueLength - 1, ndxDigitMaxCur As Integer = uLeftInvCur.ValueLength - 1,
ndxDigitMaxNew As Integer = ndxDigitMaxCur + 1
Dim awLeftInvPrev() As UInt32 = uLeftInvPrev.ValueDigits, awLeftInvCur() As UInt32 = uLeftInvCur.ValueDigits
Dim awLeftInvPrevNew(0 To ndxDigitMaxNew) As UInt32, awLeftInvCurNew(0 To ndxDigitMaxNew) As UInt32
Dim dwResult As UInt64, wCarryLeftPrev As UInt32 = DigitValue.Zero, wCarryLeftCur As UInt32 = DigitValue.Zero
Dim wDigitLeftInvPrev, wDigitLeftInvCur As UInt32
For ndxDigit As Integer = 0 To ndxDigitMaxPrev
wDigitLeftInvPrev = awLeftInvPrev(ndxDigit) : wDigitLeftInvCur = awLeftInvCur(ndxDigit)
dwResult = wCarryLeftPrev + wLeftInvPrev * CULng(wDigitLeftInvPrev) + wRightInvPrev * CULng(wDigitLeftInvCur)
awLeftInvPrevNew(ndxDigit) = CUInt(dwResult And DigitValue.Full) : wCarryLeftPrev = CUInt(dwResult >> DigitSize.Bits)
dwResult = wCarryLeftCur + wLeftInvCur * CULng(wDigitLeftInvPrev) + wRightInvCur * CULng(wDigitLeftInvCur)
awLeftInvCurNew(ndxDigit) = CUInt(dwResult And DigitValue.Full) : wCarryLeftCur = CUInt(dwResult >> DigitSize.Bits)
Next
If ndxDigitMaxCur > ndxDigitMaxPrev Then
For ndxDigit As Integer = ndxDigitMaxPrev + 1 To ndxDigitMaxCur
wDigitLeftInvCur = awLeftInvCur(ndxDigit)
dwResult = wCarryLeftPrev + wRightInvPrev * CULng(wDigitLeftInvCur)
awLeftInvPrevNew(ndxDigit) = CUInt(dwResult And DigitValue.Full) : wCarryLeftPrev = CUInt(dwResult >> DigitSize.Bits)
dwResult = wCarryLeftCur + wRightInvCur * CULng(wDigitLeftInvCur)
awLeftInvCurNew(ndxDigit) = CUInt(dwResult And DigitValue.Full) : wCarryLeftCur = CUInt(dwResult >> DigitSize.Bits)
Next
End If
If wCarryLeftPrev <> DigitValue.Zero Then awLeftInvPrevNew(ndxDigitMaxNew) = wCarryLeftPrev
If wCarryLeftCur <> DigitValue.Zero Then awLeftInvCurNew(ndxDigitMaxNew) = wCarryLeftCur
uLeftInvPrev = New BigUInteger(awLeftInvPrevNew) : uLeftInvCur = New BigUInteger(awLeftInvCurNew)
End Sub
''' <remarks>All word-sized parameters must have their most-significant bit unset.</remarks>
Private Shared Sub ComputeFusedMulMulSub(
ByRef uLeftCur As BigUInteger, ByRef uRightCur As BigUInteger,
ByVal wLeftInvPrev As UInt32, ByVal wLeftInvCur As UInt32, ByVal wRightInvPrev As UInt32, ByVal wRightInvCur As UInt32,
ByVal fShorthandIterationIsEven As Boolean)
Dim ndxDigitMax As Integer = uLeftCur.ValueLength - 1,
fRightIsShorter As Boolean = (uRightCur.ValueLength < uLeftCur.ValueLength),
ndxDigitStop As Integer = If(fRightIsShorter, ndxDigitMax - 1, ndxDigitMax)
Dim awLeftCur() As UInt32 = uLeftCur.ValueDigits, awRightCur() As UInt32 = uRightCur.ValueDigits
Dim awLeftNew(0 To ndxDigitMax) As UInt32, awRightNew(0 To ndxDigitStop) As UInt32
Dim iTemp As Int64, wCarryLeft As Int32 = 0I, wCarryRight As Int32 = 0I
Dim wDigitLeftCur, wDigitRightCur As UInt32
If fShorthandIterationIsEven Then
For ndxDigit As Integer = 0 To ndxDigitStop
wDigitLeftCur = awLeftCur(ndxDigit) : wDigitRightCur = awRightCur(ndxDigit)
iTemp = wCarryLeft + CLng(wDigitRightCur) * wRightInvPrev - CLng(wDigitLeftCur) * wLeftInvPrev
awLeftNew(ndxDigit) = CUInt(iTemp And DigitValue.Full) : wCarryLeft = CInt(iTemp >> DigitSize.Bits)
iTemp = wCarryRight + CLng(wDigitLeftCur) * wLeftInvCur - CLng(wDigitRightCur) * wRightInvCur
awRightNew(ndxDigit) = CUInt(iTemp And DigitValue.Full) : wCarryRight = CInt(iTemp >> DigitSize.Bits)
Next
If fRightIsShorter Then
wDigitLeftCur = awLeftCur(ndxDigitMax)
iTemp = wCarryLeft - CLng(wDigitLeftCur) * wLeftInvPrev
awLeftNew(ndxDigitMax) = CUInt(iTemp And DigitValue.Full)
End If
Else
For ndxDigit As Integer = 0 To ndxDigitStop
wDigitLeftCur = awLeftCur(ndxDigit) : wDigitRightCur = awRightCur(ndxDigit)
iTemp = wCarryLeft + CLng(wDigitLeftCur) * wLeftInvPrev - CLng(wDigitRightCur) * wRightInvPrev
awLeftNew(ndxDigit) = CUInt(iTemp And DigitValue.Full) : wCarryLeft = CInt(iTemp >> DigitSize.Bits)
iTemp = wCarryRight + CLng(wDigitRightCur) * wRightInvCur - CLng(wDigitLeftCur) * wLeftInvCur
awRightNew(ndxDigit) = CUInt(iTemp And DigitValue.Full) : wCarryRight = CInt(iTemp >> DigitSize.Bits)
Next
If fRightIsShorter Then
wDigitLeftCur = awLeftCur(ndxDigitMax)
iTemp = wCarryLeft + CLng(wDigitLeftCur) * wLeftInvPrev
awLeftNew(ndxDigitMax) = CUInt(iTemp And DigitValue.Full)
End If
End If
uLeftCur = New BigUInteger(awLeftNew) : uRightCur = New BigUInteger(awRightNew)
End Sub
''' <remarks>All word-sized parameters must have their most-significant bit unset.</remarks>
Private Shared Sub ComputeFusedMulAdd(ByRef uLeftInvPrev As BigUInteger, ByRef uLeftInvCur As BigUInteger, ByVal wQuotient As UInt32)
Dim ndxDigitPrevMax As Integer = uLeftInvPrev.ValueLength - 1, ndxDigitCurMax As Integer = uLeftInvCur.ValueLength - 1,
ndxDigitNewMax As Integer = ndxDigitCurMax + 1
Dim awLeftInvPrev() As UInt32 = uLeftInvPrev.ValueDigits, awLeftInvCur() As UInt32 = uLeftInvCur.ValueDigits,
awLeftInvNew(0 To ndxDigitNewMax) As UInt32
Dim dwResult As UInt64 = DigitValue.Zero, wCarry As UInt32 = DigitValue.Zero
For ndxDigit As Integer = 0 To ndxDigitPrevMax
dwResult = CULng(wCarry) + awLeftInvPrev(ndxDigit) + CULng(wQuotient) * awLeftInvCur(ndxDigit)
awLeftInvNew(ndxDigit) = CUInt(dwResult And DigitValue.Full) : wCarry = CUInt(dwResult >> DigitSize.Bits)
Next
For ndxDigit As Integer = ndxDigitPrevMax + 1 To ndxDigitCurMax
dwResult = CULng(wCarry) + CULng(wQuotient) * awLeftInvCur(ndxDigit)
awLeftInvNew(ndxDigit) = CUInt(dwResult And DigitValue.Full) : wCarry = CUInt(dwResult >> DigitSize.Bits)
Next
If wCarry <> DigitValue.Zero Then awLeftInvNew(ndxDigitNewMax) = wCarry
uLeftInvPrev = uLeftInvCur : uLeftInvCur = New BigUInteger(awLeftInvNew)
End Sub
Если вы хотите использовать этот код напрямую, вам может понадобиться компилятор Visual Basic 2012 для некоторых конструкций - я не проверял предыдущие версии; и я не знаю о минимальной версии.Net (по крайней мере, 3,5 должно быть достаточно); Известно, что скомпилированные приложения работают на Mono, но с низкой производительностью. Единственное, в чем я абсолютно уверен, это то, что нельзя пытаться использовать автоматические переводчики с VB на C#, поскольку они ужасно плохи в таких предметах; полагаться только на свою голову.