Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Arbitrary-precision arithmetic in pure Swift

License

NotificationsYou must be signed in to change notification settings

attaswift/BigInt

Repository files navigation

BigInt

Swift

This repository providesinteger types of arbitrary width implementedin 100% pure Swift. The underlying representation is in base 2^64, usingArray<UInt64>.

This module is handy when you need an integer type that's wider thanUIntMax, butyou don't want to addThe GNU Multiple Precision Arithmetic Libraryas a dependency.

Two big integer types are included:BigUInt andBigInt,the latter being the signed variant.Both of these are Swift structs with copy-on-write value semantics, and they can be used muchlike any other integer type.

The library provides implementations for some of the most frequently useful functions onbig integers, including

The implementations are intended to be reasonably efficient, but they are unlikely to becompetitive with GMP at all, even when I happened to implement an algorithm with same asymptoticbehavior as GMP. (I haven't performed a comparison benchmark, though.)

The library has 100% unit test coverage. Sadly this does not imply that there are no bugsin it.

Generated API docs are available athttps://attaswift.github.io/BigInt/.

BigInt can be used, distributed and modified underthe MIT license.

BigInt 4.0.0 requires Swift 4.2 (The last version with support for Swift 3.x was BigInt 2.1.0.The last version with support for Swift 2 was BigInt 1.3.0.)

Swift Versionlast BigInt Version
3.x2.1.0
4.03.1.0
4.24.0.0
5.x5.4.0

BigInt deploys to macOS 10.10, iOS 9, watchOS 2 and tvOS 9.It has been tested on the latest OS releases only---however, as the module uses very few platform-provided APIs,there should be very few issues with earlier versions.

BigInt uses no APIs specific to Apple platforms, soit should be easy to port it to other operating systems.

Setup instructions:

  • Swift Package Manager:Although the Package Manager is still in its infancy, BigInt provides experimental support for it.Add this to the dependency section of yourPackage.swift manifest:

    .package(url:"https://github.com/attaswift/BigInt.git", from:"5.4.0")

BigUInt is aMutableCollectionType of its 64-bit digits, with the least significantdigit at index 0. As a convenience,BigUInt allows you to subscript it with indexes ator above itscount.The subscript operator returns 0 for out-of-boundgets andautomatically extends the array on out-of-boundsets. This makes memory management simpler.

BigInt is just a tiny wrapper around aBigUInt [absolute value][magnitude] and asign bit, both of which are accessible as public read-write properties.

The types provided byBigInt are not parametric—this is very much intentional, asSwift generics cost us dearly at runtime in this use case. In every approach I tried,making arbitrary-precision arithmetic operations work with a genericDigit type parameterresulted in code that was literallyten times slower. If you can make the algorithms genericwithout such a huge performance hit,please enlighten me!

This is an area that I plan to investigate more, as it would be useful to have genericimplementations for arbitrary-width arithmetic operations. (Polynomial division and decimal basesare two examples.) The library already implements double-digit multiplication and division asextension methods on a protocol with an associated type requirement; this has not measurably affectedperformance. Unfortunately, the same is not true forBigUInt's methods.

Of course, as a last resort, we could just duplicate the code to create a separategeneric variant that was slower but more flexible.

It is easy to useBigInt to calculate the factorial function for any integer:

import BigIntfunc factorial(_ n:Int)->BigInt{return(1... n).map{BigInt($0)}.reduce(BigInt(1),*)}print(factorial(10))==>362880print(factorial(100))==>933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000print(factorial(1000))==>402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Well, I guess that's all right, but it's not very interesting. Let's try something more useful.

TheBigInt module provides all necessary parts to implement an (overly)simpleRSA cryptography system.

Let's start with a simple function that generates a random n-bit prime. The moduleincludes a function to generate random integers of a specific size, and also anisPrime method that performs the Miller–Rabin primality test. These are all we need:

func generatePrime(_ width:Int)->BigUInt{whiletrue{varrandom=BigUInt.randomInteger(withExactWidth: width)        random|=BigUInt(1)if random.isPrime(){return random}}}letp=generatePrime(1024)==>133081876506421923962564199110125448453704937284249367915614783184430716172428728198095674708718741991443516991416111660167888335861107680074358055605571417392208406194264346635072293912609713085260354070700055888678514690878149253177960273775659537560220378850112471985434373425534121373466492101182463962031letq=generatePrime(1024)==>170729544226571454895473088123333689250079490545012049838639583558971720931737831010822659694399955378425256465062476627613315758673350478461613830570116841015780784336308507083874651158029602582993233111593356512531869546706885170044355115669728424124141763799008880327106952436883614887277350838425336156327

Cool! Now that we have two large primes, we can produce an RSA public/private keypairout of them.

typealiasKey=(modulus:BigUInt, exponent:BigUInt)letn= p* q==>22721008120758282530010953362926306641542233757318103044313144976976529789946696154549667209077125159174814189815913796476353912605693490996664101272796903679788118437553375588899437064085788375498536428841379610052726276320267903713402181057933883525572232242690805678883227791774442041516929419640051653934584376704034639531697728169072805919344232379772583580978465110799473378577781371775706683915745541770710027548777039928141735282989711814097224075770856102708721720597522002207275447810167397968435583004676293892340103729490987263776871467057582629588916498579594964478080508868267360515953225283461208420137lete:BigUInt=65537letphi=(p-1)*(q-1)letd= e.inverse(phi)!     // d * e % phi == 1==>1396466434386901475973635048077683799260450090398970338320236629190555899627771977822086142456362972689566985925179681282432115598451765899180050962461295573831370692379342918841065848209981469650855314331951066867454742222226209868586965916983653246883515441255452115210364245315889536341764067661170454278457697437495445789456921660619938185093118762690200980720312508614337759620606992462563490422766695595565689175332684791909489595603975795727615298528912462835396045456912448999969287715867664304211866261387586350401612983709922304068751268453269452710980742873307409704484365002175294665608486688146261327793letpublicKey:Key=(n, e)letprivateKey:Key=(n, d)

In RSA, modular exponentiation is used to encrypt (and decrypt) messages.

func encrypt(_ message:BigUInt, key:Key)->BigUInt{return message.power(key.exponent, modulus: key.modulus)}

Let's try out our new keypair by converting a string into UTF-8, interpretingthe resulting binary representation as a big integer, and encrypting it with thepublic key.BigUInt has an initializer that takes anNSData, so this is prettyeasy to do:

letsecret:BigUInt=BigUInt("Arbitrary precision arithmetic is fun!".dataUsingEncoding(NSUTF8StringEncoding)!)==>8332344684610597607846673152472868190529306770180483892538919892912391297122945768818568737letcyphertext=encrypt(secret, key: publicKey)==>9518698254348598520066651650806609388003884289233788056155491090427729091783145354854954722744805432145474047391353716305176389470779020645959135298322520888633616749451290995759433847673303425545251203844854694280489620271491698761278903067702818390469949196205088897452460322629083698416616475958695241934358938527964147999991283152843977988979846238236160274201261075188190509539751990119132013021748666385957342228670050891571985032041922648147508320728442085203946030549017060602439473137197340259582645643594496843915366461718857080894002247199063846878349208193955207336172861151720299024935127021719852700882

Well, it looks encrypted all right, but can we get the original message back?In theory, encrypting the cyphertext with the private key returns the original message.Let's see:

letplaintext=encrypt(cyphertext, key: privateKey)==>8332344684610597607846673152472868190529306770180483892538919892912391297122945768818568737letreceived=String(data: plaintext.serialize(), encoding: NSUTF8StringEncoding)==>"Arbitrary precision arithmetic is fun!"

Yay! This is truly terrific, but please don't use this example code in an actualcryptography system. RSA has lots of subtle (and some not so subtle) complicationsthat we ignored to keep this example short.

Another fun activity to try withBigInts is to generate the digits of π.Let's try implementingJeremy Gibbon's spigot algorithm.This is a rather slow algorithm as π-generators go, but it makes up for it with its groovinessfactor: it's remarkably short, it only uses (big) integer arithmetic, and every iterationproduces a single new digit in the base-10 representation of π. This naturally leads to animplementation as an infiniteGeneratorType:

func digitsOfPi()->AnyGenerator<Int>{varq:BigUInt=1varr:BigUInt=180vart:BigUInt=60vari:UInt64=2 // Does not overflow until digit #826_566_842returnAnyIterator{letu:UInt64=3*(3* i+1)*(3* i+2)lety=(q.multiplied(byDigit:27* i-12)+5* r)/(5* t)(q, r, t)=(10* q.multiplied(byDigit: i*(2* i-1)),10*(q.multiplied(byDigit:5* i-2)+ r- y* t).multiplied(byDigit: u),            t.multiplied(byDigit: u))        i+=1returnInt(y[0])}}

Well, that was surprisingly easy. But does it work? Of course it does!

vardigits="π ≈"varcount=0fordigitindigitsOfPi(){assert(digit<10)    digits+=String(digit)    count+=1if count==1{ digits+="."}if count==1000{break}}digits==> π3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198

Now go and have some fun with big integers on your own!

About

Arbitrary-precision arithmetic in pure Swift

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors23

Languages


[8]ページ先頭

©2009-2025 Movatter.jp