Skip to content

Commit e00c568

Browse files
committed
added Quantum and Inventory
1 parent b1de652 commit e00c568

3 files changed

Lines changed: 263 additions & 8 deletions

File tree

Sources/Units/Inventory.swift

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//
2+
// Inventory.swift
3+
// Units
4+
//
5+
// Created by Jason Jobe on 1/28/26.
6+
//
7+
import Foundation
8+
9+
// MARK: SKU for Inventory
10+
public typealias SKU = URL
11+
public typealias Stock = Quantum
12+
13+
public extension QuantumType {
14+
var sku: SKU { URL(sku: "\(self)") }
15+
}
16+
17+
// MARK: Inventory - a collection of Quamtum
18+
public struct Inventory {
19+
public private(set) var items: [Stock]
20+
public var count: Int { items.count }
21+
}
22+
23+
public extension Inventory {
24+
25+
func adding(_ q: Quantum) -> Self {
26+
var newSelf = self
27+
newSelf.add(q)
28+
return newSelf
29+
}
30+
31+
func subtracting(_ q: Quantum) -> Self {
32+
var newSelf = self
33+
newSelf.subtract(q)
34+
return newSelf
35+
}
36+
37+
mutating func subtract(_ q: Quantum) {
38+
if let ndx = items.firstIndex(where: { $0.qtype == q.qtype }) {
39+
items[ndx].magnitude -= q.magnitude
40+
if items[ndx].magnitude == 0 {
41+
items.remove(at: ndx)
42+
}
43+
} else {
44+
items.append(q)
45+
}
46+
}
47+
48+
mutating func add(_ q: Quantum) {
49+
if let ndx = items.firstIndex(where: { $0.qtype == q.qtype }) {
50+
items[ndx].magnitude += q.magnitude
51+
if items[ndx].magnitude == 0 {
52+
items.remove(at: ndx)
53+
}
54+
} else {
55+
items.append(q)
56+
}
57+
}
58+
59+
func amount(of qt: QuantumType) -> Double {
60+
items.reduce(0) { partial, elem in partial + (elem.qtype == qt ? elem.magnitude : 0) }
61+
}
62+
}
63+
64+
public extension Inventory {
65+
static func += (lhs: inout Self, rhs: Quantum) {
66+
lhs.add(rhs)
67+
}
68+
static func -= (lhs: inout Self, rhs: Quantum) {
69+
lhs.subtract(rhs)
70+
}
71+
}
72+
73+
public extension Inventory {
74+
static func += (lhs: inout Self, rhs: Inventory) {
75+
rhs.items.forEach { lhs.add($0) }
76+
}
77+
78+
static func -= (lhs: inout Self, rhs: Inventory) {
79+
rhs.items.forEach { lhs.subtract($0) }
80+
}
81+
82+
static func *= (lhs: inout Self, rhs: Double) {
83+
for i in lhs.items.indices {
84+
lhs.items[i].magnitude *= rhs
85+
}
86+
}
87+
88+
static func /= (lhs: inout Self, rhs: Double) {
89+
for i in lhs.items.indices {
90+
lhs.items[i].magnitude /= rhs
91+
}
92+
}
93+
94+
static func + (lhs: Self, rhs: Inventory) -> Inventory {
95+
var result = lhs
96+
for element in rhs.items {
97+
result.add(element)
98+
}
99+
return result
100+
}
101+
}
102+
103+
// MARK: SKU Extenstion to URL
104+
extension URL {
105+
/**
106+
A SKU URL has the scheme 'sku' and has the general format of
107+
sku:<host>/<id>:=material[25units]
108+
Guarenteed to create something
109+
*/
110+
public init(sku: String) {
111+
self = Self.parse(sku: sku)
112+
}
113+
114+
public static func parse(sku: String) -> URL {
115+
if sku.lowercased().hasPrefix("sku:") {
116+
if let url = URL(string: sku) {
117+
return url
118+
}
119+
}
120+
121+
// Ensure we always create a valid URL with the `sku` scheme.
122+
// We build components and percent-encode the path so arbitrary SKU tokens are safe.
123+
var comps = URLComponents()
124+
comps.scheme = "sku"
125+
126+
// Treat the whole string as the URL path
127+
// Valid URL MUST have single leading slash
128+
let rawPath = sku.hasPrefix("/") ? sku : "/" + sku
129+
// URLComponents expects path to be unescaped
130+
// it will handle encoding when producing .url
131+
comps.path = rawPath
132+
133+
if let url = comps.url {
134+
return url
135+
}
136+
137+
// Absolute fallback: construct from a minimally encoded string.
138+
// Replace spaces with %20 and remove illegal characters conservatively.
139+
let allowed = CharacterSet.urlPathAllowed.union(CharacterSet(charactersIn: "/"))
140+
let encodedPath = rawPath.unicodeScalars.map { allowed.contains($0) ? String($0) : String(format: "%%%02X", $0.value) }.joined()
141+
return URL(string: "sku:\\" + encodedPath)
142+
?? URL(string: "sku:/UNKOWN")!
143+
}
144+
}
145+
146+
#if PLAY_TIME
147+
import Playgrounds
148+
#Playground {
149+
if let sku = URL(string: "sku:joe@wildthink.com/role[3]:=12345")
150+
{
151+
print("SKU:", sku)
152+
print("SKU.path:", sku.path)
153+
} else {
154+
print("BAD")
155+
}
156+
}
157+
#endif

Sources/Units/Measurement/Formatter.swift

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
//
2-
// Formatter.swift
3-
// Units
4-
// (aka Fountation.FormatStyle)
5-
//
6-
// Created by Jason Jobe on 10/24/25.
7-
//
8-
91
public extension Measurement {
102
struct Formatter<Output> {
113
let format: (Measurement) -> Output
@@ -48,6 +40,19 @@ extension Measurement.Formatter where Output == String {
4840

4941

5042
// MARK: Percent Formatter
43+
// Implementation
44+
import Foundation
45+
extension NumberFormatter {
46+
func string(from measurement: Measurement) -> String {
47+
return "\(self.string(from: .init(value: measurement.value)) ?? "BAD") \(measurement.unit.symbol)"
48+
}
49+
}
50+
51+
// Usage
52+
//let measurement = 28.123.measured(in: .meter)
53+
//let formatter = NumberFormatter()
54+
//formatter.maximumFractionDigits = 2
55+
//print(formatter.string(from: measurement)) // Prints `28.12 m`
5156

5257
public extension Percent {
5358
struct Formatter<Output> {

Sources/Units/Quantum.swift

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// Quantum.swift
3+
// Units
4+
//
5+
// Created by Jason Jobe on 1/17/26.
6+
//
7+
8+
import Foundation
9+
10+
/**
11+
Quantum means that which is divisible into two or more constituent parts,
12+
of which each is by nature a one and a this. A quantum is a plurality if it is numerable,
13+
a magnitude if it is measurable. Plurality means that which is divisible potentially into
14+
non-continuous parts, magnitude that which is divisible into continuous parts;
15+
of magnitude, that which is continuous in one dimension is length;
16+
in two breadth, in three depth.
17+
18+
Of these, limited plurality is number, limited length is a line, breadth a surface, depth a solid.
19+
20+
— Aristotle, Metaphysics, Book V, Ch. 11-14
21+
*/
22+
23+
/*
24+
Plurality - Numerable, Countable
25+
Magnitude - Measurable in Units
26+
Compound/Mixture -
27+
*/
28+
29+
public struct QuantumType: Equatable, Identifiable, Codable, Sendable {
30+
public var id: String { token }
31+
public let token: String
32+
public let unit: Unit
33+
}
34+
35+
extension QuantumType: ExpressibleByStringLiteral {
36+
public init(stringLiteral value: String) {
37+
token = value
38+
unit = .none
39+
}
40+
41+
public init(_ value: any StringProtocol, unit: Unit = .none) {
42+
token = value.description
43+
self.unit = unit
44+
}
45+
}
46+
47+
public struct Quantum: Equatable {
48+
public let qtype: QuantumType
49+
public var magnitude: Double
50+
}
51+
52+
public extension Quantum {
53+
var unit: Unit { qtype.unit }
54+
var count: Double { magnitude }
55+
var isPlurality: Bool { unit == .none }
56+
var isMeasurable: Bool { unit != .none }
57+
var countable: Bool { unit == .none }
58+
}
59+
60+
/*
61+
Equation - Mathematical expressions with single character variables
62+
63+
Money - System of Currency exchange
64+
Currency: EU, USD, etc
65+
66+
## ExtendedSwiftMath
67+
https://swiftpackageindex.com/ChrisGVE/ExtendedSwiftMath
68+
69+
An extended version of SwiftMath with comprehensive LaTeX
70+
symbol coverage, adding missing mathematical symbols,
71+
blackboard bold, delimiter sizing, amssymb equivalents,
72+
and automatic line wrapping.
73+
74+
## thales
75+
https://swiftpackageindex.com/ChrisGVE/thales
76+
Full Documentation on docs.rs
77+
78+
A comprehensive Computer Algebra System (CAS) library for
79+
symbolic mathematics, equation solving, calculus, and numerical
80+
methods. Named after Thales of Miletus, the first mathematician
81+
in the Greek tradition.
82+
83+
Features
84+
85+
- Expression Parsing - Parse mathematical expressions with full operator precedence
86+
- Equation Solving - Linear, quadratic, polynomial, transcendental, and systems of equations
87+
- Calculus - Differentiation, integration, limits, Taylor series, ODEs
88+
- Numerical Methods - Newton-Raphson, bisection, Brent's method when symbolic fails
89+
- Coordinate Systems - 2D/3D transformations, complex numbers, De Moivre's theorem
90+
- Units & Dimensions - Dimensional analysis and unit conversion
91+
- iOS Support - FFI bindings for Swift via swift-bridge
92+
*/
93+

0 commit comments

Comments
 (0)