Code Monkey home page Code Monkey logo

swift-numberkit's Introduction

Swift NumberKit

Platforms: macOS, iOS, Linux Language: Swift 5.9 IDE: Xcode 15 Package managers: SwiftPM, Carthage License: Apache

Overview

This is a framework implementing advanced numeric data types for the Swift programming language on macOS, iOS and Linux. Currently, the framework provides four new numeric types, each represented as a struct or enumeration:

  1. BigInt: arbitrary-size signed integers
  2. Integer: arbitrary-size signed integers whose implementation depends on the size of the represented value.
  3. Rational: signed rational numbers
  4. Complex: complex floating-point numbers

Note: So far, with every major version of Swift, Apple decided to change the foundational APIs of the numeric types in Swift significantly and consistently in a backward incompatible way. In order to be more isolated from such changes in future, with Swift 3, I decided to introduce a distinct integer type used in NumberKit based on a new protocol IntegerNumber. All standard numeric integer types implement this protocol. This is now consistent with the usage of protocol FloatingPointNumber for floating point numbers, where there was, so far, never a real, generic enough foundation (and still isn't).

BigInt

BigInt values are immutable, signed, arbitrary-size integers that can be used as a drop-in replacement for the existing binary integer types of Swift 5. Struct BigInt defines all the standard arithmetic integer operations and implements the corresponding numeric protocols of Swift.

Integer

Integer values are immutable, signed, arbitrary-size integers that can be used as a drop-in replacement for the existing binary integer types of Swift 5. As opposed to BigInt, the representation of values is chosen to optimize for memory size and performance of arithmetic operations. Enum Integer defines all the standard arithmetic integer operations and implements the corresponding numeric protocols of Swift.

Rational

Struct Rational<T> defines immutable, rational numbers based on an existing signed integer type T, like Int32, Int64, or BigInt. A rational number is a signed number that can be expressed as the quotient of two integers a and b: a / b.

Complex

Struct Complex<T> defines complex numbers based on an existing floating point type T, like Float or Double. A complex number consists of two components, a real part re and an imaginary part im and is typically written as: re + im * i where i is the imaginary unit.

Requirements

The following technologies are needed to build the components of the Swift NumberKit framework:

Copyright

Author: Matthias Zenger ([email protected])
Copyright © 2016-2024 Matthias Zenger. All rights reserved.

swift-numberkit's People

Contributors

dankogai avatar detreville avatar objecthub avatar romanpodymov avatar thsoft avatar wolfmcnally avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

swift-numberkit's Issues

iOS Support

Is there any plan to add iOS support? What work is required? If it's simple, I'd be happy to do it.

'StaticBigInt' is only available in iOS 16.4 or newer

I am using hedera-swift-sdk which has dependency on numberkit library after updating the library I am getting this below error :-
Not able to compile the code with swift-numberkit version 2.5.0
'StaticBigInt' is only available in iOS 16.4 or newer
extension BigInt - line number: 1208

Screenshot 2024-04-17 at 11 08 42 AM

doubleValue returns incorrect infinities and NaNs and zeroes

With Rational<BigInt> values, incorrect infinities or NaNs or zeroes are returned for doubleValue when the numerators or denominator or both are too large themselves to fit inside a Double, even when the values themselves fit.

It would be great to document this behavior or even to correct for it.

Example test program:

import Foundation
import NumberKit

var x: Rational<BigInt> = 1

while true {
  print("\(x)")
  print("numerator.doubleValue = \(x.numerator.doubleValue)")
  print("denominator.doubleValue = \(x.denominator.doubleValue)")
  print("doubleValue = \(x.doubleValue)")
  if x.doubleValue.isNaN {
    break
  }
  x *= 101
  x /= 100
  print()
}

Example test output:

...

449290712499035178136313067576445965611259479254722670514813444007703755277757233626012171465497954998012097338429625913922663389615772353420792692877397988852736997750126238302536602889821678795213590136955628111947677132802585714228163560288611949064673775051015631052360866493066504213939770088265101/100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
numerator.doubleValue = 4.4929071249903515e+302
denominator.doubleValue = 1e+302
doubleValue = 4.492907124990351

45378361962402552991767619825221042526737207404726989721996157844778079283053480596227229318015293454799221831181392217306189002351193007695500061980617196874126436772762750068556196891871989558316572603832518439306715390413061157137044519589149806855532051280152578736288447515799716925607916778914775201/10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
numerator.doubleValue = 4.537836196240255e+304
denominator.doubleValue = 1e+304
doubleValue = 4.537836196240256

4583214558202657852168529602347325295200457947877425961921611942322586007588401540218950161119544638934721404949320613947925089237470493777245506260042336884286770114049037756924175886079070945389973832987084362369978254431719176870841496478504130492408737179295410452365133199095771409486399594670392295301/1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
numerator.doubleValue = 4.583214558202658e+306
denominator.doubleValue = 1e+306
doubleValue = 4.583214558202658

462904670378468443069021489837079854815246252735620022154082806174581186766428555562113966273074008532406861899881382008740434012984519871501796132264276025312963781518952813449341764493986165484387357131695520599367803697603636863954991144328917179733282455108836455688878453108672912358126359061709621825401/100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
numerator.doubleValue = inf
denominator.doubleValue = 1e+308
doubleValue = inf

46753371708225312749971170473545065336339871526297622237562363423632699863409284111773510593580474861773093051888019582882783835311436507021681409358691878556609341933414234158383518213892602713923123070301247580536148173457967323259454105577220635153061527965992482024576723763975964148170762265232671804365501/10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
numerator.doubleValue =   inf
denominator.doubleValue = inf
doubleValue = nan

Codable support?

Could you make it Codable?
(Codable is Swift standard protocol for encoding/decoding. Doc)

Compile errors

I tried to use the package with the following Package.swift file:

// swift-tools-version:4.0
import PackageDescription

let package = Package(
    name: "Notation",
    dependencies: [
        .package(
            url: "https://github.com/objecthub/swift-numberkit.git",
            from: "2.0.1"
        )
    ],
    targets: [
        .target(
            name: "Notation",
            dependencies: ["NumberKit"],
            path: "Sources"
        ),
    ]
)

When building my package, I get the following errors:

MacBook-Pro-of-thSoft:Notation thsoft$ swift build
Cloning https://github.com/objecthub/swift-numberkit.git
Resolving https://github.com/objecthub/swift-numberkit.git at 2.0.1
Compile Swift Module 'NumberKit' (6 sources)
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/Rational.swift:327:12: error: argument labels '(numerator:, denominator:)' do not match any available overloads
      self.init(numerator: rat.numerator, denominator: rat.denominator)
           ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/Rational.swift:327:12: note: overloads for 'Rational<T>.init' exist with these partially matching parameter lists: (T, T), (from: String, radix: Int)
      self.init(numerator: rat.numerator, denominator: rat.denominator)
           ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/Rational.swift:360:13: error: 'Rational<T>' initializer is inaccessible due to 'private' protection level
    return (Rational(numerator: numer, denominator: denom),
            ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/Rational.swift:123:11: note: 'init(numerator:denominator:)' declared here
  private init(numerator: T, denominator: T) {
          ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:728:18: error: 'uwords' is inaccessible due to 'private' protection level
    return (self.uwords.count * UInt32.bitWidth) + 1
                 ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:737:20: error: 'uwords' is inaccessible due to 'private' protection level
    while i < self.uwords.count && self.uwords[i] == 0 {
                   ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:740:21: error: 'uwords' is inaccessible due to 'private' protection level
    return i < self.uwords.count ? i * UInt32.bitWidth + self.uwords[i].trailingZeroBitCount
                    ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:749:31: error: 'uwords' is inaccessible due to 'private' protection level
    res.reserveCapacity((self.uwords.count + 1) / 2)
                              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:751:20: error: 'uwords' is inaccessible due to 'private' protection level
    while i < self.uwords.count {
                   ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:752:31: error: 'uwords' is inaccessible due to 'private' protection level
      let current = UInt(self.uwords[i])
                              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:754:28: error: 'uwords' is inaccessible due to 'private' protection level
      let next = i == self.uwords.count ? 0 : (UInt(self.uwords[i]) << UInt32.bitWidth)
                           ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:759:13: error: 'negative' is inaccessible due to 'private' protection level
    if self.negative {
            ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:44:15: note: 'negative' declared here
  private let negative: Bool
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:788:26: error: 'uwords' is inaccessible due to 'private' protection level
    return BigInt(words: uwords, negative: false)
                         ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:897:27: error: 'uwords' is inaccessible due to 'private' protection level
      self.init(words: bi.uwords, negative: bi.negative)
                          ^
/Users/thsoft/Swift/Notation/.build/checkouts/swift-numberkit.git-6882174132934804157/Sources/NumberKit/BigInt.swift:41:15: note: 'uwords' declared here
  private let uwords: [UInt32]
              ^
warning: dependency 'NumberKit' is missing; cloning again
error: terminated(1): /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift-build-tool -f '/Users/thsoft/Swift/Notation/.build/debug.yaml' main

Rational<BigInt> stack overflow when subtracting from a negative number

With the following snippet:

let a = Rational<BigInt>(-1, 4)
let b = Rational<BigInt>(0)
let c = a - b

I get an infinite recursion between BigInt.plus(:) and BigInt.minus(:). Both methods call each other if the lhs is negative and the rhs is not:

public func plus(_ rhs: BigInt) -> BigInt {
    guard self.negative == rhs.negative else {
      return self.minus(rhs.negate)
    }
    ...
}

public func minus(_ rhs: BigInt) -> BigInt {
    guard self.negative == rhs.negative else {
      return self.plus(rhs.negate)
    }
    ...
}

BigInt.divided(by:) sign of remainder

I'm wondering if there is a bug in the BigInt.divided(by:) function. I'm using it to find the remainder of -180 / 360. I would have expected the remainder to be -180, but instead I get 180:

(lldb) p BigInt(-180).divided(by: 360).quotient.description
(String) $R15 = "0"
(lldb) p BigInt(-180).divided(by: 360).remainder.description
(String) $R16 = "180"

I'm actually trying to use the % function (which maps to divided(by:) behind the scenes):

(lldb) p (BigInt(-180) % BigInt(360)).description
(String) $R21 = "180"

For Swift integers, % works as I would expect:

(lldb) p -180 % 360
(Int) $R22 = -180

Anyway, thanks for a great Swift package!

Question: Are you interested in making one of the internal initializers public?

I was wondering what stomach you might have for making one of the initializers that is currently marked as internal as public. I was thinking of documenting the initializer and changing the access scope to public and submitting a PR. I wanted to check to see if that was something you were willing to entertain before I spent the time.

I'm writing a DER/BER encoder/decoder and your BigInt implementation is a great solution for storing and displaying arbitrary length integers. In fact, for the most part your init I linked above is almost exactly what is encoded in the format. If I parse and send it to that init I get the integer I was expecting.

FloatingPoint to Rational

Is there an initializer or method to convert floats and doubles to rationals? I ended up writing my own conversions to Rational, but I wanted to check if I'm overlooking already existing support for that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.