From 655d2afbc3dc306261610ecb0db73a6b0564041f Mon Sep 17 00:00:00 2001 From: rimim Date: Tue, 10 Jan 2023 12:43:02 -0500 Subject: [PATCH] Fix for intermittent failure for multi term expressions. The cause is Dictionary is not Ordered and Terms must be evaluated in insertion order. --- Package.swift | 8 +++++++- Sources/Cassowary/Expression.swift | 5 ++++- Sources/Cassowary/Row.swift | 6 +++++- Tests/CassowaryTests/CassowaryTests.swift | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/Package.swift b/Package.swift index d711e31..0363ae0 100644 --- a/Package.swift +++ b/Package.swift @@ -7,8 +7,14 @@ let cassowary = Package( products: [ .library(name: "cassowary", type: .dynamic, targets: ["Cassowary"]), ], + dependencies: [ + .package(url: "https://github.com/apple/swift-collections.git", + .upToNextMinor(from: "1.0.0")), + ], targets: [ - .target(name: "Cassowary", dependencies: []), + .target(name: "Cassowary", dependencies: [ + .product(name: "OrderedCollections", package: "swift-collections") + ]), .testTarget(name: "CassowaryTests", dependencies: ["Cassowary"]), ] ) diff --git a/Sources/Cassowary/Expression.swift b/Sources/Cassowary/Expression.swift index 4c5add1..ffa6e1f 100644 --- a/Sources/Cassowary/Expression.swift +++ b/Sources/Cassowary/Expression.swift @@ -1,6 +1,9 @@ // Copyright © 2019 Saleem Abdulrasool . // SPDX-License-Identifier: BSD-3-Clause +@_implementationOnly +import OrderedCollections + public struct Expression { public let terms: [Term] public let constant: Double @@ -38,7 +41,7 @@ extension Expression: Equatable { } internal func reduce(_ expression: Expression) -> Expression { - var vars: [Variable:Double] = [:] + var vars: OrderedDictionary = [:] for term in expression.terms { vars[term.variable] = vars[term.variable, default: 0.0] + term.coefficient } diff --git a/Sources/Cassowary/Row.swift b/Sources/Cassowary/Row.swift index f42eb7d..9fa643c 100644 --- a/Sources/Cassowary/Row.swift +++ b/Sources/Cassowary/Row.swift @@ -1,8 +1,12 @@ // Copyright © 2019 Saleem Abdulrasool . // SPDX-License-Identifier: BSD-3-Clause +@_implementationOnly +import OrderedCollections + internal class Row { - public private(set) var cells: [Symbol:Double] = [:] + // Must be in insertion order + public private(set) var cells: OrderedDictionary = [:] public private(set) var constant: Double public init() { diff --git a/Tests/CassowaryTests/CassowaryTests.swift b/Tests/CassowaryTests/CassowaryTests.swift index 4810cab..0a669e8 100644 --- a/Tests/CassowaryTests/CassowaryTests.swift +++ b/Tests/CassowaryTests/CassowaryTests.swift @@ -218,4 +218,25 @@ final class CassowaryTests: XCTestCase { XCTAssertEqual(x_l.value, 80) XCTAssertEqual(x_r.value, 100) } + + // Terms must be evaluated in insertion order + func testMultiTermExpressionOrder() throws { + let solver: Solver = Solver() + + let c: Variable = Variable("c") + let a: Variable = Variable("a") + let b: Variable = Variable("b") + + try solver.add(variable: c, strength: .strong) + try solver.add(constraint: a >= 0, .strong) + try solver.add(constraint: b >= a, .strong) + try solver.add(constraint: b - a == c, .required) + try solver.suggest(value: 100, for: c) + + solver.update() + + XCTAssertEqual(a.value, 0) + XCTAssertEqual(b.value, 100) + XCTAssertEqual(c.value, 100) + } }