Hi Everyone,
I'm really excited to write my first post in the Swift forums
I'm very interested in participating in this year's GSoC, I've been contemplating to contribute to Swift for a long time and I feel the safe environment this programme offers is the right opportunity to get started.
I've been in contact with Konrad Malawski, I'm fully aware I'm very late for proposing a project idea and securing a mentor. Nonetheless, there's still a bit of time left and I'd still rather try than regretting it later—writing this proposal is already a personal achievement and I'm learning so much just doing it
The following is my draft that I plan to submit tomorrow if I'm able to secure a mentor. I'm looking for feedback on the proposal itself and examples of use cases where developers could benefit from this project. The timeline is still a work in progress.
Thank you @ktoso for encouraging me
Definitions
- Fraction: The quotient of two numbers without loss of precision.
- MixedFraction: The quotient and remainder of a fraction.
- ContinuedFraction: A fraction of infinite length whose denominator is a quantity plus a fraction.
- Percentage: A fraction with a denominator of one hundred.
Abstract
Swift doesn't provide rational types in its standard library. A fraction type is useful when a developer wishes to simplify symbolic expressions without needing to worry about floating point errors. I'm proposing to implement types representing a fraction, mixed fraction, continued fraction and a percentage to the Swift Numerics package.
Proposal Details
Project size
Large (350 hours)
Recommended skills
- Proficiency with Swift
- Interest in numerical computing.
Expected difficulty
Medium
Description
Add Fraction, MixedFraction, ContinuedFraction and Percentage to the Swift Numerics package.
Some programming languages provide a built-in rational data type to represent rational numbers like 1/3 and -11/7 without rounding and to do arithmetic on them.
- C++ has included support for compile-time rational arithmetic in the form of the contents of its standard library's ratio header.
- Go provides rational numbers in the standard library, in the math/big package.
- Python's standard library includes a fraction type in the fractions module.
- The Apache Commons math library provides rational numbers for Java.
See also:
- This is my first try at tackling fractions in Swift.
- This issue and this gist are other examples of rational types in Swift.
Expected deliverables
The project would fit nicely as a new module in Swift Numerics with its own branch. The deliverables would include source code with tests and documentation.
Potential mentor(s)
I'm still looking for a mentor.
Proposed Design
I plan to follow the standard library contributor guidelines and conventions, and use Swift's numeric protocols as much as possible.
Rational
A new Rational
protocol will be needed to implement the four fraction types detailed below. The main difference with my first try, is this version is generic so that developers can declare their fraction's terms to be any BinaryInteger
as well as BigInt
when it's merged. BigInt
gives a new meaning to this project and re-evaluates whether implementing a fraction type is beneficial.
public protocol Rational {
associatedtype Term
where Term: BinaryInteger
var numerator: Term { get }
var denominator: Term { get }
var quotient: Double { get }
}
extension Rational {
public var isProper: Bool { get }
public var isImproper: Bool { get }
public var isWhole: Bool { get }
internal var isNormalized: Bool { get }
internal func normalized() -> Self
internal mutating func normalize()
}
I believe Rational types shouldn't conform to FloatingPoint
, however, NaN and Infinity must be represented. I could create new protocols RepresentableByNaN
and RepresentableByInfinity
for this purpose that would copy some of the properties from FloatingPoint
. If that design gets accepted, I wish to pitch these two protocols for the standard library and in turn have FloatingPoint
conform to both.
extension Rational
where Self: RepresentableByNaN {
public var isNaN: Bool { get }
public static var nan: Self { get }
}
extension Rational
where Self: RepresentableByInfinity {
public var isFinite: Bool { get }
public var isInfinite: Bool { get }
public static var infinity: Self { get }
}
RationalSimplifiable
To keep any Rational
type to it's lowest terms, I propose to create a separate protocol as the Percentage
type doesn't require this functionality. A fraction would always be reduced to its lowest terms so that developers won't have to simplify after every arithmetic operation.
Another alternative is making a @Simplified
property wrapper and give developers the freedom to keep their fractions simplified or not. I'll investigate more which approach developers would prefer during the initial part of the project.
public protocol RationalSimplifiable: Rational {}
extension RationalSimplifiable {
internal var isSimplifiable: Bool { get }
internal var isSimplified: Bool { get }
internal func simplified() -> Self
internal mutating func simplify()
}
Swift Numerics has the gcd method already implemented that I'll be needing
Fraction
Take the fraction 13/3. The quotient is 4.333... It is somewhat difficult to perform arithmetic with numbers using this form. Fraction
aims to make it more convenient for developers to handle rational numbers. It retains precision as it isn't converted to its quotient during these operations.
Fraction
represents a fraction m / n where m and n are two integer numbers. The denominator n will be constrained to be non-zero, and the two numbers may be kept in reduced form. The type will conform to Equatable
, Hashable
and Comparable
, as well as ExpressibleByFloatLiteral
, ExpressibleByIntegerLiteral
and LosslessStringConvertible
. Basic arithmetic operations such as addition, subtraction, multiplication, division and negation will be available.
public struct Fraction<Term>: RationalSimplifiable
where Term: BinaryInteger {
public let numerator: Term
public let denominator: Term
public init(_ numerator: Term, on denominator: Term)
public init<Value>(
approximately value: Value,
withPrecision precision: Value = 1e-6
) where Value: BinaryFloatingPoint
public var quotient: Double { get }
}
MixedFraction
Mixed fractions are used around the world such as in the United States and the United Kingdom to represent measurements. They are easier to understand as final outputs, since addition is easier than division. I don't understand 13/3 gallons of water, but I do understand 4 1/3, this expression make it clear that I have a little over 4 gallons of water.
MixedFraction
works similarly to a Fraction
—abstracting all the complexity away, it is represented by combining a whole number and a fraction.
public struct MixedFraction<Term>: RationalSimplifiable
where Term: BinaryInteger {
public let integral: Term { get }
public let fractional: Fraction<Term> { get }
public init(_ numerator: Term, on denominator: Term)
public init(_ integral: Term, _ numerator: Term, on denominator: Term)
public init(_ integral: Term, _ fractional: Fraction<Term>)
public var numerator: Term { get }
public var denominator: Term { get }
public var quotient: Double { get }
}
extension MixedFraction {
internal var isMixable: Bool { get }
public var isMixed: Bool { get }
internal func mixed() -> Self
internal mutating func mix()
}
ContinuedFraction
I'm yet undecided how ContinuedFraction
would look like but I expect it to behave similarly to Fraction
and conform to RationalSimplifiable
. I'll have time during the initial part of the project to investigate more deeply the most efficient way to implement it.
Percentage
As humans we use percentages on a daily basis without even noticing. It's used to describe variations in prices, it's used when representing data in pie charts and it even has its own button on calculators alongside the four basic operators. I believe this simple type should have its place in Swift because it's so common.
Percentage
works the same way as Fraction
except it can not be simplified and the denominator is fixed to 100.
public struct Percentage<Term>: Rational
where Term: BinaryInteger {
public let numerator: Term { get }
public init<Value>(_ numerator: Value)
where Value: BinaryInteger
public init<Value>(_ numerator: Value)
where Value: BinaryFloatingPoint
public let denominator: Term = 100
public var quotient: Double { get }
}
Proposal Timeline
As per last year's contributors' experiences posted on the forums, I'd love to have a weekly 10 to 15 minute call with my mentor.
My university still hasn't given us the start date of the summer, all they've said is it will be between the 10th and the 24th of June. In consequence, I have to be flexible with the first two weeks in my timeline.
-
June 13th (Week 1)
Coding officially begins -
June 20th (Week 2)
TODO: timeline -
June 27th (Week 3)
TODO: timeline -
July 4th (Week 4)
TODO: timeline -
July 11th (Week 5)
TODO: timeline -
July 18th (Week 6)
TODO: timeline -
July 25th (Week 7)
July 29th 18:00 UTC: Mentors and contributors submit phase 1 evaluations -
August 1st (Week 8)
TODO: timeline -
August 8th (Week 9)
TODO: timeline -
August 22nd (Week 10)
TODO: timeline -
September 5th (Week 11)
Submit final work product and final mentor evaluation -
September 12th (Week 12)
September 12th 18:00 UTC: Contributors submit their final work product and their final mentor evaluation
September 19th 18:00 UTC: Mentors submit final GSoC contributor evaluations -
September 20th
Initial results of Google Summer of Code 2022 announced
About Me
Hi, my name is Alex, I'm currently studying at the Apple Developer Academy in Naples and I've been coding with Swift for the past three years. That's since SwiftUI was revealed at WWDC19 which completely changed my professional career. I've never contributed to an open-source project before but I do have a few public repositories on GitHub that are listed below for your perusal. You can also find more details about my work experience on LinkdedIn.
- Swift Measures is a package containing measurement types.
- Swift Locations is a package wrapping CoreLocation with concurrency.
- Swift Motions is a package wrapping CoreMotion with concurrency.