Skip to content

Instantly share code, notes, and snippets.

@wildthink
Last active November 10, 2021 03:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wildthink/5b8eb62b843666b5a8e7a4f69e7ede15 to your computer and use it in GitHub Desktop.
Save wildthink/5b8eb62b843666b5a8e7a4f69e7ede15 to your computer and use it in GitHub Desktop.
//
// CompoundUnit.swift
//
// Created by Jason Jobe on 11/7/21.
//
// https://github.com/ole/Ampere
import Foundation
public protocol CompoundUnitProtocol: Dimension {
associatedtype Numerator: Unit
associatedtype Denominator: Unit
init(symbol: String?, _: Numerator, per: Denominator)
}
public final class CompoundUnit<Numerator: Unit, Denominator: Unit>: Dimension, CompoundUnitProtocol {
public typealias Numerator = Numerator
public typealias Denominator = Denominator
public override class func baseUnit() -> Self {
.init(symbol: nil, Numerator.anyBaseUnit(), per: Denominator.anyBaseUnit())
}
public private(set) var numerator: Numerator
public private(set) var denominator: Denominator
public var defaultSymbol: String {
"\(numerator.symbol)/\(denominator.symbol)"
}
public required init(symbol: String? = nil, _ num: Numerator, per den: Denominator) {
self.numerator = num
self.denominator = den
super.init(symbol: symbol ?? "\(num.symbol)/\(den.symbol)",
converter: UnitConverterLinear(coefficient: num.coefficient/den.coefficient))
}
public override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? Self else {
return false
}
return self === other ||
(self.symbol == other.symbol
&& self.numerator == other.numerator
&& self.denominator == other.denominator)
}
required public init?(coder: NSCoder) {
return nil
}
}
public extension Unit {
@objc class func anyBaseUnit() -> Self {
Self(symbol: String(describing: self).lowercased())
}
@objc var coefficient: Double {
1.0
}
}
extension Dimension {
@objc public override class func anyBaseUnit() -> Self {
baseUnit()
}
@objc public override var coefficient: Double {
(converter as? UnitConverterLinear)?.coefficient ?? 1.0
}
}
public extension Double {
func callAsFunction <R: CompoundUnitProtocol>(_ num: R.Numerator, per dem: R.Denominator) -> Measurement<R> {
Measurement(value: self, unit: R(symbol: nil, num, per: dem))
}
}
public extension Int {
func callAsFunction <R: CompoundUnitProtocol>(_ num: R.Numerator, per dem: R.Denominator) -> Measurement<R> {
Measurement(value: Double(self), unit: R(symbol: nil, num, per: dem))
}
}
public extension Measurement where UnitType: CompoundUnitProtocol {
/// Returns a new measurement created by converting to the specified unit.
///
/// - parameter otherUnit: A unit of the same `CompoundUnit`.
/// - returns: A converted measurement.
///
func converted(to num: UnitType.Numerator, per dem: UnitType.Denominator) -> Self {
return self.converted(to: CompoundUnit(symbol: nil, num, per: dem) as! UnitType)
}
/// Converts the measurement to the specified unit.
///
/// - parameter otherUnit: A unit of the same `CompoundUnit`.
mutating func convert(to num: UnitType.Numerator, per dem: UnitType.Denominator) {
self.convert(to: CompoundUnit(symbol: nil, num, per: dem) as! UnitType)
}
}
public func / <LU: Unit, RU: Unit> (lhs: Measurement<LU>, rhs: Measurement<RU>)
-> Measurement<CompoundUnit<LU, RU>> {
Measurement(lhs.value / rhs.value, CompoundUnit(lhs.unit, per: rhs.unit))
}
public func * <LU: Unit, RU: Dimension> (lhs: Measurement<CompoundUnit<LU, RU>>, rhs: Measurement<RU>)
-> Measurement<LU> {
let rhv = rhs.converted(to: lhs.unit.denominator)
return Measurement(lhs.value * rhv.value, lhs.unit.numerator)
}
func / <A: Unit, C: Unit>(lhs: A, rhs: C) -> CompoundUnit<A, C> {
CompoundUnit<A, C>(symbol: nil, lhs, per: rhs)
}
extension Measurement: ExpressibleByFloatLiteral {
public init(floatLiteral value: FloatLiteralType) {
self = Measurement(value: value, unit: UnitType.anyBaseUnit())
}
}
extension Measurement: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = Measurement(value: Double(value), unit: UnitType.anyBaseUnit())
}
}
public typealias UnitVelocity = CompoundUnit<UnitLength, UnitDuration>
public typealias Velocity = Measurement<UnitVelocity>
//public typealias Velocity = Measurement<CompoundUnit<UnitLength, UnitDuration>>
extension UnitVelocity {
static let mps: UnitVelocity = baseUnit()
static let mph: UnitVelocity = .init(symbol: "mph", .miles, per: .hours)
static let kph: UnitVelocity = .init(symbol: "kph", .kilometers, per: .hours)
}
func demo_vel() {
let s1_mph: Measurement<UnitSpeed> = .init(value: 60, unit: .milesPerHour)
let s2_kph: Measurement<UnitSpeed> = .init(value: 60, unit: .kilometersPerHour)
let v1_mph: Velocity = 60(.mph) // .init(value: 60, unit: .mph)
let v2_kph: Velocity = 96.56(.kph)
// let uv: Unit = UnitVelocity(.kilometers, per: .hours)
let v3: Velocity = 55(.kilometers, per: .hour)
let v4: Velocity = 88(.meters, per: .hour)
print (v3, v4.converted(to: .meters, per: .seconds))
print (s1_mph, s1_mph.converted(to: .kilometersPerHour))
print (s1_mph.unit.coefficient, s2_kph.unit.coefficient)
print (s1_mph.unit.coefficient/s2_kph.unit.coefficient)
// print ("mph", UnitVelocity.mph.coefficient, UnitVelocity.kph.coefficient)
print (v1_mph, v1_mph * 2, v1_mph / 3)
print (v1_mph, v1_mph.converted(to: .kph))
print (v2_kph, v2_kph.converted(to: .mph))
print (v2_kph, v2_kph.converted(to: .meters, per: .hours))
print (v2_kph, v2_kph.converted(to: .mps))
// v2_kph.converted(to: .meters, per: .hour)
print (String(describing: Velocity.self))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment