├── .swift-version ├── .gitignore ├── .xcodesamplecode.plist ├── Sources ├── EquationKitSupportBigInt │ ├── EquationKitSupportBigInt.swift │ ├── README.md │ ├── Operators │ │ ├── BigInt+IntegerNumberExpressible.swift │ │ └── BigInt_Operators.swift │ └── Variables │ │ └── BigInt_Variables.swift ├── EquationKitSupportDouble │ ├── EquationKitSupportDouble.swift │ ├── README.md │ ├── Variables │ │ └── Double_Variables.swift │ └── Operators │ │ └── Double_Operators.swift └── EquationKit │ ├── Protocols │ ├── Evaluatable.swift │ ├── Negatable.swift │ ├── AbsoluteConvertible.swift │ ├── Differentiatable.swift │ ├── Algebraic.swift │ ├── Substitutionable+Evaluate.swift │ ├── Solvable.swift │ └── Substitutionable.swift │ ├── Modulus │ ├── ModulusMode.swift │ ├── ModulusFunction.swift │ └── Modulus.swift │ ├── Term │ ├── TermProtocol+Negatable.swift │ ├── TermProtocol+AbsoluteConvertible.swift │ ├── TermProtocol+Comparable.swift │ ├── TermProtocol+Concatenation.swift │ ├── TermProtocol+Sorting.swift │ ├── TermProtocol+Concrete.swift │ ├── TermProtocol+Substitionable.swift │ ├── TermProtocol+CustomStringConvertible.swift │ ├── TermProtocol+Differentiatable.swift │ └── TermProtocol.swift │ ├── Polynomial │ ├── PolynomialProtocol+Negatable.swift │ ├── PolynomialProtocol+AbsoluteConvertible.swift │ ├── PolynomialProtocol+Equatable.swift │ ├── PolynomialProtocol+Concrete.swift │ ├── PolynomialProtocol+Substitutionable.swift │ ├── PolynomialProtocol+Differentiatable.swift │ ├── PolynomialProtocol+CustomStringConvertible.swift │ ├── PolynomialProtocol.swift │ └── PolynomialProtocol+Concatenation.swift │ ├── MathematicalOperators │ ├── ModularEvaluationOperand.swift │ ├── Variable+Assignment.swift │ ├── PolynomialEvaluationSubtitutionOfVariablesToConstantsOperators.swift │ ├── PolynomialEvaluationOperators.swift │ ├── IntermediateOperandsFor_N-ary_Operators │ │ ├── CongruentEqualityOperand.swift │ │ └── NumberAndConstants.swift │ └── PolynomialCongruenceEvaluationOperators.swift │ ├── NumberExpressible │ ├── IntegerNumberExpressible.swift │ ├── FloatingPointNumberExpressible.swift │ ├── Int+IntegerNumberExpressible.swift │ ├── Double+FloatingPointNumberExpressible.swift │ └── NumberExpressible.swift │ ├── CustomOperators.swift │ ├── Extensions │ ├── Comparable_Extension.swift │ ├── Set_Extension.swift │ └── Array_Extension.swift │ ├── Exponentiation │ ├── ExponentiationProtocol+Comparable.swift │ ├── ExponentiationProtocol+Concrete.swift │ ├── ExponentiationProtocol.swift │ ├── ExponentiationProtocol+Substitionable.swift │ ├── ExponentiationProtocol+CustomStringConvertible.swift │ └── ExponentiationProtocol+Differentiatable.swift │ ├── SortingOfTerms │ ├── TermSorting.swift │ ├── Sorting.swift │ ├── SortingWithinTerm.swift │ └── SortingBetweenTerms.swift │ └── NamedVariables │ ├── NamedVariable.swift │ ├── Constant.swift │ └── Variable.swift ├── Tests ├── EquationKitSupportBigIntTests │ ├── LinuxMain.swift │ └── Tests │ │ ├── XCTestManifests.swift │ │ ├── BigIntAdditionTests.swift │ │ └── MathematicalOperatorTests.swift └── EquationKitSupportDoubleTests │ ├── LinuxMain.swift │ ├── Tests │ ├── XCTestManifests.swift │ ├── ConstantSubstitutionTests.swift │ ├── Concatenation │ │ ├── Multiplication │ │ │ └── ConcatenationByMultiplicationTests.swift │ │ ├── Addition │ │ │ └── ConcatenationByAdditionTests.swift │ │ └── Subtraction │ │ │ └── ConcatenationBySubtractionTests.swift │ ├── ExponentiationArraySortingTests.swift │ ├── ExploringRandomStuffTests.swift │ ├── PolynomialMultipliedByPolynomialTests.swift │ ├── PolynomialsExponentiatedTests.swift │ ├── Differentiation │ │ └── SimpleDifferentiationTests.swift │ └── TermSortingTests.swift │ └── DoubleTestsBase.swift ├── Package.resolved ├── Package.swift ├── README.md └── LICENSE /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /*XCTestManifests.swift 3 | /Test/LinuxMain.swift 4 | /.build 5 | /Packages 6 | /*.xcodeproj 7 | /.swiftpm 8 | -------------------------------------------------------------------------------- /.xcodesamplecode.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportBigInt/EquationKitSupportBigInt.swift: -------------------------------------------------------------------------------- 1 | // A module that re-exports the complete EquationKitSupportBigInt public API. 2 | @_exported import EquationKitBigIntOperators 3 | @_exported import EquationKitBigIntVariables 4 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportDouble/EquationKitSupportDouble.swift: -------------------------------------------------------------------------------- 1 | // A module that re-exports the complete EquationKitSupportDouble public API. 2 | @_exported import EquationKitDoubleOperators 3 | @_exported import EquationKitDoubleVariables 4 | -------------------------------------------------------------------------------- /Sources/EquationKit/Protocols/Evaluatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Evaluatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportBigIntTests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import EquationKitSupportBigIntTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += BigIntAdditionTests.allTests() 7 | tests += MathematicalOperatorTests.allTests() 8 | XCTMain(tests) 9 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportBigInt/README.md: -------------------------------------------------------------------------------- 1 | # EquationKitSupportBigInt 2 | 3 | This umbrella module provides an easy way to get access to both the `BigInt` supoort for *Operators* and *Variables* with a single import statement: 4 | 5 | ```swift 6 | import EquationKitSupportBigInt 7 | ``` 8 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportDouble/README.md: -------------------------------------------------------------------------------- 1 | # EquationKitSupportDouble 2 | 3 | This umbrella module provides an easy way to get access to both the `Doubble` supoort for *Operators* and *Variables* with a single import statement: 4 | 5 | ```swift 6 | import EquationKitSupportDouble 7 | ``` 8 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportBigIntTests/Tests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(BigIntAdditionTests.allTests), 7 | testCase(MathematicalOperatorTests.allTests) 8 | ] 9 | } 10 | #endif 11 | -------------------------------------------------------------------------------- /Sources/EquationKit/Modulus/ModulusMode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModulusMode.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ModulusMode { 12 | /// Swift style 13 | case allowNegative 14 | 15 | /// Python Style 16 | case alwaysPositive 17 | } 18 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "BigInt", 6 | "repositoryURL": "https://github.com/attaswift/BigInt", 7 | "state": { 8 | "branch": null, 9 | "revision": "19f5e8a48be155e34abb98a2bcf4a343316f0343", 10 | "version": "5.0.0" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+Negatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+Negatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Negatable 12 | public extension TermProtocol { 13 | func negated() -> Self { 14 | return Self(exponentiations: exponentiations, coefficient: coefficient.negated())! 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol+Negatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol+Negatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Negatable 12 | public extension PolynomialProtocol { 13 | func negated() -> Self { 14 | return Self(terms: terms.negated(), constant: constant.negated()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/EquationKit/Protocols/Negatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Negatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Negatable { 12 | func negated() -> Self 13 | } 14 | 15 | extension Array: Negatable where Element: Negatable { 16 | public func negated() -> [Element] { 17 | return map { $0.negated() } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/EquationKit/MathematicalOperators/ModularEvaluationOperand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModularEvaluationOperand.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-01. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public func % (scalar: N, modulusConstants: ModularEvaluationOperand) -> CongruentEqualityOperand { 12 | return CongruentEqualityOperand(scalar: scalar, modulusConstants: modulusConstants) 13 | } 14 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+AbsoluteConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+AbsoluteConvertible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-31. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - AbsoluteConvertible 12 | public extension TermProtocol { 13 | func absolute() -> Self { 14 | return Self(exponentiations: exponentiations, coefficient: coefficient.absolute())! 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol+AbsoluteConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol+AbsoluteConvertible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-31. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - AbsoluteConvertible 12 | public extension PolynomialProtocol { 13 | func absolute() -> Self { 14 | return Self(terms: terms.absolute(), constant: constant.absolute()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol+Equatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol+Equatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Equatable 12 | public extension PolynomialProtocol { 13 | static func == (lhs: Self, rhs: Self) -> Bool { 14 | return lhs.constant == rhs.constant 15 | && lhs.terms.sorted() == rhs.terms.sorted() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sources/EquationKit/Protocols/AbsoluteConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AbsoluteConvertible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-31. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol AbsoluteConvertible { 12 | 13 | /// Turns `3y - 2x - 5` => `3y + 2x + 5` 14 | func absolute() -> Self 15 | 16 | } 17 | 18 | extension Array: AbsoluteConvertible where Element: AbsoluteConvertible { 19 | public func absolute() -> [Element] { 20 | return map { $0.absolute() } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/EquationKit/NumberExpressible/IntegerNumberExpressible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntegerNumberExpressible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol IntegerNumberExpressible: NumberExpressible, BinaryInteger { 12 | init(_ int: Int) 13 | } 14 | 15 | public extension IntegerNumberExpressible { 16 | func mod(_ modulus: Self, mode: ModulusMode) -> Self { 17 | return EquationKit.mod(self, modulus: Modulus(modulus, mode: mode)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/EquationKit/NumberExpressible/FloatingPointNumberExpressible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FloatingPointNumberExpressible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol FloatingPointNumberExpressible: NumberExpressible, BinaryFloatingPoint { 12 | init(_ float: Float) 13 | } 14 | 15 | public extension FloatingPointNumberExpressible { 16 | func mod(_ modulus: Self, mode: ModulusMode) -> Self { 17 | return EquationKit.mod(self, modulus: Modulus(modulus, mode: mode)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/EquationKit/MathematicalOperators/Variable+Assignment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Variable+Assignment.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-01. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | infix operator <-: AssignmentPrecedence 12 | infix operator ≔: AssignmentPrecedence 13 | 14 | public func <-(variable: VariableStruct, value: N) -> ConstantStruct where N: NumberExpressible { 15 | return ConstantStruct(variable, value: value) 16 | } 17 | public func ≔ (variable: VariableStruct, value: N) -> ConstantStruct { 18 | return variable <- value 19 | } 20 | -------------------------------------------------------------------------------- /Sources/EquationKit/CustomOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomOperators.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | precedencegroup ExponentiationPrecedence { 12 | higherThan: MultiplicationPrecedence 13 | associativity: left 14 | } 15 | 16 | infix operator ^^: ExponentiationPrecedence 17 | 18 | internal func += (lhs: inout N?, rhs: N) where N: Numeric { 19 | if let lhsIndeed = lhs { 20 | lhs = lhsIndeed + rhs 21 | } else { 22 | lhs = rhs 23 | } 24 | if lhs == 0 { 25 | lhs = nil 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/EquationKit/Protocols/Differentiatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Differentiatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias PolynomialType = PolynomialStruct>> 12 | 13 | public protocol NumberTypeSpecifying { 14 | associatedtype NumberType: NumberExpressible 15 | } 16 | 17 | public protocol Differentiatable: NumberTypeSpecifying { 18 | func differentiateWithRespectTo(_ variableToDifferentiate: VariableStruct) -> PolynomialType? 19 | } 20 | 21 | -------------------------------------------------------------------------------- /Sources/EquationKit/Extensions/Comparable_Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Comparable_Extension.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-21. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Comparable { 12 | func compare(to other: Self) -> ComparisonResult { 13 | if self > other { 14 | return .orderedDescending 15 | } else if self < other { 16 | return .orderedAscending 17 | } else if self == other { 18 | return .orderedSame 19 | } else { 20 | fatalError("should not happen.") 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | import EquationKitSupportDoubleTests 4 | 5 | var tests = [XCTestCaseEntry]() 6 | tests += ConcatenationByAdditionTests.allTests) 7 | tests += ConcatenationByMultiplicationTests.allTests) 8 | tests += ConcatenationBySubtractionTests.allTests) 9 | tests += SimpleDifferentiationTests.allTests) 10 | tests += ConstantSubstitutionTests.allTests) 11 | tests += ExploringRandomStuffTests.allTests) 12 | tests += ExponentiationArraySortingTests.allTests) 13 | tests += PolynomialMultipliedByPolynomialTests.allTests) 14 | tests += PolynomialsExponentiatedTests.allTests) 15 | tests += TermSortingTests.allTests) 16 | XCTMain(tests) 17 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+Comparable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+Comparable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Equatable 12 | public extension TermProtocol { 13 | static func == (lhs: Self, rhs: Self) -> Bool { 14 | return lhs.exponentiations.sorted() == rhs.exponentiations.sorted() && lhs.coefficient == rhs.coefficient 15 | } 16 | } 17 | 18 | // MARK: - Comparable 19 | extension TermProtocol { 20 | public static func < (lhs: Self, rhs: Self) -> Bool { 21 | return [rhs, lhs].sorted() == [lhs, rhs] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+Concatenation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+Concatenation.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Private Extension Term 12 | public extension TermProtocol { 13 | func multipliedBy(other: Self) -> Self { 14 | return Self(exponentiations: exponentiations + other.exponentiations, coefficient: coefficient*other.coefficient)! 15 | } 16 | 17 | func multiplyingCoefficientBy(constant: NumberType) -> Self? { 18 | return Self(exponentiations: exponentiations, coefficient: coefficient * constant) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/EquationKit/Exponentiation/ExponentiationProtocol+Comparable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExponentiationProtocol+Comparable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Equatable 12 | public extension ExponentiationProtocol { 13 | static func == (lhs: Self, rhs: Self) -> Bool { 14 | return lhs.variable == rhs.variable && lhs.exponent == rhs.exponent 15 | } 16 | } 17 | 18 | // MARK: - Comparable 19 | extension ExponentiationProtocol { 20 | public static func < (lhs: Self, rhs: Self) -> Bool { 21 | return [rhs, lhs].sorted() == [lhs, rhs] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+Sorting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+Sorting.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension TermProtocol { 12 | 13 | func sortingExponentiations(by sorting: [SortingWithinTerm] = SortingWithinTerm.defaultArray) -> Self { 14 | return Self(exponentiations: exponentiations, sorting: sorting, coefficient: coefficient)! 15 | } 16 | 17 | func sortingExponentiations(by sorting: SortingWithinTerm) -> Self { 18 | return sortingExponentiations(by: [sorting]) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/EquationKit/Exponentiation/ExponentiationProtocol+Concrete.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExponentiationProtocol+Concrete.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - ExponentiationStruct 12 | public struct ExponentiationStruct: ExponentiationProtocol { 13 | 14 | public typealias NumberType = Number 15 | 16 | public let variable: VariableStruct 17 | public let exponent: NumberType 18 | 19 | public init(variable: VariableStruct, exponent: NumberType) { 20 | self.variable = variable 21 | self.exponent = exponent 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/XCTestManifests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | #if !canImport(ObjectiveC) 4 | public func allTests() -> [XCTestCaseEntry] { 5 | return [ 6 | testCase(ConcatenationByAdditionTests.allTests), 7 | testCase(ConcatenationByMultiplicationTests.allTests), 8 | testCase(ConcatenationBySubtractionTests.allTests), 9 | testCase(SimpleDifferentiationTests.allTests), 10 | testCase(ConstantSubstitutionTests.allTests), 11 | testCase(ExploringRandomStuffTests.allTests), 12 | testCase(ExponentiationArraySortingTests.allTests), 13 | testCase(PolynomialMultipliedByPolynomialTests.allTests), 14 | testCase(PolynomialsExponentiatedTests.allTests), 15 | testCase(TermSortingTests.allTests), 16 | ] 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /Sources/EquationKit/Extensions/Set_Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Set_Extension.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-02. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Set { 12 | /// Returns the union between this and `other` but `Element` being that of `other`. 13 | /// 1. Maps the set `other` to the same elements as this set: 14 | /// 2. Performs the union between these to sets 15 | /// 3. Returns the elements in the original set `other`, that exists in the union. 16 | public func unionTo(other: Set, transform: (O) -> (Element)) -> Set { 17 | return Set(union(Set(other.map(transform))).compactMap { element in other.first(where: { element == transform($0) }) }) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Sources/EquationKit/MathematicalOperators/PolynomialEvaluationSubtitutionOfVariablesToConstantsOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialEvaluationSubtitutionOfVariablesToConstantsOperators.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-01. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Non special unicode char operators 12 | infix operator <--: BitwiseShiftPrecedence 13 | public func <-- (number: N, constants: [ConstantStruct]) -> NumberAndConstants { 14 | return NumberAndConstants(number: number, constants: constants) 15 | } 16 | 17 | // MARK: Special unicode char corresponding operators 18 | infix operator ↤: BitwiseShiftPrecedence 19 | public func ↤ (number: N, constants: [ConstantStruct]) -> NumberAndConstants { 20 | return number <-- constants 21 | } 22 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+Concrete.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+Concrete.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct TermStruct: TermProtocol { 12 | 13 | public typealias NumberType = Exponentiation.NumberType 14 | public typealias ExponentiationType = Exponentiation 15 | public let coefficient: NumberType 16 | public let exponentiations: [ExponentiationType] 17 | 18 | public init?(exponentiations: [ExponentiationType], sorting: [SortingWithinTerm], coefficient: NumberType) { 19 | guard coefficient != 0 else { return nil } 20 | self.exponentiations = exponentiations.merged().sorted(by: sorting) 21 | self.coefficient = coefficient 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/EquationKit/Exponentiation/ExponentiationProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExponentiationProtocol.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ExponentiationProtocol: Algebraic, Comparable { 12 | var variable: VariableStruct { get } 13 | var exponent: NumberType { get } 14 | init(variable: VariableStruct, exponent: NumberType) 15 | } 16 | 17 | // MARK: - Convenience Initializers 18 | public extension ExponentiationProtocol { 19 | init(_ variable: VariableStruct, exponent: NumberType = .one) { 20 | self.init(variable: variable, exponent: exponent) 21 | } 22 | 23 | init(_ name: String, exponent: NumberType = .one) { 24 | self.init(VariableStruct(name), exponent: exponent) 25 | } 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/EquationKit/MathematicalOperators/PolynomialEvaluationOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialEvaluationOperators.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-01. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Non special unicode char operators 12 | public func == (polynomial: PolynomialType, evaluationOperand: EvaluationOperand) -> Bool { 13 | return polynomial.evaluate(constants: evaluationOperand.constants) == evaluationOperand.reference 14 | } 15 | public func != (polynomial: PolynomialType, evaluationOperand: EvaluationOperand) -> Bool { 16 | return !(polynomial == evaluationOperand) 17 | } 18 | 19 | // MARK: Special unicode char corresponding operators 20 | infix operator ≠: AssignmentPrecedence 21 | public func ≠ (polynomial: PolynomialType, evaluationProxy: EvaluationOperand) -> Bool { 22 | return polynomial != evaluationProxy 23 | } 24 | -------------------------------------------------------------------------------- /Sources/EquationKit/SortingOfTerms/TermSorting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermSorting.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-21. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct TermSorting { 12 | public let betweenTerms: [SortingBetweenTerms] 13 | public let withinTerm: [SortingWithinTerm] 14 | public init(betweenTerms: [SortingBetweenTerms] = SortingBetweenTerms.defaultArray, withinTerm: [SortingWithinTerm] = SortingWithinTerm.defaultArray) { 15 | self.betweenTerms = betweenTerms 16 | self.withinTerm = withinTerm 17 | } 18 | } 19 | 20 | public extension TermSorting { 21 | 22 | init(betweenTerms: SortingBetweenTerms) { 23 | self.init(betweenTerms: [betweenTerms]) 24 | } 25 | 26 | static var `default`: TermSorting { 27 | return TermSorting() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/EquationKit/Modulus/ModulusFunction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModulusFunction.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public func mod(_ number: T, modulus: Modulus) -> T where T: BinaryInteger { 12 | var mod = number % modulus.number 13 | guard modulus.mode == .alwaysPositive else { return mod } 14 | if mod < 0 { 15 | mod = mod + modulus.number 16 | } 17 | guard mod >= 0 else { fatalError("NEGATIVE VALUE") } 18 | return mod 19 | } 20 | 21 | public func mod(_ number: F, modulus: Modulus) -> F where F: BinaryFloatingPoint { 22 | var mod = number.truncatingRemainder(dividingBy: modulus.number) 23 | guard modulus.mode == .alwaysPositive else { return mod } 24 | if mod < 0 { 25 | mod = mod + modulus.number 26 | } 27 | guard mod >= 0 else { fatalError("NEGATIVE VALUE") } 28 | return mod 29 | } 30 | -------------------------------------------------------------------------------- /Sources/EquationKit/MathematicalOperators/IntermediateOperandsFor_N-ary_Operators/CongruentEqualityOperand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CongruentEqualityOperand.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-01. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct CongruentEqualityOperand { 12 | public let scalar: Number 13 | public let modulus: Modulus 14 | public let constants: Set> 15 | 16 | public init(scalar: Number, modulus: Number, constants: Set>) { 17 | self.scalar = scalar 18 | self.modulus = Modulus(modulus) 19 | self.constants = constants 20 | } 21 | } 22 | 23 | public extension CongruentEqualityOperand { 24 | public init(scalar: Number, modulusConstants: ModularEvaluationOperand) { 25 | self.init(scalar: scalar, modulus: modulusConstants.modulus, constants: modulusConstants.constants) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/EquationKit/NumberExpressible/Int+IntegerNumberExpressible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int+IntegerNumberExpressible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Int: IntegerNumberExpressible {} 12 | public extension Int { 13 | 14 | static var zero: Int { return 0 } 15 | static var one: Int { return 1 } 16 | 17 | var shortFormat: String { 18 | return "\(self)" 19 | } 20 | 21 | var isNegative: Bool { 22 | return self < 0 23 | } 24 | 25 | var asInteger: Int { 26 | return self 27 | } 28 | 29 | var isPositive: Bool { 30 | return self > 0 31 | } 32 | 33 | func absolute() -> Int { 34 | return abs(self) 35 | } 36 | 37 | func negated() -> Int { 38 | return -self 39 | } 40 | 41 | 42 | func raised(to exponent: Int) -> Int { 43 | return Int(pow(Double(self), Double(exponent))) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/EquationKit/Exponentiation/ExponentiationProtocol+Substitionable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExponentiationProtocol+Substitionable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Substitionable 12 | public extension ExponentiationProtocol { 13 | 14 | var uniqueVariables: Set> { 15 | return variable.uniqueVariables 16 | } 17 | 18 | func substitute(constants: Set>, modulus: Modulus?) -> PolynomialType { 19 | 20 | return parse( 21 | substitutionable: variable, 22 | constants: constants, 23 | modulus: modulus, 24 | handleNumber: { base in 25 | base.raised(to: exponent) 26 | }, 27 | handleAlgebraic: { poly in 28 | return poly.raised(to: self.exponent.asInteger) 29 | } 30 | ) 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol+Concrete.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol+Concrete.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct PolynomialStruct: PolynomialProtocol { 12 | 13 | public typealias NumberType = Term.NumberType 14 | public typealias TermType = Term 15 | 16 | public let constant: NumberType 17 | public let terms: [TermType] 18 | 19 | public init(terms: [TermType], sorting: TermSorting, constant: NumberType) { 20 | self.terms = terms.merged().sorted(by: sorting) 21 | self.constant = constant 22 | } 23 | } 24 | 25 | // MARK: - ExpressibleByIntegerLiteral 26 | extension PolynomialStruct: ExpressibleByIntegerLiteral where Term.NumberType == Double { 27 | public typealias IntegerLiteralType = Int 28 | public init(integerLiteral value: Int) { 29 | self.init(constant: NumberType(value)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/EquationKit/Extensions/Array_Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array_Extension.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Array where Element: Hashable { 12 | public func containsDuplicates() -> Bool { 13 | return Set(self).count != count 14 | } 15 | } 16 | 17 | extension Array where Element: Equatable { 18 | func removed(element: Element) -> [Element] { 19 | guard contains(element) else { return self } 20 | var elements = self 21 | elements.removeAll { $0 == element } 22 | return elements 23 | } 24 | } 25 | 26 | public extension Array { 27 | func droppingFirst() -> [Element] { 28 | guard count > 1 else { return [] } 29 | return Array(dropFirst()) 30 | } 31 | 32 | func droppingFirstNilIfEmpty() -> [Element]? { 33 | guard count > 1 else { return nil } 34 | return Array(dropFirst()) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol+Substitutionable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol+Substitutionable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Substitutionable 12 | public extension PolynomialProtocol { 13 | 14 | var uniqueVariables: Set> { 15 | return Set(terms.flatMap { Array($0.uniqueVariables) }) 16 | } 17 | 18 | func substitute(constants: Set>, modulus: Modulus?) -> PolynomialType { 19 | return parseMany( 20 | substitutionables: terms, 21 | constants: constants, 22 | modulus: modulus, 23 | manyHandleAllNumbers: { values in 24 | values.reduce(self.constant, { $0 + $1 }) 25 | }, 26 | manyHandleMixedReduce: (initialResult: PolynomialType(constant: NumberType.zero), combine: { 27 | $0.adding(other: $1) 28 | }) 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+Substitionable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+Substitionable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Substitionable 12 | public extension TermProtocol { 13 | 14 | var uniqueVariables: Set> { 15 | return Set(exponentiations.map { $0.variable }) 16 | } 17 | 18 | func substitute(constants: Set>, modulus: Modulus?) -> PolynomialType { 19 | return parseMany( 20 | substitutionables: exponentiations, 21 | constants: constants, 22 | modulus: modulus, 23 | manyHandleAllNumbers: { values in 24 | values.reduce(NumberType.one, { $0 * $1 }) * coefficient 25 | }, 26 | manyHandleMixedReduce: (initialResult: PolynomialType(constant: coefficient), combine: { 27 | PolynomialType($0).multipliedBy(other: PolynomialType($1)) 28 | }) 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/EquationKit/Protocols/Algebraic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Algebraic.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-17. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Shared protocol by all algebraic types. 12 | public protocol Atom {} 13 | 14 | public protocol Algebraic: Atom, Differentiatable, Substitutionable, Hashable, CustomStringConvertible {} 15 | 16 | public extension PolynomialProtocol { 17 | 18 | init(_ atom: Atom) { 19 | // if let constant = atom as? NumberType { 20 | // self.init(constant: constant) 21 | // } else 22 | if let variable = atom as? VariableStruct { 23 | self.init(variable: variable) 24 | } else if let exponentiation = atom as? ExponentiationType { 25 | self.init(exponentiation: exponentiation) 26 | } else if let term = atom as? TermType { 27 | self.init(term: term) 28 | } else if let polynomial = atom as? Self { 29 | self.init(polynomial: polynomial) 30 | } else { 31 | fatalError("incorrect implementation") 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/ConstantSubstitutionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConstantSubstitutionTests.swift 3 | // EquationKitDoubleTests 4 | // 5 | // Created by Alexander Cyon on 2018-09-02. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import EquationKit 12 | @testable import EquationKitDoubleSupport 13 | 14 | class ConstantSubstitutionTests: DoubleTestsBase { 15 | 16 | static var allTests = [ 17 | ("testReplaceYWith2", testReplaceYWith2), 18 | ("testWeierstrass", testWeierstrass), 19 | ] 20 | 21 | func testReplaceYWith2() { 22 | let eq = x + y 23 | XCTAssertEqual( 24 | x + 2, 25 | eq.substitute() {[ y <- 2 ]} 26 | ) 27 | } 28 | 29 | func testWeierstrass() { 30 | let 𝑥 = Variable("𝑥") 31 | let 𝑦 = Variable("𝑦") 32 | let 𝑥³ = 𝑥^^3 33 | let 𝑦² = 𝑦^^2 34 | let 𝑎 = Variable("𝑎") 35 | let 𝑏 = Variable("𝑏") 36 | let eq = 𝑦² - 𝑥³ - 𝑎*𝑥 - 𝑏 37 | XCTAssertEqual(eq.substitute() {[ 𝑎 <- 0, 𝑏 <- 7 ]}, 𝑦² - 𝑥³ - 7) 38 | } 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /Sources/EquationKit/NumberExpressible/Double+FloatingPointNumberExpressible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double+FloatingPointNumberExpressible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Double: FloatingPointNumberExpressible {} 12 | public extension Double { 13 | 14 | static var zero: Double { return 0 } 15 | static var one: Double { return 1 } 16 | var shortFormat: String { 17 | let decimalsEqualToZero = truncatingRemainder(dividingBy: 1) == 0 18 | let format = decimalsEqualToZero ? "%.0f" : "%.2f" 19 | return String(format: format, self) 20 | } 21 | 22 | var asInteger: Int { 23 | return Int(exactly: self)! 24 | } 25 | 26 | var isNegative: Bool { 27 | return self < 0 28 | } 29 | 30 | var isPositive: Bool { 31 | return self > 0 32 | } 33 | 34 | func absolute() -> Double { 35 | return abs(self) 36 | } 37 | 38 | func negated() -> Double { 39 | return -self 40 | } 41 | 42 | func raised(to exponent: Double) -> Double { 43 | return pow(self, exponent) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/EquationKit/SortingOfTerms/Sorting.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sorting.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-21. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Sorting: Equatable { 12 | associatedtype TypeToSort 13 | typealias AreInIncreasingOrder = (TypeToSort, TypeToSort) -> (Bool) 14 | func areInIncreasingOrder(tieBreakers: [Self]?) -> AreInIncreasingOrder 15 | var comparing: (TypeToSort, TypeToSort) -> (ComparisonResult) { get } 16 | var targetComparisonResult: ComparisonResult { get } 17 | } 18 | 19 | public extension Sorting { 20 | 21 | func areInIncreasingOrder(tieBreakers: [Self]?) -> AreInIncreasingOrder { 22 | return { 23 | let comparison = self.comparing($0, $1) 24 | guard comparison != .orderedSame else { 25 | if let tieBreakers = tieBreakers, let tieBreaker = tieBreakers.removed(element: self).first { 26 | return tieBreaker.areInIncreasingOrder(tieBreakers: tieBreakers.droppingFirst())($0, $1) 27 | } else { 28 | return false 29 | } 30 | } 31 | return comparison == self.targetComparisonResult 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/EquationKit/NumberExpressible/NumberExpressible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberExpressible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-22. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol NumberExpressible: Numeric, Negatable, AbsoluteConvertible, Hashable, Comparable { 12 | 13 | static func + (lhs: Self, rhs: Self) -> Self 14 | static func * (lhs: Self, rhs: Self) -> Self 15 | static func - (lhs: Self, rhs: Self) -> Self 16 | static func / (lhs: Self, rhs: Self) -> Self 17 | 18 | var asInteger: Int { get } 19 | 20 | func raised(to exponent: Self) -> Self 21 | func mod(_ modulus: Self, mode: ModulusMode) -> Self 22 | 23 | static var zero: Self { get } 24 | static var one: Self { get } 25 | 26 | var isNegative: Bool { get } 27 | var isPositive: Bool { get } 28 | var shortFormat: String { get } 29 | } 30 | 31 | public extension NumberExpressible { 32 | var isZero: Bool { 33 | return self == .zero 34 | } 35 | } 36 | 37 | public extension NumberExpressible { 38 | func modIfNeeded(_ modulus: Modulus?) -> Self { 39 | guard let modulus = modulus else { return self } 40 | return mod(modulus.number, mode: modulus.mode) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/EquationKit/MathematicalOperators/IntermediateOperandsFor_N-ary_Operators/NumberAndConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberAndConstants.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-01. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct NumberAndConstants { 12 | 13 | public let number: Number 14 | public let constants: Set> 15 | public init(number: Number, constants: Set>) { 16 | self.number = number 17 | self.constants = constants 18 | } 19 | 20 | } 21 | public extension NumberAndConstants { 22 | public init(number: Number, constants: [ConstantStruct]) { 23 | guard !constants.containsDuplicates() else { fatalError("Constants array should not contain duplicates") } 24 | self.init(number: number, constants: Set(constants)) 25 | } 26 | } 27 | 28 | public typealias EvaluationOperand = NumberAndConstants 29 | public extension EvaluationOperand { 30 | var reference: Number { return number } 31 | } 32 | 33 | public typealias ModularEvaluationOperand = NumberAndConstants 34 | public extension ModularEvaluationOperand { 35 | var modulus: Number { return number } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportBigIntTests/Tests/BigIntAdditionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BigIntAdditionTests.swift 3 | // EquationKitBigIntTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-25. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | 12 | @testable import EquationKit 13 | @testable import EquationKitBigIntSupport 14 | @testable import EquationKitBigIntSupport 15 | import BigInt 16 | 17 | class BigIntAdditionTests: XCTestCase { 18 | 19 | static var allTests = [ 20 | ("testSimpleBigIntTests", testSimpleBigIntTests), 21 | ("testAddition", testAddition), 22 | ("testBigExp", testBigExp), 23 | ] 24 | 25 | func testSimpleBigIntTests() { 26 | let f = BigInt("F", radix: 16)! 27 | let c = BigInt("C", radix: 16)! 28 | XCTAssertEqual(f-c, 3) 29 | XCTAssertEqual(f+c, 27) 30 | } 31 | 32 | func testAddition() { 33 | XCTAssertEqual((x + y).evaluate() {[ x <- 1 << 65, y <- 1 << 66 ]}!.description, "110680464442257309696") 34 | } 35 | 36 | func testBigExp() { 37 | XCTAssertEqual((x⁹ + 1).evaluate() { x <- 1 << 65 }!.description, "126633165554229521438977290762059361297987250739820462036000284719563379254544315991201997343356439034674007770120263341747898897565056619503383631412169301973302667340133957633") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/EquationKit/NamedVariables/NamedVariable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NamedVariable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol NamedVariable: NumberTypeSpecifying, CustomStringConvertible, Hashable, Comparable { 12 | var name: String { get } 13 | static func == (lhs: N, rhs: Self) -> Bool where N: NamedVariable, N.NumberType == Self.NumberType 14 | } 15 | 16 | public extension NamedVariable { 17 | static func == (lhs: N, rhs: Self) -> Bool where N: NamedVariable, N.NumberType == Self.NumberType { 18 | return lhs.name == rhs.name 19 | } 20 | } 21 | 22 | // MARK: - Equatable 23 | public extension NamedVariable { 24 | static func == (lhs: Self, rhs: Self) -> Bool { 25 | return lhs.name == rhs.name 26 | } 27 | } 28 | 29 | // MARK: - Comparable 30 | public extension NamedVariable { 31 | static func < (lhs: Self, rhs: Self) -> Bool { 32 | return lhs.name < rhs.name 33 | } 34 | } 35 | 36 | // MARK: - Hashable 37 | public extension NamedVariable { 38 | var hashValue: Int { 39 | return name.hashValue 40 | } 41 | } 42 | 43 | // MARK: - CustomStringConvertible 44 | public extension NamedVariable { 45 | var description: String { 46 | return name 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+CustomStringConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+CustomStringConvertible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - CustomStringConvertible 12 | public extension TermProtocol { 13 | 14 | func asString(sorting: SortingWithinTerm) -> String { 15 | return asString(sorting: [sorting]) 16 | } 17 | 18 | func asString(sorting: [SortingWithinTerm] = SortingWithinTerm.defaultArray) -> String { 19 | let sorted = sortingExponentiations(by: sorting) 20 | return sorted.description 21 | } 22 | 23 | var description: String { 24 | var exponentiationsString: String { 25 | return exponentiations.map { $0.description }.joined() 26 | } 27 | 28 | var coefficientString: String { 29 | let absoluteCoefficient = coefficient.absolute() 30 | guard absoluteCoefficient != .one else { return "" } 31 | return "\(absoluteCoefficient.shortFormat)" 32 | } 33 | 34 | return "\(coefficientString)\(exponentiationsString)" 35 | } 36 | } 37 | 38 | // MARK: - CustomDebugStringConvertible 39 | public extension TermProtocol { 40 | var debugDescription: String { 41 | return "\(signString)\(description)" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/EquationKit/Exponentiation/ExponentiationProtocol+CustomStringConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExponentiationProtocol+CustomStringConvertible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - CustomStringConvertible 12 | public extension ExponentiationProtocol { 13 | var description: String { 14 | guard exponent != 1 else { return variable.description } 15 | if exponent > 1 && exponent < 10 { 16 | let superscript: String 17 | if exponent == 2 { 18 | superscript = "²" 19 | } else if exponent == 3 { 20 | superscript = "³" 21 | } else if exponent == 4 { 22 | superscript = "⁴" 23 | } else if exponent == 5 { 24 | superscript = "⁵" 25 | } else if exponent == 6 { 26 | superscript = "⁶" 27 | } else if exponent == 7 { 28 | superscript = "⁷" 29 | } else if exponent == 8 { 30 | superscript = "⁸" 31 | } else if exponent == 9 { 32 | superscript = "⁹" 33 | } else { fatalError("forgot number") } 34 | return "\(variable)\(superscript)" 35 | } else { 36 | return "\(variable)^\(exponent.shortFormat)" 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportBigInt/Operators/BigInt+IntegerNumberExpressible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BigInt+IntegerNumberExpressible.swift 3 | // EquationKitBigIntTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import EquationKit 11 | 12 | /// https://github.com/attaswift/BigInt 13 | import BigInt 14 | 15 | 16 | extension BigInt: IntegerNumberExpressible { 17 | public var asInteger: Int { 18 | guard bitWidth < 32 else { fatalError("overflow") } 19 | return Int(description)! 20 | } 21 | } 22 | 23 | public extension BigInt { 24 | var isNegative: Bool { 25 | return self < 0 26 | } 27 | 28 | var isPositive: Bool { 29 | return self > 0 30 | } 31 | 32 | func absolute() -> BigInt { 33 | return BigInt(sign: .plus, magnitude: magnitude) 34 | } 35 | 36 | func raised(to exponent: BigInt) -> BigInt { 37 | guard exponent.bitWidth <= Int.bitWidth else { fatalError("to big") } 38 | return power(Int(exponent)) 39 | } 40 | 41 | static var zero: BigInt { 42 | return 0 43 | } 44 | 45 | static var one: BigInt { 46 | return 1 47 | } 48 | 49 | var shortFormat: String { 50 | return description 51 | } 52 | 53 | func negated() -> BigInt { 54 | return BigInt(sign: sign == .plus ? .minus : .plus, magnitude: magnitude) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Sources/EquationKit/Modulus/Modulus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Modulus.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-03. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Modulus { 12 | public let number: Number 13 | public let mode: ModulusMode 14 | public init(_ number: Number, mode: ModulusMode = .alwaysPositive) { 15 | self.number = number 16 | self.mode = mode 17 | } 18 | } 19 | 20 | extension Modulus: ExpressibleByIntegerLiteral where Number: InitializableByInteger {//where Number: IntegerNumberExpressible { 21 | public typealias IntegerLiteralType = Int 22 | 23 | public init(integerLiteral value: IntegerLiteralType) { 24 | self.init(Number(value)) 25 | } 26 | } 27 | 28 | extension Modulus: ExpressibleByFloatLiteral where Number: InitializableByFloat {//where Number: FloatingPointNumberExpressible { 29 | public typealias FloatLiteralType = Float 30 | 31 | public init(floatLiteral value: FloatLiteralType) { 32 | self.init(Number(value)) 33 | } 34 | } 35 | 36 | public protocol InitializableByInteger { 37 | init(_ int: Int) 38 | } 39 | public protocol InitializableByFloat { 40 | init(_ float: Float) 41 | } 42 | extension Int: InitializableByInteger {} 43 | extension Double: InitializableByInteger {} 44 | extension Int: InitializableByFloat {} 45 | extension Double: InitializableByFloat {} 46 | -------------------------------------------------------------------------------- /Sources/EquationKit/MathematicalOperators/PolynomialCongruenceEvaluationOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialCongruenceEvaluationOperators.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-01. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Non special unicode char operators 12 | infix operator =%=: AssignmentPrecedence 13 | public func =%= (polynomial: PolynomialType, congruence: CongruentEqualityOperand) -> Bool { 14 | return polynomial.evaluate(constants: congruence.constants, modulus: congruence.modulus) == congruence.scalar 15 | } 16 | 17 | infix operator =!%=: AssignmentPrecedence 18 | public func =!%= (polynomial: PolynomialType, congruence: CongruentEqualityOperand) -> Bool { 19 | return !(polynomial ≡≡ congruence) 20 | } 21 | 22 | // MARK: Special unicode char corresponding operators 23 | infix operator ≡≡: AssignmentPrecedence 24 | public func ≡≡ (polynomial: PolynomialType, congruence: CongruentEqualityOperand) -> Bool { 25 | return polynomial =%= congruence 26 | } 27 | 28 | infix operator !≡: AssignmentPrecedence 29 | public func !≡ (polynomial: PolynomialType, congruence: CongruentEqualityOperand) -> Bool { 30 | return polynomial =!%= congruence 31 | } 32 | 33 | infix operator ≢: AssignmentPrecedence 34 | public func ≢ (polynomial: PolynomialType, congruence: CongruentEqualityOperand) -> Bool { 35 | return polynomial !≡ congruence 36 | } 37 | -------------------------------------------------------------------------------- /Sources/EquationKit/Exponentiation/ExponentiationProtocol+Differentiatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExponentiationProtocol+Differentiatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Differentiatable 12 | public extension ExponentiationProtocol { 13 | 14 | func differentiateWithRespectTo(_ variableToDifferentiate: VariableStruct) -> PolynomialType? { 15 | 16 | guard let _ = variable.differentiateWithRespectTo(variableToDifferentiate) else { 17 | return PolynomialType(exponentiation: self as! PolynomialType.ExponentiationType) 18 | } 19 | 20 | let exponentPriorToDifferentiation = self.exponent 21 | let exponent = exponentPriorToDifferentiation - 1 22 | guard exponent > 0 else { 23 | // actually this is never used.... but makes us able to distinguish between 24 | // doing `exponentiations.append(exponentiation)` and doing 25 | // nothing in differentiation in TermProtocol 26 | return PolynomialType(constant: NumberType.one) 27 | } 28 | 29 | let exponentiation = PolynomialType.ExponentiationType(variable, exponent: exponent) 30 | let term = PolynomialType.TermType(exponentiation: exponentiation, coefficient: exponentPriorToDifferentiation)! 31 | return PolynomialType(term: term) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/Concatenation/Multiplication/ConcatenationByMultiplicationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConcatenationByMultiplicationTests.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-22. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | 10 | import Foundation 11 | import XCTest 12 | @testable import EquationKit 13 | @testable import EquationKitDoubleSupport 14 | 15 | class ConcatenationByMultiplicationTests: DoubleTestsBase { 16 | 17 | static var allTests = [ 18 | ("testX³", testX³), 19 | ("testVariableTimesVariableTimesVariable", testVariableTimesVariableTimesVariable), 20 | ] 21 | 22 | func testX³() { 23 | 24 | let eq = x*x*x 25 | XCTAssertEqual(eq, x³ + 0) 26 | XCTAssertEqual(eq.evaluate() { x <- 2 }!, 8) 27 | XCTAssertEqual(eq.evaluate() { x <- 3 }!, 27) 28 | XCTAssertEqual(eq.evaluate() { x <- 4 }!, 64) 29 | XCTAssertEqual(eq.evaluate() { x <- 5 }!, 125) 30 | } 31 | 32 | // Testing different ways of expressiong `x*y*z` 33 | func testVariableTimesVariableTimesVariable() { 34 | let xy = x*y 35 | let yx = y*x 36 | XCTAssertEqual(xy, yx) 37 | let yz = y*z 38 | 39 | let xy_z = xy*z 40 | let yx_z = yx*z 41 | XCTAssertEqual(xy_z, yx_z) 42 | 43 | let x_yz = x*yz 44 | XCTAssertEqual(x_yz, xy_z) 45 | XCTAssertEqual(x_yz, yx_z) 46 | XCTAssertEqual(x*y*z, x_yz) 47 | XCTAssertEqual(x*y*z, x_yz) 48 | 49 | XCTAssertEqual(Term(exponentiation: x³), Term(x, x, x)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportDouble/Variables/Double_Variables.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double_Variables.swift 3 | // EquationKitDoubleTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import EquationKit 11 | import EquationKitDoubleOperators 12 | 13 | public let x = Variable("x") 14 | public let y = Variable("y") 15 | public let z = Variable("z") 16 | 17 | public let x² = Exponentiation(x, exponent: 2) 18 | public let x³ = Exponentiation(x, exponent: 3) 19 | public let x⁴ = Exponentiation(x, exponent: 4) 20 | public let x⁵ = Exponentiation(x, exponent: 5) 21 | public let x⁶ = Exponentiation(x, exponent: 6) 22 | public let x⁷ = Exponentiation(x, exponent: 7) 23 | public let x⁸ = Exponentiation(x, exponent: 8) 24 | public let x⁹ = Exponentiation(x, exponent: 9) 25 | 26 | public let y² = Exponentiation(y, exponent: 2) 27 | public let y³ = Exponentiation(y, exponent: 3) 28 | public let y⁴ = Exponentiation(y, exponent: 4) 29 | public let y⁵ = Exponentiation(y, exponent: 5) 30 | public let y⁶ = Exponentiation(y, exponent: 6) 31 | public let y⁷ = Exponentiation(y, exponent: 7) 32 | public let y⁸ = Exponentiation(y, exponent: 8) 33 | public let y⁹ = Exponentiation(y, exponent: 9) 34 | 35 | public let z² = Exponentiation(z, exponent: 2) 36 | public let z³ = Exponentiation(z, exponent: 3) 37 | public let z⁴ = Exponentiation(z, exponent: 4) 38 | public let z⁵ = Exponentiation(z, exponent: 5) 39 | public let z⁶ = Exponentiation(z, exponent: 6) 40 | public let z⁷ = Exponentiation(z, exponent: 7) 41 | public let z⁸ = Exponentiation(z, exponent: 8) 42 | public let z⁹ = Exponentiation(z, exponent: 9) 43 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol+Differentiatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol+Differentiatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Differentiatable 12 | public extension TermProtocol { 13 | 14 | func differentiateWithRespectTo(_ variableToDifferentiate: VariableStruct) -> PolynomialType? { 15 | guard contains(variable: variableToDifferentiate) else { return nil } 16 | 17 | var exponentiations = [ExponentiationType]() 18 | var coefficient = self.coefficient 19 | for exponentiation in self.exponentiations { 20 | guard let differentiationResult = exponentiation.differentiateWithRespectTo(variableToDifferentiate) else { exponentiations.append(exponentiation); continue } 21 | 22 | guard let term = differentiationResult.terms.first else { continue } 23 | guard differentiationResult.terms.count == 1 else { fatalError("what? bad state...") } 24 | 25 | coefficient *= term.coefficient 26 | guard let exponentiation = term.exponentiations.first, term.exponentiations.count == 1 else { return nil } 27 | exponentiations.append(exponentiation as! ExponentiationType) 28 | } 29 | 30 | if exponentiations.count == 0 { 31 | return PolynomialType(constant: coefficient) 32 | } else { 33 | let term = Self(exponentiations: exponentiations, coefficient: coefficient) 34 | return PolynomialType(term: term as! PolynomialType.TermType) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/DoubleTestsBase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DoubleTestsBase.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import EquationKit 12 | @testable import EquationKitDoubleSupport 13 | 14 | class DoubleTestsBase: XCTestCase { 15 | 16 | lazy var tx = Term(x) 17 | lazy var -tx = tx.negated() 18 | 19 | lazy var ty = Term(y) 20 | lazy var -ty = ty.negated() 21 | 22 | lazy var tz = Term(z) 23 | lazy var -tz = tz.negated() 24 | 25 | lazy var tx² = Term(exponentiation: x²) 26 | lazy var -tx² = tx².negated() 27 | 28 | lazy var ty² = Term(exponentiation: y²) 29 | lazy var -ty² = ty².negated() 30 | 31 | lazy var tz² = Term(exponentiation: z²) 32 | lazy var -tz² = tz².negated() 33 | 34 | lazy var tx³ = Term(exponentiation: x³) 35 | lazy var -tx³ = tx³.negated() 36 | 37 | lazy var ty³ = Term(exponentiation: y³) 38 | lazy var -ty³ = ty³.negated() 39 | 40 | lazy var tz³ = Term(exponentiation: z³) 41 | lazy var -tz³ = tz³.negated() 42 | 43 | lazy var tx⁴ = Term(x⁴) 44 | 45 | lazy var txyz = Term(x, y, z) 46 | lazy var -txyz = txyz.negated() 47 | 48 | lazy var txy = Term(x, y) 49 | lazy var -txy = txy.negated() 50 | lazy var txz = Term(x, z) 51 | lazy var -txz = txz 52 | lazy var tx²z³ = Term(x², z³) 53 | lazy var -tx²z³ = tx²z³.negated() 54 | lazy var tx²y²z² = Term(x², y², z²) 55 | lazy var -tx²y²z² = tx²y²z².negated() 56 | lazy var tx²y²z³ = Term(x²,y²,z³) 57 | lazy var -tx²y²z³ = tx²y²z³.negated() 58 | 59 | } 60 | -------------------------------------------------------------------------------- /Sources/EquationKit/NamedVariables/Constant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constant.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol ConstantProtocol: NamedVariable { 12 | var name: String { get } 13 | var value: NumberType { get } 14 | init(name: String, value: NumberType) 15 | 16 | func toVariable() -> VariableStruct 17 | } 18 | 19 | // MARK: - Default Implementation 20 | public extension ConstantProtocol { 21 | func toVariable() -> VariableStruct { 22 | return VariableStruct(name) 23 | } 24 | } 25 | 26 | // MARK: - CustomStringConvertible 27 | public extension ConstantProtocol { 28 | var description: String { 29 | return "<\(name)=\(value.shortFormat)>" 30 | } 31 | } 32 | 33 | // MARK: - Convenience Initializers 34 | public extension ConstantProtocol { 35 | 36 | init(_ variable: VariableStruct, value: NumberType) { 37 | self.init(name: variable.name, value: value) 38 | } 39 | } 40 | 41 | public extension ConstantProtocol where NumberType: FloatingPointNumberExpressible { 42 | init(_ variable: VariableStruct, value: Int) { 43 | self.init(variable, value: NumberType(Double(value))) 44 | } 45 | } 46 | 47 | // MARK: - ConstantStruct 48 | public struct ConstantStruct: ConstantProtocol { 49 | public typealias NumberType = Number 50 | public let name: String 51 | public let value: NumberType 52 | public init(name: String, value: NumberType) { 53 | self.name = name 54 | self.value = value 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportBigInt/Variables/BigInt_Variables.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BigInt_Variables.swift 3 | // EquationKitBigIntTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import EquationKit 11 | import EquationKitBigIntOperators 12 | 13 | /// https://github.com/attaswift/BigInt 14 | import BigInt 15 | 16 | public let x = Variable("x") 17 | public let y = Variable("y") 18 | public let z = Variable("z") 19 | public let x² = Exponentiation(x, exponent: 2) 20 | public let x³ = Exponentiation(x, exponent: 3) 21 | public let x⁴ = Exponentiation(x, exponent: 4) 22 | public let x⁵ = Exponentiation(x, exponent: 5) 23 | public let x⁶ = Exponentiation(x, exponent: 6) 24 | public let x⁷ = Exponentiation(x, exponent: 7) 25 | public let x⁸ = Exponentiation(x, exponent: 8) 26 | public let x⁹ = Exponentiation(x, exponent: 9) 27 | 28 | public let y² = Exponentiation(y, exponent: 2) 29 | public let y³ = Exponentiation(y, exponent: 3) 30 | public let y⁴ = Exponentiation(y, exponent: 4) 31 | public let y⁵ = Exponentiation(y, exponent: 5) 32 | public let y⁶ = Exponentiation(y, exponent: 6) 33 | public let y⁷ = Exponentiation(y, exponent: 7) 34 | public let y⁸ = Exponentiation(y, exponent: 8) 35 | public let y⁹ = Exponentiation(y, exponent: 9) 36 | 37 | public let z² = Exponentiation(z, exponent: 2) 38 | public let z³ = Exponentiation(z, exponent: 3) 39 | public let z⁴ = Exponentiation(z, exponent: 4) 40 | public let z⁵ = Exponentiation(z, exponent: 5) 41 | public let z⁶ = Exponentiation(z, exponent: 6) 42 | public let z⁷ = Exponentiation(z, exponent: 7) 43 | public let z⁸ = Exponentiation(z, exponent: 8) 44 | public let z⁹ = Exponentiation(z, exponent: 9) 45 | 46 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol+Differentiatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol+Differentiatable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Differentiatable 12 | public extension PolynomialProtocol { 13 | 14 | func differentiateWithRespectTo(_ variableToDifferentiate: VariableStruct) -> PolynomialType? { 15 | guard contains(variable: variableToDifferentiate) else { return PolynomialType(constant: NumberType.zero) } 16 | var terms = [PolynomialType.TermType]() 17 | var constant: NumberType = .zero 18 | for term in self.terms { 19 | guard let differentiationResult = term.differentiateWithRespectTo(variableToDifferentiate) else { continue } 20 | if differentiationResult.terms.isEmpty && !differentiationResult.constant.isZero { 21 | constant += differentiationResult.constant 22 | } else if differentiationResult.terms.count == 1 && differentiationResult.constant.isZero, let term = differentiationResult.terms.first { 23 | terms.append(term) 24 | } else { 25 | fatalError("should not happen") 26 | } 27 | 28 | } 29 | return PolynomialType(terms: terms, constant: constant) 30 | } 31 | } 32 | 33 | // MARK: - Public Extensions 34 | public extension PolynomialProtocol { 35 | func contains(variable: VariableStruct) -> Bool { 36 | for term in terms { 37 | guard term.contains(variable: variable) else { continue } 38 | return true 39 | } 40 | return false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol+CustomStringConvertible.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol+CustomStringConvertible.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - CustomStringConvertible 12 | public extension PolynomialProtocol { 13 | 14 | func sortingTerms(sorting: TermSorting) -> Self { 15 | return Self(terms: terms, sorting: sorting, constant: constant) 16 | } 17 | 18 | func asString(sorting betweenTerms: SortingBetweenTerms) -> String { 19 | return asString(sorting: TermSorting(betweenTerms: betweenTerms)) 20 | } 21 | 22 | func asString(sorting: TermSorting) -> String { 23 | let sortedPolynomial = sortingTerms(sorting: sorting) 24 | return sortedPolynomial.description 25 | } 26 | 27 | var description: String { 28 | var constantString: String { 29 | guard constant != .zero else { return "" } 30 | let constantSignString = constant.isPositive ? "+" : "-" 31 | return " \(constantSignString) \(constant.absolute().shortFormat)" 32 | } 33 | 34 | var termsString: String { 35 | func termString(index: Int, term: TermType) -> String { 36 | let signStringIfNegative = term.isNegative ? term.signString : "" 37 | let signString = (index == 0 || index == terms.endIndex) ? "\(signStringIfNegative)" : " \(term.signString) " 38 | return "\(signString)\(term.description)" 39 | 40 | } 41 | return terms.enumerated().map { termString(index: $0, term: $1) }.joined() 42 | } 43 | 44 | return "\(termsString)\(constantString)" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportDouble/Operators/Double_Operators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double_Operators.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-25. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import EquationKit 11 | 12 | public typealias Variable = VariableStruct 13 | public typealias Constant = ConstantStruct 14 | public typealias Polynomial = PolynomialType 15 | 16 | public typealias Exponentiation = ExponentiationStruct 17 | public typealias Term = TermStruct 18 | 19 | public func +(lhs: Atom, rhs: Atom) -> PolynomialType { 20 | return Polynomial(lhs).adding(other: Polynomial(rhs)) 21 | } 22 | 23 | public func +(lhs: Atom, rhs: Double) -> PolynomialType { 24 | return Polynomial(lhs).adding(constant: rhs) 25 | } 26 | 27 | public func +(lhs: Double, rhs: Atom) -> PolynomialType { 28 | return Polynomial(rhs).adding(constant: lhs) 29 | } 30 | 31 | public func -(lhs: Atom, rhs: Atom) -> PolynomialType { 32 | return Polynomial(lhs).subtracting(other: Polynomial(rhs)) 33 | } 34 | 35 | public func -(lhs: Atom, rhs: Double) -> PolynomialType { 36 | return Polynomial(lhs).subtracting(constant: rhs) 37 | } 38 | 39 | public func -(lhs: Double, rhs: Atom) -> PolynomialType { 40 | return Polynomial(rhs).negated().adding(constant: lhs) 41 | } 42 | 43 | public func *(lhs: Atom, rhs: Atom) -> PolynomialType { 44 | return Polynomial(lhs).multipliedBy(other: Polynomial(rhs)) 45 | } 46 | 47 | public func *(lhs: Atom, rhs: Double) -> PolynomialType { 48 | return Polynomial(lhs).multipliedBy(constant: rhs) 49 | } 50 | 51 | public func *(lhs: Double, rhs: Atom) -> PolynomialType { 52 | return rhs * lhs 53 | } 54 | 55 | public func ^^(lhs: Atom, rhs: Int) -> PolynomialType { 56 | return Polynomial(lhs).raised(to: rhs) 57 | } 58 | -------------------------------------------------------------------------------- /Sources/EquationKit/Protocols/Substitutionable+Evaluate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Substitutionable+Evaluate.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-02. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Substitutionable { 12 | 13 | func evaluate(constants: Set>, modulus: Modulus? = nil) -> NumberType? { 14 | return substitute(constants: constants, modulus: modulus).asNumber 15 | } 16 | 17 | func evaluate(constants: [ConstantStruct], modulus: Modulus? = nil) -> NumberType? { 18 | guard !constants.containsDuplicates() else { fatalError("cannot contain duplicates") } 19 | return evaluate(constants: Set(constants), modulus: modulus) 20 | } 21 | 22 | func evaluate(constants: ConstantStruct..., modulus: Modulus? = nil) -> NumberType? { 23 | return evaluate(constants: constants, modulus: modulus) 24 | } 25 | 26 | func evaluate(modulus: Modulus? = nil, makeConstants: () -> [ConstantStruct]) -> NumberType? { 27 | return evaluate(constants: makeConstants(), modulus: modulus) 28 | } 29 | 30 | func evaluate(modulus: Modulus? = nil, makeConstants: () -> ConstantStruct) -> NumberType? { 31 | return evaluate(constants: [makeConstants()], modulus: modulus) 32 | } 33 | 34 | func evaluate(constants: [VariableStruct: NumberType], modulus: Modulus? = nil) -> NumberType? { 35 | return evaluate(constants: constants.map { ConstantStruct($0, value: $1) }, modulus: modulus) 36 | } 37 | 38 | func evaluate(modulus: Modulus? = nil, makeConstants: () -> [(VariableStruct, NumberType)]) -> NumberType? { 39 | let array = makeConstants().map { ConstantStruct($0, value: $1) } 40 | return evaluate(constants: array, modulus: modulus) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/EquationKit/NamedVariables/Variable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Variable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol VariableProtocol: NamedVariable, Algebraic { 12 | init(_ name: String) 13 | } 14 | 15 | public struct VariableStruct: VariableProtocol { 16 | public typealias NumberType = Number 17 | public let name: String 18 | 19 | public init(_ name: String) { 20 | self.name = name 21 | } 22 | } 23 | 24 | // MARK: - Substitution 25 | public extension VariableProtocol { 26 | func substitute(constants: Set>, modulus: Modulus?) -> PolynomialType { 27 | guard let constant = constants.first(where: { $0 == self }) else { return PolynomialType(variable: self as! VariableStruct) } 28 | return PolynomialType(constant: constant.value) 29 | } 30 | } 31 | 32 | 33 | // MARK: - Solvable 34 | public extension VariableProtocol { 35 | 36 | var uniqueVariables: Set> { 37 | return Set([self as! VariableStruct]) 38 | } 39 | 40 | func findRoots(constants: Set>) -> Set> { 41 | guard let constant = constants.first(where: { $0.toVariable() == self }) else { return Set() } 42 | return Set([ConstantStruct(name: self.name, value: constant.value)]) 43 | } 44 | } 45 | 46 | // MARK: - Differentiatable 47 | public extension VariableProtocol { 48 | func differentiateWithRespectTo(_ variableToDifferentiate: VariableStruct) -> PolynomialType? { 49 | guard variableToDifferentiate == self else { return nil } 50 | // actually this is never used.... but makes us able to distinguish between 51 | // doing `exponentiations.append(exponentiation)` and doing 52 | // nothing in differentiation in TermProtocol 53 | return PolynomialType(constant: .one) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/EquationKitSupportBigInt/Operators/BigInt_Operators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BigInt_Operators.swift 3 | // EquationKitBigIntTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import EquationKit 11 | 12 | /// https://github.com/attaswift/BigInt 13 | import BigInt 14 | 15 | public typealias Variable = VariableStruct 16 | public typealias Constant = ConstantStruct 17 | public typealias Polynomial = PolynomialType 18 | 19 | public typealias Exponentiation = ExponentiationStruct 20 | public typealias Term = TermStruct 21 | 22 | public func +(lhs: Atom, rhs: Atom) -> PolynomialType { 23 | return Polynomial(lhs).adding(other: Polynomial(rhs)) 24 | } 25 | 26 | public func +(lhs: Atom, rhs: BigInt) -> PolynomialType { 27 | return Polynomial(lhs).adding(constant: rhs) 28 | } 29 | 30 | public func +(lhs: BigInt, rhs: Atom) -> PolynomialType { 31 | return Polynomial(rhs).adding(constant: lhs) 32 | } 33 | 34 | public func -(lhs: Atom, rhs: Atom) -> PolynomialType { 35 | return Polynomial(lhs).subtracting(other: Polynomial(rhs)) 36 | } 37 | 38 | public func -(lhs: Atom, rhs: BigInt) -> PolynomialType { 39 | return Polynomial(lhs).subtracting(constant: rhs) 40 | } 41 | 42 | public func -(lhs: BigInt, rhs: Atom) -> PolynomialType { 43 | return Polynomial(rhs).negated().adding(constant: lhs) 44 | } 45 | 46 | public func *(lhs: Atom, rhs: Atom) -> PolynomialType { 47 | return Polynomial(lhs).multipliedBy(other: Polynomial(rhs)) 48 | } 49 | 50 | public func *(lhs: Atom, rhs: BigInt) -> PolynomialType { 51 | return Polynomial(lhs).multipliedBy(constant: rhs) 52 | } 53 | 54 | public func *(lhs: BigInt, rhs: Atom) -> PolynomialType { 55 | return rhs * lhs 56 | } 57 | 58 | public func ^^(lhs: Atom, rhs: Int) -> PolynomialType { 59 | return Polynomial(lhs).raised(to: rhs) 60 | } 61 | 62 | public func ^^(lhs: BigInt, rhs: Int) -> BigInt { 63 | return lhs.power(rhs) 64 | } 65 | 66 | public func ^^(lhs: Int, rhs: Int) -> BigInt { 67 | return BigInt(lhs) ^^ rhs 68 | } 69 | 70 | public func -(lhs: BigInt, rhs: Int) -> BigInt { 71 | return lhs - BigInt(rhs) 72 | } 73 | -------------------------------------------------------------------------------- /Sources/EquationKit/Protocols/Solvable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Solvable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-26. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// ***************************************** 12 | /// NOT IMPLEMENTED YET 13 | /// ***************************************** 14 | 15 | public enum Solution: NumberTypeSpecifying { 16 | public typealias NumberType = Number 17 | 18 | case roots(Set>) 19 | case number(NumberType) 20 | } 21 | 22 | public protocol Solvable: Substitutionable { 23 | func solve(constants: Set>) -> Solution 24 | 25 | /// Might have a look at Sympys solver: https://github.com/sympy/sympy/blob/master/sympy/solvers/solvers.py#L450-L1349 26 | func findRoots(constants: Set>) -> Set> 27 | } 28 | 29 | public extension Solvable { 30 | 31 | func solve(constants: Set>) -> Solution { 32 | let variablesPassed = Set>(constants.map { $0.toVariable() }) 33 | 34 | if variablesPassed.isSuperset(of: uniqueVariables) { 35 | return .number(evaluate(constants: constants)!) 36 | } else { 37 | return .roots(findRoots(constants: constants)) 38 | } 39 | } 40 | } 41 | 42 | public extension Set where Element: ConstantProtocol { 43 | func toVariables() -> [VariableStruct: Element.NumberType] { 44 | let array: [(VariableStruct, Element.NumberType)] = self.map { ($0.toVariable(), $0.value) } 45 | let dictionary: [VariableStruct: Element.NumberType] = array.reduce(into: [:]) { $0[$1.0] = $1.1 } 46 | return dictionary 47 | } 48 | } 49 | public extension ExponentiationProtocol { 50 | 51 | func findRoots(constants: Set>) -> Set> { 52 | fatalError() 53 | } 54 | } 55 | // MARK: - Solvable 56 | public extension TermProtocol { 57 | 58 | func findRoots(constants: Set>) -> Set> { 59 | fatalError() 60 | } 61 | } 62 | 63 | 64 | // MARK: - Solvable 65 | public extension PolynomialProtocol { 66 | 67 | func findRoots(constants: Set>) -> Set> { 68 | fatalError() 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sources/EquationKit/SortingOfTerms/SortingWithinTerm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SortingWithinTerm.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-21. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum SortingWithinTerm: Sorting { 12 | public typealias TypeToSort = ExponentiationStruct 13 | case descendingExponent 14 | case variablesAlphabetically 15 | 16 | public static var defaultArray: [SortingWithinTerm] { 17 | return [.descendingExponent, .variablesAlphabetically] 18 | } 19 | } 20 | 21 | public extension SortingWithinTerm { 22 | 23 | 24 | var comparing: (TypeToSort, TypeToSort) -> (ComparisonResult) { 25 | switch self { 26 | case .descendingExponent: return { $0.exponent.compare(to: $1.exponent) } 27 | case .variablesAlphabetically: return { $0.variable.name.compare(to: $1.variable.name) } 28 | } 29 | } 30 | 31 | var targetComparisonResult: ComparisonResult { 32 | switch self { 33 | case .variablesAlphabetically: return .orderedAscending 34 | case .descendingExponent: return .orderedDescending 35 | } 36 | } 37 | } 38 | 39 | 40 | public extension Array where Element: ExponentiationProtocol { 41 | 42 | func sorted(by sorting: SortingWithinTerm) -> [Element] { 43 | return sorted(by: [sorting]) 44 | } 45 | 46 | func sorted(by sorting: [SortingWithinTerm] = SortingWithinTerm.defaultArray) -> [Element] { 47 | guard let first = sorting.first else { return self } 48 | 49 | let areInIncreasingOrderClosure: (Element, Element) -> Bool = { 50 | guard 51 | let lhs = $0 as? ExponentiationStruct, 52 | let rhs = $1 as? ExponentiationStruct 53 | else { 54 | fatalError("what to do") 55 | } 56 | 57 | return first.areInIncreasingOrder(tieBreakers: sorting.droppingFirstNilIfEmpty())(lhs, rhs) 58 | } 59 | 60 | return sorted(by: areInIncreasingOrderClosure) 61 | } 62 | 63 | func merged() -> [Element] { 64 | var count: [VariableStruct: Element.NumberType] = [:] 65 | for exponentiation in self { 66 | count[exponentiation.variable] += exponentiation.exponent 67 | } 68 | return count.compactMap { Element.init(variable: $0.key, exponent: $0.value) } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol PolynomialProtocol: 12 | Algebraic, 13 | AbsoluteConvertible, 14 | Negatable 15 | where 16 | TermType.NumberType == Self.NumberType 17 | { 18 | associatedtype TermType: TermProtocol 19 | var constant: NumberType { get } 20 | var terms: [TermType] { get } 21 | 22 | init(terms: [TermType], sorting: TermSorting, constant: NumberType) 23 | init(term: TermType, constant: NumberType) 24 | init(constant: NumberType) 25 | } 26 | 27 | // MARK: - Typealias 28 | public extension PolynomialProtocol { 29 | typealias ExponentiationType = TermType.ExponentiationType 30 | } 31 | 32 | // MARK: - Convenience Initializers 33 | public extension PolynomialProtocol { 34 | 35 | init(polynomial: Self) { 36 | self.init(terms: polynomial.terms, constant: polynomial.constant) 37 | } 38 | 39 | init(terms: [TermType], sorting: TermSorting = .default, constant: NumberType = .zero) { 40 | self.init(terms: terms, sorting: sorting, constant: constant) 41 | } 42 | 43 | init(term: TermType, constant: NumberType = .zero) { 44 | self.init(terms: [term], constant: constant) 45 | } 46 | 47 | init(constant: NumberType) { 48 | self.init(terms: [], constant: constant) 49 | } 50 | 51 | init(exponentiation: ExponentiationType, constant: NumberType = .zero) { 52 | self.init(term: TermType(exponentiation: exponentiation), constant: constant) 53 | } 54 | 55 | init(variable: VariableStruct, constant: NumberType = .zero) { 56 | self.init(exponentiation: ExponentiationType(variable), constant: constant) 57 | } 58 | } 59 | 60 | // MARK: - Public Extensions 61 | public extension PolynomialProtocol { 62 | var highestExponent: NumberType? { 63 | guard !terms.isEmpty else { return nil } 64 | return terms.sorting(betweenTerms: .descendingExponent)[0].highestExponent 65 | } 66 | 67 | var asNumber: NumberType? { 68 | guard terms.isEmpty else { return nil } 69 | return constant 70 | } 71 | var isNumber: Bool { 72 | return asNumber != nil 73 | } 74 | } 75 | 76 | // MARK: - ExpressibleByArrayLiteral 77 | extension PolynomialStruct: ExpressibleByArrayLiteral { 78 | public init(arrayLiteral terms: TermType...) { 79 | self.init(terms: terms) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/EquationKit/Term/TermProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermProtocol.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol TermProtocol: 12 | Algebraic, 13 | Negatable, 14 | AbsoluteConvertible, 15 | Comparable, 16 | CustomDebugStringConvertible 17 | where 18 | ExponentiationType.NumberType == Self.NumberType 19 | { 20 | 21 | associatedtype ExponentiationType: ExponentiationProtocol 22 | 23 | var coefficient: NumberType { get } 24 | var exponentiations: [ExponentiationType] { get } 25 | 26 | init?(exponentiations: [ExponentiationType], sorting: [SortingWithinTerm], coefficient: NumberType) 27 | 28 | } 29 | 30 | // MARK: - Convenience Initializers 31 | public extension TermProtocol { 32 | init?(exponentiations: [ExponentiationType], sorting: [SortingWithinTerm] = SortingWithinTerm.defaultArray, coefficient: NumberType = .one) { 33 | self.init(exponentiations: exponentiations, sorting: sorting, coefficient: coefficient) 34 | } 35 | 36 | init?(exponentiation: ExponentiationType, coefficient: NumberType) { 37 | self.init(exponentiations: [exponentiation], coefficient: coefficient) 38 | } 39 | 40 | init?(_ exponentiations: ExponentiationType..., coefficient: NumberType) { 41 | self.init(exponentiations: exponentiations, coefficient: coefficient) 42 | } 43 | 44 | init(exponentiation: ExponentiationType) { 45 | self.init(exponentiations: [exponentiation], coefficient: .one)! 46 | } 47 | 48 | init(_ exponentiations: ExponentiationType...) { 49 | self.init(exponentiations: exponentiations, coefficient: .one)! 50 | } 51 | 52 | init(_ variable: VariableStruct) { 53 | self.init(exponentiation: ExponentiationType(variable)) 54 | } 55 | 56 | init(_ variables: [VariableStruct]) { 57 | self.init(exponentiations: variables.map { ExponentiationType($0) })! 58 | } 59 | 60 | init(_ variables: VariableStruct...) { 61 | self.init(variables) 62 | } 63 | 64 | } 65 | 66 | // MARK: - Public Extensions 67 | public extension TermProtocol { 68 | var isNegative: Bool { 69 | return coefficient.isNegative 70 | } 71 | 72 | func contains(variable: VariableStruct) -> Bool { 73 | return exponentiations.map { $0.variable }.contains(variable) 74 | } 75 | 76 | var highestExponent: NumberType { 77 | return exponentiations.sorted(by: .descendingExponent)[0].exponent 78 | } 79 | } 80 | 81 | // MARK: - Internal Extensions 82 | internal extension TermProtocol { 83 | 84 | var signString: String { 85 | return isNegative ? "-" : "+" 86 | } 87 | 88 | var variableNames: String { 89 | return exponentiations.map { $0.variable.name }.joined() 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Sources/EquationKit/Polynomial/PolynomialProtocol+Concatenation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialProtocol+Concatenation.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-08-24. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: - Appending 12 | public extension PolynomialProtocol { 13 | 14 | func adding(other: Self) -> Self { 15 | return Self(terms: terms + other.terms, constant: constant + other.constant) 16 | } 17 | 18 | func adding(constant: NumberType) -> Self { 19 | return Self.init(terms: terms, constant: self.constant + constant) 20 | } 21 | 22 | } 23 | 24 | // MARK: - Subtracting 25 | public extension PolynomialProtocol { 26 | 27 | func subtracting(other: Self) -> Self { 28 | return self.adding(other: other.negated()) 29 | } 30 | 31 | func subtracting(constant: NumberType) -> Self { 32 | return Self(terms: terms, constant: self.constant - constant) 33 | } 34 | 35 | 36 | } 37 | 38 | // MARK: - Multiplying 39 | public extension PolynomialProtocol { 40 | func multipliedBy(other: Self) -> Self { 41 | var multipliedTerm = [TermType]() 42 | for lhsTerm in terms { 43 | for rhsTerm in other.terms { 44 | multipliedTerm.append(lhsTerm.multipliedBy(other: rhsTerm)) 45 | } 46 | if other.constant != 0 { 47 | guard let multiplied = lhsTerm.multiplyingCoefficientBy(constant: other.constant) else { continue } 48 | multipliedTerm.append(multiplied) 49 | } 50 | } 51 | if constant != 0 { 52 | for rhsTerm in other.terms { 53 | guard let multiplied = rhsTerm.multiplyingCoefficientBy(constant: constant) else { continue } 54 | multipliedTerm.append(multiplied) 55 | } 56 | } 57 | 58 | return Self(terms: multipliedTerm, constant: constant * other.constant) 59 | } 60 | 61 | func multipliedBy(constant: NumberType) -> Self { 62 | guard let firstTerm = terms.first else { return Self(terms: [], constant: constant * constant) } 63 | guard let termMultiplied = TermType(exponentiations: firstTerm.exponentiations, coefficient: firstTerm.coefficient * constant) else { 64 | return Self(constant: .zero) 65 | } 66 | guard terms.count > 1 else { return Self(termMultiplied) } 67 | let rest = terms.dropFirst() 68 | return Self(terms: [termMultiplied] + rest, constant: constant) 69 | } 70 | 71 | 72 | } 73 | 74 | // MARK: - Exponentiating 75 | public extension PolynomialProtocol { 76 | func raised(to exponent: Int) -> Self { 77 | 78 | var polynomialExponentiated = Self(constant: 1) 79 | 80 | for _ in 0..: Sorting { 12 | 13 | public typealias TypeToSort = TermStruct> 14 | 15 | case descendingExponent 16 | case coefficient // positive higher than negative naturally 17 | case termsWithMostVariables 18 | case termsAlphabetically 19 | } 20 | 21 | public extension SortingBetweenTerms { 22 | 23 | static var defaultArray: [SortingBetweenTerms] { 24 | return [.descendingExponent, .termsWithMostVariables, .termsAlphabetically, .coefficient] 25 | } 26 | } 27 | 28 | public extension SortingBetweenTerms { 29 | 30 | var comparing: (TypeToSort, TypeToSort) -> (ComparisonResult) { 31 | switch self { 32 | case .descendingExponent: return { $0.highestExponent.compare(to: $1.highestExponent) } 33 | case .coefficient: return { $0.coefficient.compare(to: $1.coefficient) } 34 | case .termsWithMostVariables: return { $0.exponentiations.count.compare(to: $1.exponentiations.count) } 35 | case .termsAlphabetically: return { $0.variableNames.compare(to: $1.variableNames) } 36 | } 37 | } 38 | 39 | var targetComparisonResult: ComparisonResult { 40 | switch self { 41 | case .descendingExponent: return .orderedDescending 42 | case .coefficient: return .orderedDescending 43 | case .termsWithMostVariables: return .orderedDescending 44 | case .termsAlphabetically: return .orderedAscending 45 | } 46 | } 47 | } 48 | 49 | extension SortingBetweenTerms: CustomStringConvertible {} 50 | public extension SortingBetweenTerms { 51 | var description: String { 52 | switch self { 53 | case .descendingExponent: return "descendingExponent" 54 | case .coefficient: return "coefficient" 55 | case .termsWithMostVariables: return "termsWithMostVariables" 56 | case .termsAlphabetically: return "termsAlphabetically" 57 | } 58 | } 59 | } 60 | 61 | public extension Array where Element: TermProtocol { 62 | 63 | func merged() -> [Element] { 64 | var count: [[Element.ExponentiationType]: Element.NumberType] = [:] 65 | for term in self { 66 | count[term.exponentiations] += term.coefficient 67 | } 68 | return count.compactMap { Element.init(exponentiations: $0.key, coefficient: $0.value) } 69 | } 70 | 71 | func sorting(betweenTerms: SortingBetweenTerms) -> [Element] { 72 | return sorting(betweenTerms: [betweenTerms]) 73 | } 74 | 75 | func sorting(betweenTerms: [SortingBetweenTerms] = SortingBetweenTerms.defaultArray) -> [Element] { 76 | return sorted(by: TermSorting(betweenTerms: betweenTerms)) 77 | } 78 | 79 | func sorted(by sorting: TermSorting = TermSorting()) -> [Element] { 80 | guard let first = sorting.betweenTerms.first else { return self } 81 | 82 | let areInIncreasingOrderClosure: (Element, Element) -> Bool = { 83 | guard 84 | let lhs = $0 as? SortingBetweenTerms.TypeToSort, 85 | let rhs = $1 as? SortingBetweenTerms.TypeToSort 86 | else { 87 | fatalError("what to do") 88 | } 89 | 90 | return first.areInIncreasingOrder(tieBreakers: sorting.betweenTerms.droppingFirstNilIfEmpty())(lhs, rhs) 91 | } 92 | 93 | return sorted(by: areInIncreasingOrderClosure) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/ExploringRandomStuffTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExploringRandomStuffTests.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-17. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import EquationKit 12 | @testable import EquationKitDoubleSupport 13 | 14 | class ExploringRandomStuffTests: DoubleTestsBase { 15 | 16 | static var allTests = [ 17 | ("testDifferenceOfConsecutiveSquares", testDifferenceOfConsecutiveSquares), 18 | ("testDifferenceOfConsecutiveCubes", testDifferenceOfConsecutiveCubes), 19 | ("testDifferenceOfConsecutivePowersOfFour", testDifferenceOfConsecutivePowersOfFour), 20 | ("testDifferenceOfConsecutivePowersOfFour", testDifferenceOfConsecutivePowersOfFour), 21 | ] 22 | 23 | func testDifferenceOfConsecutiveSquares() { 24 | var lastSquareDiff: Double? = nil 25 | let equation = x² - y² 26 | for i in 0..<1000 { 27 | let squareDiff = equation.evaluate() {[ x <- Double(i+1), y <- Double(i) ]}! 28 | defer { lastSquareDiff = squareDiff } 29 | guard let lastSquareDiff = lastSquareDiff else { continue } 30 | XCTAssertEqual(squareDiff - lastSquareDiff, 2) 31 | } 32 | } 33 | 34 | func testDifferenceOfConsecutiveCubes() { 35 | var list = [Double]() 36 | let hexagonalRightmostDigitsPattern: [Double] = [1, 7, 9, 7, 1] 37 | let equation = x³ - y³ 38 | for i in 0..<1000 { 39 | let difference = equation.evaluate() {[ x <- Double(i+1), y <- Double(i) ]}! 40 | let modRes = mod(difference, modulus: 10) 41 | list.append(modRes) 42 | if list.count == hexagonalRightmostDigitsPattern.count { 43 | XCTAssertEqual(list, hexagonalRightmostDigitsPattern) 44 | list = [] 45 | } 46 | } 47 | } 48 | 49 | func testDifferenceOfConsecutivePowersOfFour() { 50 | continueAfterFailure = false 51 | var lastDiff: Double? = nil 52 | 53 | var diffsMod10 = [Double]() 54 | // Let `mod₁₀ { X }` denote `mod(x, 10)`: 55 | 56 | // `x⁴ⁿ - x⁴ⁿ⁻⁴` 57 | // Apparently `mod₁₀ { n⁴ - (n-1)⁴ }` have a cyclic property of size 5, repeating the following pattern: 58 | let diffsMod10Pattern: [Double] = [5, 5, 5, 9, 1] 59 | 60 | var diffDiffsMod10 = [Double]() 61 | 62 | 63 | // `mod₁₀ { (n⁴ - (n-1)⁴) - ((n-1)⁴ - (n-2)⁴) }` <=> `mod₁₀ { n⁴ - 2(n-1)⁴ + (n-2)⁴ }` have a cyclic property of size 5, repeating the following pattern: 64 | let diffDiffsMod10Pattern: [Double] = [4, 0, 0, 4, 2] 65 | 66 | let eq1 = x⁴ - y⁴ 67 | let eq2 = 8*(x³+x) // (x-1)^^4 - (x-1)^^4/ 68 | 69 | for i in 5..<1000 { 70 | let cx = Constant(x, value: i+1) 71 | let cy = Constant(y, value: i) 72 | let constants = [cx, cy] 73 | 74 | let difference = eq1.evaluate(constants: constants)! 75 | 76 | defer { lastDiff = difference } 77 | guard let lastDiff = lastDiff else { continue } 78 | let diffDiff = difference - lastDiff 79 | 80 | let diffMod10 = mod(difference, modulus: 10) 81 | diffsMod10.append(diffMod10) 82 | let diffDiffMod10 = mod(diffDiff, modulus: 10) 83 | diffDiffsMod10.append(diffDiffMod10) 84 | 85 | if diffsMod10.count == diffsMod10Pattern.count { 86 | XCTAssertEqual(diffsMod10, diffsMod10Pattern) 87 | diffsMod10 = [] 88 | } 89 | 90 | if diffDiffsMod10.count == diffDiffsMod10Pattern.count { 91 | XCTAssertEqual(diffDiffsMod10, diffDiffsMod10Pattern) 92 | diffDiffsMod10 = [] 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/PolynomialMultipliedByPolynomialTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialMultipliedByPolynomialTests.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | 12 | @testable import EquationKit 13 | @testable import EquationKitDoubleSupport 14 | 15 | class PolynomialMultipliedByPolynomialTests: DoubleTestsBase { 16 | 17 | static var allTests = [ 18 | ("testGrade1Short", testGrade1Short), 19 | ("testGrade1Long", testGrade1Long), 20 | ("testConcatenation", testConcatenation), 21 | ("testFourRoots", testFourRoots), 22 | ] 23 | 24 | override func setUp() { 25 | super.setUp() 26 | continueAfterFailure = false 27 | } 28 | 29 | func testGrade1Short() { 30 | let eq = ((4*x) + 9) * ((8*x) + (5*y)) 31 | XCTAssertEqual(eq, 32*x² + 20*x*y + 72*x + 45*y) 32 | } 33 | 34 | func testGrade1Long() { 35 | let eq = (3*x + 5*y - 17) * (7*x - 9*y + 23) 36 | XCTAssertEqual(eq, 21*x² + 8*x*y - 50*x - 45*y² + 268*y - 391) 37 | XCTAssertEqual( 38 | 0, 39 | eq.evaluate() {[ 40 | x <- 4, 41 | y <- 1 42 | ]}) 43 | 44 | let y' = eq.differentiateWithRespectTo(x)! 45 | XCTAssertEqual(42*x + 8*y - 50, y') 46 | XCTAssertEqual( 47 | 0, 48 | y'.evaluate() {[ 49 | x <- 1, 50 | y <- 1 51 | ]} 52 | ) 53 | 54 | let x' = eq.differentiateWithRespectTo(y)! 55 | XCTAssertEqual(8*x - 90*y + 268, x') 56 | XCTAssertEqual( 57 | 0, 58 | x'.evaluate() {[ 59 | x <- 11.5, 60 | y <- 4 61 | ]} 62 | ) 63 | } 64 | 65 | 66 | func testConcatenation() { 67 | let term = Term(x) 68 | let polynomial = Polynomial(variable: y) 69 | let eq1 = term + polynomial 70 | XCTAssertEqual(eq1, x+y) 71 | let exponentiation = Exponentiation(x, exponent: 2) 72 | let eq2 = exponentiation + polynomial 73 | XCTAssertEqual(eq2, x²+y) 74 | 75 | let eq3 = term - polynomial 76 | XCTAssertEqual(eq3, x-y) 77 | let eq4 = exponentiation - polynomial 78 | XCTAssertEqual(eq4, x²-y) 79 | } 80 | 81 | func testFourRoots() { 82 | 83 | let roots2Factorized = (x-2)*(x+3) 84 | let roots2Expanded = x² + x - 6 85 | 86 | XCTAssertEqual(roots2Factorized, roots2Expanded) 87 | 88 | let roots3FromRoots2FactorizedLHS = roots2Factorized * (x+7) 89 | let roots3FromRoots2FactorizedRHS = (x+7) * roots2Factorized 90 | 91 | let roots3ExpanedManual = x³ + 8*x² + x - 42 92 | XCTAssertEqual(roots3FromRoots2FactorizedLHS, roots3ExpanedManual) 93 | XCTAssertEqual(roots3FromRoots2FactorizedRHS, roots3ExpanedManual) 94 | 95 | let roots3FactorizedManual = (x-2)*(x+3)*(x+7) 96 | XCTAssertEqual(roots3ExpanedManual, roots3FactorizedManual) 97 | 98 | let roots4FromRoots3FactorizedLHS = roots3FromRoots2FactorizedLHS * (x-11) 99 | let roots4FromRoots3FactorizedRHS = (x-11) * roots3FromRoots2FactorizedLHS 100 | 101 | let eq = (x-2)*(x+3)*(x+7)*(x-11) 102 | XCTAssertEqual(eq, roots4FromRoots3FactorizedLHS) 103 | XCTAssertEqual(eq, roots4FromRoots3FactorizedRHS) 104 | 105 | XCTAssertEqual(eq, x⁴ - 3*x³ - 87*x² - 53*x + 462) 106 | let solutions = [ 107 | eq.evaluate() { x <- 2 }, 108 | eq.evaluate() { x <- -3 }, 109 | eq.evaluate() { x <- -7 }, 110 | eq.evaluate() { x <- 11 } 111 | ] 112 | solutions.forEach { 113 | XCTAssertEqual($0, 0) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/PolynomialsExponentiatedTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PolynomialsExponentiatedTests.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-17. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | import Foundation 12 | import XCTest 13 | @testable import EquationKit 14 | @testable import EquationKitDoubleSupport 15 | 16 | class PolynomialsExponentiatedTests: DoubleTestsBase { 17 | 18 | static var allTests = [ 19 | ("test﹙𝑥+𝟙﹚²", test﹙𝑥+𝟙﹚²), 20 | ("test𝑥²ToPowerOf3", test𝑥²ToPowerOf3), 21 | ("test﹙𝑥+𝟙﹚³", test﹙𝑥+𝟙﹚³), 22 | ("testMultiplyWithZero", testMultiplyWithZero), 23 | ("test﹙𝑥-𝟙﹚²", test﹙𝑥-𝟙﹚²), 24 | ("test﹙𝑥-𝟙﹚³", test﹙𝑥-𝟙﹚³), 25 | ("test𝑥·𝑥²+𝑥²·𝑥-𝟚𝑥³", test𝑥·𝑥²+𝑥²·𝑥-𝟚𝑥³), 26 | ("test﹙𝑥-𝟙﹚⁹", test﹙𝑥-𝟙﹚⁹), 27 | ("testAbsolute", testAbsolute), 28 | ("testHighestExponent", testHighestExponent), 29 | ("testNegationOfTermsInsidePolynmoial", testNegationOfTermsInsidePolynmoial), 30 | ] 31 | 32 | 33 | func test﹙𝑥+𝟙﹚²() { 34 | let eq = (x + 1)^^2 35 | XCTAssertEqual(eq, (x + 1) * (x + 1)) 36 | XCTAssertEqual(eq, x² + 2*x + 1) 37 | XCTAssertEqual(eq, (Double(1) + x)^^2) 38 | } 39 | 40 | func test𝑥²ToPowerOf3() { 41 | let eq = x²^^3 42 | XCTAssertEqual(eq, x^^6) 43 | XCTAssertEqual(eq, Polynomial(x⁶)) 44 | } 45 | 46 | func test﹙𝑥+𝟙﹚³() { 47 | let eq = (x + 1)^^3 48 | XCTAssertEqual(eq, (x + 1) * (x + 1) * (x + 1)) 49 | XCTAssertEqual(eq, x³ + 3*x² + 3*x + 1) 50 | } 51 | 52 | func testMultiplyWithZero() { 53 | let a = 0.0 54 | let b = 7.0 55 | let eq = y² - x³ + a*x + b 56 | XCTAssertEqual(eq, y² - x³ + 7) 57 | } 58 | 59 | func test﹙𝑥-𝟙﹚²() { 60 | let eq = (x - 1)^^2 61 | XCTAssertEqual(eq, (x - 1) * (x - 1)) 62 | XCTAssertEqual(eq, x² - 2*x + 1) 63 | } 64 | 65 | func test﹙𝑥-𝟙﹚³() { 66 | let eq = (x - 1)^^3 67 | XCTAssertEqual(eq, (x - 1) * (x - 1) * (x - 1)) 68 | XCTAssertEqual(eq, x³ - 3*x² + 3*x - 1) 69 | } 70 | 71 | func test𝑥·𝑥²+𝑥²·𝑥-𝟚𝑥³() { 72 | let eq = x*x² + x²*x - 2*x³ 73 | XCTAssertEqual(eq.description, "") 74 | XCTAssertEqual(eq, 0) 75 | } 76 | 77 | func test﹙𝑥-𝟙﹚⁹() { 78 | let eq = (x - 1)^^9 79 | XCTAssertEqual(eq.asString(sorting: .descendingExponent), "x⁹ - 9x⁸ + 36x⁷ - 84x⁶ + 126x⁵ - 126x⁴ + 84x³ - 36x² + 9x - 1") 80 | XCTAssertEqual(eq, x⁹ - 9*x⁸ + 36*x⁷ - 84*x⁶ + 126*x⁵ - 126*x⁴ + 84*x³ - 36*x² + 9*x - 1) 81 | XCTAssertEqual(eq.evaluate() { x <- 1 }!, 0) 82 | XCTAssertEqual(eq.evaluate() { x <- 2 }!, 1) 83 | XCTAssertEqual(eq.evaluate() { x <- 3 }!, 512) 84 | XCTAssertEqual(eq.evaluate() { x <- 4 }!, 19683) 85 | } 86 | 87 | func testAbsolute() { 88 | let eq = y³ - x² - 2*x + y - 5 89 | let expectedAbsolute = y³ + x² + 2*x + y + 5 90 | XCTAssertEqual(eq.absolute(), expectedAbsolute) 91 | XCTAssertEqual(expectedAbsolute.evaluate() {[ x <- 2, y <- 3 ]}, 43) 92 | } 93 | 94 | func testHighestExponent() { 95 | let eq = x³ + x⁸ - y⁵ + 1337 96 | XCTAssertEqual(eq.highestExponent, 8) 97 | XCTAssertEqual((x + 1).highestExponent, 1) 98 | XCTAssertNil((x - x).highestExponent) 99 | } 100 | 101 | func testNegationOfTermsInsidePolynmoial() { 102 | let eq = 5*y² - (x³ + 3*x² + x) 103 | let expected = 5*y² - x³ - 3*x² - x 104 | func _test(_ poly: Polynomial) { 105 | XCTAssertEqual(poly.evaluate() {[ x <- 2, y <- 3 ]}, 23) 106 | } 107 | _test(eq) 108 | _test(expected) 109 | XCTAssertEqual(eq, expected) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Sources/EquationKit/Protocols/Substitutionable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Substitutionable.swift 3 | // EquationKit 4 | // 5 | // Created by Alexander Cyon on 2018-09-02. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Substitutionable: NumberTypeSpecifying { 12 | 13 | var uniqueVariables: Set> { get } 14 | 15 | func substitute(constants: Set>, modulus: Modulus?) -> PolynomialType 16 | } 17 | 18 | public extension Substitutionable { 19 | func substitute(constants: Set>, modulus: Modulus? = nil) -> PolynomialType { 20 | return substitute(constants: constants, modulus: modulus) 21 | } 22 | 23 | func substitute(constants: [ConstantStruct], modulus: Modulus? = nil) -> PolynomialType { 24 | guard !constants.containsDuplicates() else { fatalError("cannot contain duplicates") } 25 | return substitute(constants: Set(constants), modulus: modulus) 26 | } 27 | 28 | func substitute(constants: ConstantStruct..., modulus: Modulus? = nil) -> PolynomialType { 29 | return substitute(constants: constants, modulus: modulus) 30 | } 31 | 32 | func substitute(modulus: Modulus? = nil, makeConstants: () -> ([ConstantStruct])) -> PolynomialType { 33 | return substitute(constants: makeConstants(), modulus: modulus) 34 | } 35 | 36 | func substitute(modulus: Modulus? = nil, makeConstants: () -> ConstantStruct) -> PolynomialType { 37 | return substitute(constants: [makeConstants()], modulus: modulus) 38 | } 39 | 40 | // Constants passed as Dictionary 41 | func substitute(constants: [VariableStruct: NumberType], modulus: Modulus? = nil) -> PolynomialType { 42 | return substitute(constants: Set(constants.map { ConstantStruct($0, value: $1) }), modulus: modulus) 43 | } 44 | 45 | // Constants passed as Array<(Variable, Number)> 46 | func substitute(modulus: Modulus? = nil, makeConstants: () -> [(VariableStruct, NumberType)]) -> PolynomialType { 47 | let array = makeConstants().map { ConstantStruct($0, value: $1) } 48 | return substitute(constants: array, modulus: modulus) 49 | } 50 | 51 | } 52 | 53 | internal func parse( 54 | substitutionable: S, 55 | constants: Set>, 56 | modulus: Modulus? = nil, 57 | handleNumber: ((N) -> N) = { $0 }, 58 | handleAlgebraic: ((PolynomialType) -> PolynomialType) 59 | ) -> PolynomialType where S: Substitutionable, N == S.NumberType { 60 | 61 | let polynomial = substitutionable.substitute(constants: constants, modulus: modulus) 62 | guard let constant = polynomial.asNumber else { return handleAlgebraic(polynomial) } 63 | 64 | return PolynomialType(constant: handleNumber(constant).modIfNeeded(modulus)) 65 | } 66 | 67 | internal func parseMany( 68 | substitutionables: [S], 69 | constants: Set>, 70 | modulus: Modulus? = nil, 71 | manyHandleAllNumbers: (([N]) -> N), 72 | manyHandleMixedReduce: (initialResult: PolynomialType, combine: ((PolynomialType, PolynomialType) -> PolynomialType)) 73 | ) -> PolynomialType where S: Substitutionable, N == S.NumberType { 74 | 75 | let polynomials = substitutionables.map { $0.substitute(constants: constants, modulus: modulus) } 76 | 77 | 78 | if polynomials.allSatisfy({ $0.isNumber }) { 79 | let value = manyHandleAllNumbers(polynomials.compactMap { $0.asNumber }) 80 | return PolynomialType(constant: value.modIfNeeded(modulus)) 81 | } else { 82 | let (initialResult, combine) = manyHandleMixedReduce 83 | return polynomials.reduce(initialResult, combine) 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/Differentiation/SimpleDifferentiationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SimpleDifferentiationTests.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-15. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import EquationKit 12 | @testable import EquationKitDoubleSupport 13 | 14 | class SimpleDifferentiationTests: DoubleTestsBase { 15 | 16 | static var allTests = [ 17 | ("test𝑦³𝑥²", test𝑦³𝑥²), 18 | ("test𝟛𝑥²-𝟛𝑦³𝑥²", test𝟛𝑥²-𝟛𝑦), 19 | ("test𝟛𝑦²-𝟛𝑥", test𝟛𝑦²-𝟛𝑥), 20 | ("test𝟙𝟚𝑥²+𝑦³-𝟙𝟚𝑥𝑦", test𝟙𝟚𝑥²+𝑦³-𝟙𝟚𝑥𝑦), 21 | ("test𝟛𝑥²+𝟚𝑥𝑦-𝑦²", test𝟛𝑥²+𝟚𝑥𝑦-𝑦²), 22 | ("test𝟛𝑥⁵𝑦²+𝟙𝟛𝑥𝑦+𝟟𝑥+𝟡𝑦", test𝟛𝑥⁵𝑦²+𝟙𝟛𝑥𝑦+𝟟𝑥+𝟡𝑦), 23 | ("test𝟙𝟘𝑥^𝟙𝟙-𝟜𝑥^𝟙𝟙-𝟝𝑥^𝟙𝟙+𝟞·𝟠𝑥", test𝟙𝟘𝑥^𝟙𝟙-𝟜𝑥^𝟙𝟙-𝟝𝑥^𝟙𝟙+𝟞·𝟠𝑥), 24 | ("testDoubleDifferentationOf𝟝𝑦⁴𝑥³", testDoubleDifferentationOf𝟝𝑦⁴𝑥³), 25 | 26 | ] 27 | 28 | /// y³x² 29 | func test𝑦³𝑥²() { 30 | let eq = (y³*x²) 31 | XCTAssertEqual((2*y³*x), eq.differentiateWithRespectTo(x)!) 32 | XCTAssertEqual((3*y²*x²), eq.differentiateWithRespectTo(y)!) 33 | } 34 | 35 | 36 | /// 3x² - 3y 37 | func test𝟛𝑥²-𝟛𝑦() { 38 | let eq = 3*x² - 3*y 39 | XCTAssertEqual((6*x), eq.differentiateWithRespectTo(x)!) 40 | XCTAssertEqual(-3, eq.differentiateWithRespectTo(y)!) 41 | } 42 | 43 | /// 3y² - 3x 44 | func test𝟛𝑦²-𝟛𝑥() { 45 | let eq = 3*y² - 3*x 46 | XCTAssertEqual(-3, eq.differentiateWithRespectTo(x)!) 47 | XCTAssertEqual((6*y), eq.differentiateWithRespectTo(y)!) 48 | } 49 | 50 | /// 12x² + y³ - 12xy 51 | func test𝟙𝟚𝑥²+𝑦³-𝟙𝟚𝑥𝑦() { 52 | let eq = 12*x² + y³ - 12*x*y 53 | XCTAssertEqual(24*x - 12*y, eq.differentiateWithRespectTo(x)!) 54 | XCTAssertEqual(3*y² - 12*x, eq.differentiateWithRespectTo(y)!) 55 | } 56 | 57 | /// 3x² + 2xy - y² 58 | func test𝟛𝑥²+𝟚𝑥𝑦-𝑦²() { 59 | let equationVerbose = 3*x^^2 + 2*x*y - y^^2 60 | let equation = 3*x² + 2*x*y - y² 61 | XCTAssertEqual(equationVerbose.description, equation.description) 62 | XCTAssertEqual(equationVerbose, equation) 63 | 64 | XCTAssertEqual(6*x + 2*y, equation.differentiateWithRespectTo(x)!) 65 | XCTAssertEqual(2*x - 2*y, equation.differentiateWithRespectTo(y)!) 66 | } 67 | 68 | /// 3x⁵y² + 13xy + 7x + 9y 69 | func test𝟛𝑥⁵𝑦²+𝟙𝟛𝑥𝑦+𝟟𝑥+𝟡𝑦() { 70 | let equation = 3*x⁵*y² + 13*x*y + 7*x + 9*y 71 | XCTAssertEqual(15*x⁴*y² + 13*y + 7, equation.differentiateWithRespectTo(x)!) 72 | XCTAssertEqual(6*x⁵*y + 13*x + 9, equation.differentiateWithRespectTo(y)!) 73 | } 74 | 75 | func test𝟙𝟘𝑥^𝟙𝟙-𝟜𝑥^𝟙𝟙-𝟝𝑥^𝟙𝟙+𝟞·𝟠𝑥() { 76 | let eq = 10*x^^11 - 4*x^^11 - 5*x^^11 + 6*8*x 77 | XCTAssertEqual(eq, x^^11 + 48*x) 78 | let y' = eq.differentiateWithRespectTo(x)! 79 | XCTAssertEqual(11*x^^10 + 48, y') 80 | XCTAssertEqual(eq.evaluate() { x <- 1 }, 49) 81 | XCTAssertEqual(eq.evaluate() { x <- 2 }, 2048+96) 82 | XCTAssertEqual(y'.evaluate() { x <- 1 }, 59) 83 | XCTAssertEqual(y'.evaluate() { x <- 2 }, 11*1024+48) 84 | } 85 | 86 | func testDoubleDifferentationOf𝟝𝑦⁴𝑥³() { 87 | let eq = y⁵*x³ 88 | 89 | let y' = eq.differentiateWithRespectTo(x)! 90 | XCTAssertEqual((3*y⁵*x²), y') 91 | 92 | let yy' = y'.differentiateWithRespectTo(x)! 93 | XCTAssertEqual((6 * y⁵ * x), yy') 94 | 95 | let x' = eq.differentiateWithRespectTo(y)! 96 | XCTAssertEqual((5*y⁴*x³), x') 97 | 98 | let xx' = x'.differentiateWithRespectTo(y)! 99 | XCTAssertEqual((20 * y³ * x³), xx') 100 | 101 | 102 | let xy' = x'.differentiateWithRespectTo(x)! 103 | XCTAssertEqual(15*y⁴*x², xy') 104 | 105 | let yx' = y'.differentiateWithRespectTo(y)! 106 | XCTAssertEqual(15*y⁴*x², xy') 107 | 108 | XCTAssertEqual(xy', yx') 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/Concatenation/Addition/ConcatenationByAdditionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConcatenationByAdditionTests.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-17. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import EquationKit 12 | @testable import EquationKitDoubleSupport 13 | 14 | class ConcatenationByAdditionTests: DoubleTestsBase { 15 | 16 | static var allTests = [ 17 | ("testNumberAddVariableAndReversed", testNumberAddVariableAndReversed), 18 | ("testNumberAddExponentiationAndReversed", testNumberAddExponentiationAndReversed), 19 | ("testNumberAddTermAndReversed", testNumberAddTermAndReversed), 20 | ("testNumberAddPolynomialAndReversed", testNumberAddPolynomialAndReversed), 21 | ("testVariableAddVariable", testVariableAddVariable), 22 | ("testVariableAddExponentiationAndReversed", testVariableAddExponentiationAndReversed), 23 | ("testVariableAddTermAndReversed", testVariableAddTermAndReversed), 24 | ("testVariableAddPolynomialAndReversed", testVariableAddPolynomialAndReversed), 25 | ("testExponentiationAddExponentiation", testExponentiationAddExponentiation), 26 | ("testExponentiationAddTermAndReversed", testExponentiationAddTermAndReversed), 27 | ("testExponentiationAddPolynomialAndReversed", testExponentiationAddPolynomialAndReversed), 28 | ("testTermAddTerm", testTermAddTerm), 29 | ("testTermAddPolynomialAndReversed", testTermAddPolynomialAndReversed), 30 | ("testPolynomialAddPolynomial", testPolynomialAddPolynomial), 31 | ("test3xAddY", test3xAddY), 32 | ] 33 | 34 | // MARK: - Number 35 | func testNumberAddVariableAndReversed() { 36 | XCTAssertEqual(x + 2, 2 + x) 37 | XCTAssertEqual(x + 2.5, 2.5 + x) 38 | 39 | XCTAssertNotEqual(x + 2, y + 2) 40 | 41 | XCTAssertNotEqual(x + 0.5, 0.4 + x) 42 | XCTAssertNotEqual(x + 1, 2 + x) 43 | } 44 | 45 | func testNumberAddExponentiationAndReversed() { 46 | XCTAssertEqual(Exponentiation(x, exponent: 2), x²) 47 | 48 | XCTAssertEqual(1 + x², x² + 1) // Commutative 49 | XCTAssertEqual(1 + y², y² + 1) // Commutative 50 | XCTAssertEqual(1.5 + x², x² + 1.5) // Commutative 51 | 52 | XCTAssertNotEqual(1 + x², 1 + y²) 53 | } 54 | 55 | func testNumberAddTermAndReversed() { 56 | let xy = x*y 57 | let yx = y*x 58 | XCTAssertEqual(yx, xy) // Multiplication is commutative 59 | 60 | XCTAssertEqual(xy + 2, 2 + xy) 61 | XCTAssertEqual(xy + 2.5, 2.5 + xy) 62 | } 63 | 64 | func testNumberAddPolynomialAndReversed() { 65 | let eq = x + 2 66 | let eq2 = x + 5 67 | 68 | XCTAssertEqual(eq + 3, eq2) 69 | XCTAssertEqual(3 + eq, eq2) 70 | XCTAssertEqual(1 + eq + 2, eq2) 71 | XCTAssertEqual(1.5 + eq + 1.5, eq2) 72 | } 73 | 74 | // MARK: - Variable 75 | func testVariableAddVariable() { 76 | XCTAssertEqual(x + x, x + x) // trivial 77 | XCTAssertEqual(x + y, x + y) // trivial 78 | XCTAssertEqual(x + y, y + x) // commutative 79 | } 80 | 81 | func testVariableAddExponentiationAndReversed() { 82 | XCTAssertEqual(x² + x, x + x²) // Commutative 83 | XCTAssertNotEqual(x² + x, x² + y) 84 | XCTAssertNotEqual(x² + x, x³ + x) 85 | } 86 | 87 | func testVariableAddTermAndReversed() { 88 | let xy = x*y 89 | XCTAssertEqual(x + xy, xy + x) // Commutative 90 | XCTAssertNotEqual(x + xy, y + xy) 91 | } 92 | 93 | func testVariableAddPolynomialAndReversed() { 94 | let eq = x + 2 95 | 96 | XCTAssertEqual(x + eq, eq + x) // Commutative 97 | XCTAssertNotEqual(x + eq, y + eq) 98 | } 99 | 100 | // MARK: - Exponentiation 101 | func testExponentiationAddExponentiation() { 102 | XCTAssertEqual(x² + y², y² + x²) // Commutative 103 | XCTAssertEqual(x³ + y², y² + x³) // Commutative 104 | XCTAssertEqual(x² + y³, y³ + x²) // Commutative 105 | XCTAssertNotEqual(x² + y², x² + y³) 106 | } 107 | 108 | func testExponentiationAddTermAndReversed() { 109 | let xy = x*y 110 | let yx = y*x 111 | XCTAssertEqual(x² + xy, xy + x²) // Commutative 112 | XCTAssertEqual(y² + xy, xy + y²) // Commutative 113 | XCTAssertEqual(x² + xy, x² + yx) // Commutative 114 | XCTAssertNotEqual(x² + xy, y² + xy) 115 | } 116 | 117 | func testExponentiationAddPolynomialAndReversed() { 118 | let eq = x + 2 119 | XCTAssertEqual(x² + eq, eq + x²) 120 | XCTAssertEqual(y² + eq, eq + y²) 121 | XCTAssertNotEqual(x² + eq, y² + eq) 122 | } 123 | 124 | // MARK: - Term 125 | func testTermAddTerm() { 126 | let xy = x*y 127 | let xz = x*z 128 | XCTAssertEqual((xy + xz).asString(sorting: TermSorting(betweenTerms: .termsAlphabetically)), "xy + xz") 129 | XCTAssertEqual((xz + xy).asString(sorting: TermSorting(betweenTerms: .termsAlphabetically)), "xy + xz") 130 | XCTAssertEqual(xy + xz, xz + xy) // Commutative 131 | } 132 | 133 | func testTermAddPolynomialAndReversed() { 134 | let xy = x*y 135 | let xz = x*z 136 | let eq = x + 2 137 | let eq2 = x + 3 138 | 139 | XCTAssertEqual(xy + eq, eq + xy) // Commutative 140 | XCTAssertNotEqual(xy + eq, xz + eq) // Commutative 141 | XCTAssertNotEqual(xy + eq, xy + eq2) 142 | } 143 | 144 | // MARK: - Polynomial 145 | func testPolynomialAddPolynomial() { 146 | let eq = x + 2 147 | let eq2 = y + 3 148 | XCTAssertEqual(eq + eq2, eq2 + eq) // Commutative 149 | XCTAssertEqual(eq + eq2, x + y + 5) 150 | } 151 | 152 | func test3xAddY() { 153 | let eq = x + x + x + y 154 | XCTAssertEqual(eq.description, "3x + y") 155 | } 156 | 157 | 158 | } 159 | 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EquationKit 2 | 3 | ## Write equations in pure Swift, differentiate and/or evaluate them. 4 | 5 | ```swift 6 | let polynomial = (3*x + 5*y - 17) * (7*x - 9*y + 23) 7 | print(polynomial) // 21x² + 8xy - 50x - 45y² + 268y - 391) 8 | let number = polynomial.evaluate() {[ x <- 4, y <- 1 ]} 9 | print(number) // 0 10 | 11 | let y' = polynomial.differentiateWithRespectTo(x) 12 | print(y') // 42x + 8y - 50 13 | y'.evaluate() {[ x <- 1, y <- 1 ]} // 0 14 | 15 | let x' = polynomial.differentiateWithRespectTo(y) 16 | print(x') // 8x - 90y + 268 17 | x'.evaluate() {[ x <- 11.5, y <- 4 ]} // 0 18 | ``` 19 | 20 | # Installation 21 | 22 | ## Swift Package Manager 23 | 24 | ```swift 25 | dependencies: [ 26 | .package(url: "https://github.com/Sajjon/EquationKit", from: "0.1.0") 27 | ] 28 | ``` 29 | 30 | # Usage 31 | 32 | ## Generics 33 | EquationKit is fully generic and supports any number type conforming to the protocol [`NumberExpressible`](Sources/EquationKit/NumberExpressible/NumberExpressible.swift), Swift Foundation's `Int` and `Double` both conforms to said protocol. By conforming to `NumberExpressible` you can use EquationKit with e.g. excellent [attaswift/BigInt](https://github.com/attaswift/BigInt). 34 | 35 | You need only to `import EquationKitBigIntSupport` 36 | 37 | We would like to use operator overloading, making it possible to write `x + y`, `x - 2`, `x*z² - y³` etc. Supporting operator overloading using generic `func + (lhs: Variable, rhs: N) -> PolynomialStruct` results in Swift compiler taking too long time to compile polynomials having over 3 terms (using Xcode 10 beta 6 at least). Thus EquationKit does not come bundled with any operator support at all. Instead, you chose your Number type yourself. If you don't need `BigInt` then `Double` is probably what you want, just `import EquationKitDoubleSupport` and you are good to go! It contains around 10 operators which are all 3 lines of code each. 38 | 39 | ## Variables 40 | You write powers using the custom operator `x^^2`, but for powers between `2` and `9` you can use the [unicode superscript symbols](https://en.wikipedia.org/wiki/Unicode_subscripts_and_superscripts#Superscripts_and_subscripts_block) instead, like so: 41 | ```swift 42 | let x = Variable("x") 43 | let y = Variable("y") 44 | let x² = Exponentiation(x, exponent: 2) 45 | let x³ = Exponentiation(x, exponent: 3) 46 | let x⁴ = Exponentiation(x, exponent: 4) 47 | let x⁵ = Exponentiation(x, exponent: 5) 48 | let x⁶ = Exponentiation(x, exponent: 6) 49 | let x⁷ = Exponentiation(x, exponent: 7) 50 | let x⁸ = Exponentiation(x, exponent: 8) 51 | let x⁹ = Exponentiation(x, exponent: 9) 52 | 53 | let y² = Exponentiation(y, exponent: 2) 54 | ``` 55 | 56 | ## Advanced operators 57 | You can use some of the advanced mathematical operators provided in the folder [MathematicalOperators](Sources/EquationKit/MathematicalOperators) to precisely express the mathematical constraints you might have. 58 | 59 | ### Variable to Constant (evaluation) 60 | Let's have a look at one of the simplest scenario: 61 | 62 | Using the special Unicode char `≔` (single character for `:=` often used in literature for `assignment` of value.) we can write evaluations as: 63 | ```swift 64 | 𝑦² - 𝑥³.evaluate() {[ x ≔ 1, y ≔ 2 ]} 65 | ``` 66 | 67 | Instead of: 68 | ```swift 69 | 𝑦² - 𝑥³.evaluate() {[ x <- 1, y <- 2 ]} 70 | ``` 71 | 72 | ### Complex examples 73 | 74 | Below is the example of how [`EllipticCurveKit`](https://github.com/Sajjon/EllipticCurveKit) uses `EquationKit` to express requirements on the elliptic curve parameters. Elliptic curves on the Weierstraß form requires this congruence inequality to hold: 75 | 76 | ```math 77 | 𝟜𝑎³ + 𝟚𝟟𝑏² ≢ 𝟘 mod 𝑝 78 | ``` 79 | 80 | Thanks to `EquationKit` we can express said inequality almost identically to pure math in Swift: 81 | ```swift 82 | 𝟜𝑎³ + 𝟚𝟟𝑏² ≢ 0 % 𝑝 83 | ``` 84 | 85 | But that is not enough since we also need to evaluate said inequality (polynomial) using the arguments passed in the initializer. We can of course write 86 | ```swift 87 | (𝟜𝑎³ + 𝟚𝟟𝑏²).evaluate(modulus: 𝑝) {[ 𝑎 ≔ a, 𝑏 ≔ b ]} != 0 88 | ``` 89 | 90 | But a slightly more "mathy" syntax would be: 91 | ```swift 92 | 𝟜𝑎³ + 𝟚𝟟𝑏² ≢ 𝟘 % 𝑝 ↤ [ 𝑎 ≔ a, 𝑏 ≔ b ] 93 | ``` 94 | 95 | 96 | Which evaluates the polynomial `𝟜𝑎³ + 𝟚𝟟𝑏²` given `a` and `b` and performs modulo `𝑝` and compares it to `0`. We could, of course, add support for this syntax as well: 97 | ```swift 98 | // This syntax is not yet supported, but can easily be added 99 | [a→𝑎, b→𝑏] ⟼ 𝟜𝑎³ + 𝟚𝟟𝑏² ≢ 𝟘 % 𝑝 100 | ``` 101 | 102 | We can of course also write this without using any special unicode char, like so: 103 | ```swift 104 | 4*a^^3 + 27*b^^2 =!%= 0 % p <-- [ a ≔ constA, b ≔ constB ] 105 | ``` 106 | where `=!%=` replaces `≢`. 107 | 108 | Please give feedback on the choice of operators, by [submitting an issue](https://github.com/Sajjon/EquationKit/issues/new). 109 | 110 | ```swift 111 | let 𝑎 = Variable("𝑎") 112 | let 𝑏 = Variable("𝑏") 113 | let 𝑎³ = Exponentiation(𝑎, exponent: 3) 114 | let 𝑏² = Exponentiation(𝑏, exponent: 2) 115 | 116 | let 𝟜𝑎³ = 4*𝑎³ 117 | let 𝟚𝟟𝑏² = 27*𝑏² 118 | let 𝟘: BigInt = 0 119 | 120 | /// 121 | /// Elliptic Curve on Short Weierstraß form (`𝑆`) 122 | /// - Covers all elliptic curves char≠𝟚,𝟛 123 | /// - Mixed Jacobian coordinates have been the speed leader for a long time. 124 | /// 125 | /// 126 | /// # Equation 127 | /// 𝑆: 𝑦² = 𝑥³ + 𝑎𝑥 + 𝑏 128 | /// - Requires: `𝟜𝑎³ + 𝟚𝟟𝑏² ≠ 𝟘 in 𝔽_𝑝 (mod 𝑝)` 129 | /// 130 | struct ShortWeierstraßCurve { 131 | /// Try to initialize an elliptic curve on the ShortWeierstraß form using parameters for `a`, `b` in the given Galois field (mod 𝑝). 132 | public init(a: BigInt, b: BigInt, field 𝑝: BigInt) throws { 133 | guard 134 | 𝟜𝑎³ + 𝟚𝟟𝑏² ≢ 𝟘 % 𝑝 ↤ [ 𝑎 ≔ a, 𝑏 ≔ b ] 135 | else { throw EllipticCurveError.invalidCurveParameters } 136 | self.a = a 137 | self.b = b 138 | self.field = 𝑝 139 | } 140 | } 141 | ``` 142 | 143 | 144 | ## Supported 145 | - Single and multivariate equations (no limitation to how many variables, go crazy!) 146 | - Differentiate any single or multivariate equation with respect to some of its variables 147 | - Multiply equations with equations 148 | - Modulus 149 | - BigInt support 150 | 151 | ## Limitations 152 | 153 | ### Not supported, but on roadmap 154 | - Substitution `(3*(4*x + 5)^^2 - 2*(4x+5) - 1).substitute() { z <~ (4*x + 5) }` // `3*z²-2*z-1` 155 | - Division 156 | - Finding roots (solving) 157 | 158 | 159 | ### Not supported and not on the roadmap 160 | - Variables in exponents, such as `2^x` 161 | - `log`/`ln` functions 162 | - Trigonometric functions (`sin`, `cos`, `tan` etc.) 163 | - Complex numbers 164 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/TermSortingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TermSortingTests.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-18. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import EquationKit 12 | @testable import EquationKitDoubleSupport 13 | 14 | class TermSortingTests: DoubleTestsBase { 15 | 16 | static var allTests = [ 17 | ("testTrivial", testTrivial), 18 | ("testSingleSortingWithOneElement", testSingleSortingWithOneElement), 19 | ("testSingleSortingWithTwoElementsUnsortedAlphabetically", testSingleSortingWithTwoElementsUnsortedAlphabetically), 20 | ("testSingleSortingElementsUnsortedExponent", testSingleSortingElementsUnsortedExponent), 21 | ("testSingleSortingElementsCoefficients", testSingleSortingElementsCoefficients), 22 | ("testSortingOfTermsOfPolynomialByMostVariables", testSortingOfTermsOfPolynomialByMostVariables), 23 | ("testSortingOfTermsOfPolynomialByAlphabeticOrder", testSortingOfTermsOfPolynomialByAlphabeticOrder), 24 | ("testXYEqualsYX", testXYEqualsYX), 25 | ("testSortingOfExponentiationsInsideTerms", testSortingOfExponentiationsInsideTerms), 26 | ("testSingleSortingMultiVariantAlphabetically", testSingleSortingMultiVariantAlphabetically), 27 | ("testSingleSortingExponentTwoValues", testSingleSortingExponentTwoValues), 28 | ("testSortingMultiExponentiationTermsAlphabetically", testSortingMultiExponentiationTermsAlphabetically), 29 | ("testSingleSortingElementsUnsortedByVariableCount", testSingleSortingElementsUnsortedByVariableCount), 30 | ("testMultiSortingAlreadySortedComplicated", testMultiSortingAlreadySortedComplicated), 31 | ("testComparable", testComparable), 32 | ] 33 | 34 | override func setUp() { 35 | super.setUp() 36 | continueAfterFailure = false 37 | } 38 | 39 | func testTrivial() { 40 | 41 | func trivial(_ terms: [Term]) { 42 | XCTAssertEqual(terms, terms) 43 | } 44 | trivial([tx]) 45 | trivial([ty]) 46 | trivial([tz]) 47 | trivial([tx, tx]) 48 | trivial([ty, ty]) 49 | trivial([tz, tz]) 50 | trivial([tx, ty]) 51 | trivial([tx, tz]) 52 | trivial([ty, tx]) 53 | trivial([ty, tz]) 54 | trivial([tz, tx]) 55 | trivial([tz, ty]) 56 | trivial([tx, ty, tz]) 57 | trivial([tx, tz, ty]) 58 | trivial([ty, tz, tx]) 59 | trivial([tz, tx, ty]) 60 | trivial([tz, ty, tx]) 61 | } 62 | 63 | func testSingleSortingWithOneElement() { 64 | let terms = [tx] 65 | SortingBetweenTerms.defaultArray.forEach { 66 | XCTAssertEqual(terms[0], terms.sorting(betweenTerms: $0)[0]) 67 | } 68 | } 69 | 70 | func testSingleSortingWithTwoSortedElement() { 71 | let terms = [tx, ty] 72 | SortingBetweenTerms.defaultArray.forEach { 73 | XCTAssertEqual(terms, terms.sorting(betweenTerms: $0)) 74 | } 75 | } 76 | 77 | func testSingleSortingWithTwoElementsUnsortedAlphabetically() { 78 | let terms = [ty, tx] 79 | XCTAssertEqual(terms.sorting(betweenTerms: .coefficient), terms) 80 | XCTAssertEqual(terms.sorting(betweenTerms: .descendingExponent), terms) 81 | XCTAssertEqual(terms.sorting(betweenTerms: .termsWithMostVariables), terms) 82 | XCTAssertEqual(terms.sorting(betweenTerms: .termsAlphabetically), [tx, ty]) 83 | } 84 | 85 | func testSingleSortingElementsUnsortedExponent() { 86 | let terms = [ty², tz³, ty, tx², tx³, tx] 87 | XCTAssertEqual(terms.sorting(betweenTerms: .coefficient), terms) 88 | XCTAssertEqual(terms.sorting(betweenTerms: .termsWithMostVariables), terms) 89 | XCTAssertEqual(terms.sorting(betweenTerms: .termsAlphabetically), [tx², tx³, tx, ty², ty, tz³]) 90 | XCTAssertEqual(terms.sorting(betweenTerms: .descendingExponent), [tz³, tx³, ty², tx², ty, tx]) 91 | } 92 | 93 | func testSingleSortingElementsCoefficients() { 94 | let terms = [-tz, ty², -tz³, -ty, tx², -tx³, tx, ty³] 95 | XCTAssertEqual(terms.sorting(betweenTerms: .termsWithMostVariables), terms) 96 | XCTAssertEqual(terms.sorting(betweenTerms: .descendingExponent), [-tz³, -tx³, ty³, ty², tx², -tz, -ty, tx]) 97 | XCTAssertEqual(terms.sorting(betweenTerms: .termsAlphabetically), [tx², -tx³, tx, ty², -ty, ty³, -tz, -tz³]) 98 | XCTAssertEqual(terms.sorting(betweenTerms: .coefficient), [ty², tx², tx, ty³, -tz, -tz³, -ty, -tx³]) 99 | } 100 | 101 | func testSortingOfTermsOfPolynomialByMostVariables() { 102 | let eq = 2*x*y + 3*y + 5*x 103 | XCTAssertEqual(eq.asString(sorting: TermSorting(betweenTerms: [.termsWithMostVariables, .termsAlphabetically])), "2xy + 5x + 3y") 104 | } 105 | 106 | func testSortingOfTermsOfPolynomialByAlphabeticOrder() { 107 | let eq = 2*x*y + 3*y + 5*x 108 | XCTAssertEqual(eq.asString(sorting: TermSorting(betweenTerms: [.termsWithMostVariables, .termsAlphabetically])), "2xy + 5x + 3y") 109 | 110 | } 111 | 112 | func testXYEqualsYX() { 113 | let tyx = Term(y, x) 114 | XCTAssertEqual(txy, tyx) 115 | } 116 | 117 | func testSortingOfExponentiationsInsideTerms() { 118 | XCTAssertEqual(txyz.variableNames, "xyz") 119 | XCTAssertEqual(tx²z³.asString(sorting: .variablesAlphabetically), "x²z³") 120 | XCTAssertEqual(tx²z³.asString(sorting: .descendingExponent), "z³x²") 121 | } 122 | 123 | func testSingleSortingMultiVariantAlphabetically() { 124 | 125 | XCTAssertEqual([tz, tx, txy].sorting(betweenTerms: .termsAlphabetically), [tx, txy, tz]) 126 | XCTAssertEqual([tx, txy, tz].sorting(betweenTerms: .termsAlphabetically), [tx, txy, tz]) 127 | 128 | // Alphabetical sorting is coefficient (sign) agnositc 129 | XCTAssertEqual([-txyz, txyz].sorting(betweenTerms: .termsAlphabetically), [-txyz, txyz]) 130 | XCTAssertEqual([txyz, -txyz].sorting(betweenTerms: .termsAlphabetically), [txyz, -txyz]) 131 | 132 | // Alphabetical sorting is exponent agnositc 133 | XCTAssertEqual([txyz, tx²y²z²].sorting(betweenTerms: .termsAlphabetically), [txyz, tx²y²z²]) 134 | XCTAssertEqual([tx²y²z², txyz].sorting(betweenTerms: .termsAlphabetically), [tx²y²z², txyz]) 135 | 136 | XCTAssertEqual([txyz, tx].sorting(betweenTerms: .termsAlphabetically), [tx, txyz]) 137 | } 138 | 139 | func testSingleSortingExponentTwoValues() { 140 | let terms = [tx²z³, tx³] 141 | XCTAssertEqual(terms.sorting(betweenTerms: .descendingExponent), [tx²z³, tx³]) 142 | } 143 | 144 | func testSortingMultiExponentiationTermsAlphabetically() { 145 | let terms = [tx²y²z³, ty] 146 | XCTAssertEqual(terms, terms.map { $0.sortingExponentiations(by: .descendingExponent) }) 147 | XCTAssertEqual(terms.sorting(betweenTerms: .termsAlphabetically), [ty, tx²y²z³]) 148 | XCTAssertEqual(terms.map { $0.sortingExponentiations(by: .variablesAlphabetically) } .sorting(betweenTerms: .termsAlphabetically), [tx²y²z³, ty]) 149 | } 150 | 151 | func testSingleSortingElementsUnsortedByVariableCount() { 152 | let terms = [-txyz, tx²y²z², -tx²z³, -ty, tx², -tx³, txy, tx²y²z³, tx⁴].map { $0.sortingExponentiations(by: .variablesAlphabetically) } 153 | 154 | SortingBetweenTerms.defaultArray.forEach { 155 | XCTAssertEqual(terms.sorting(betweenTerms: $0), terms.sorting(betweenTerms: $0)) 156 | } 157 | 158 | XCTAssertEqual(tx²y²z³.description, "z³x²y²") 159 | 160 | XCTAssertEqual(terms.sorting(betweenTerms: .coefficient), [tx²y²z², tx², txy, tx²y²z³, tx⁴, -txyz, -tx²z³, -ty, -tx³]) 161 | XCTAssertEqual(terms.sorting(betweenTerms: .descendingExponent), [tx⁴, -tx²z³, -tx³, tx²y²z³, tx²y²z², tx², -txyz, -ty, txy]) 162 | XCTAssertEqual(terms.sorting(betweenTerms: .termsAlphabetically), [tx², -tx³, tx⁴, txy, -txyz, tx²y²z², tx²y²z³, -tx²z³, -ty].map { $0.sortingExponentiations(by: .variablesAlphabetically) } ) 163 | } 164 | 165 | func testMultiSortingAlreadySortedComplicated() { 166 | let sorted = [tx⁴, ty³, tx², txz ,tx, ty] 167 | XCTAssertEqual(sorted, sorted.sorted()) 168 | XCTAssertEqual(sorted, sorted.reversed().sorted()) 169 | } 170 | 171 | func testComparable() { 172 | // We are comparing the expected inter term order 173 | XCTAssertLessThan(tx, ty, "we prefer `x + y` over `y + x`") 174 | XCTAssertLessThan(tx², tx, "we prefer `x² + x` over `x + x²`") 175 | XCTAssertLessThan(txz, tx, "we prefer `xz + x` over `x + xz`") 176 | XCTAssertLessThan(tx, -tx, "we prefer `x - x` over `-x + x`") 177 | 178 | // inverse of above, using `GreaterThan` 179 | XCTAssertGreaterThan(ty, tx, "we prefer `x + y` over `y + x`") 180 | XCTAssertGreaterThan(tx, tx², "we prefer `x² + x` over `x + x²`") 181 | XCTAssertGreaterThan(tx, txz, "we prefer `xz + x` over `x + xz`") 182 | XCTAssertGreaterThan(-tx, tx, "we prefer `x - x` over `-x + x`") 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportDoubleTests/Tests/Concatenation/Subtraction/ConcatenationBySubtractionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConcatenationBySubtractionTests.swift 3 | // EquationKitTests 4 | // 5 | // Created by Alexander Cyon on 2018-08-17. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | @testable import EquationKit 12 | @testable import EquationKitDoubleSupport 13 | 14 | class ConcatenationBySubtractionTests: DoubleTestsBase { 15 | 16 | 17 | static var allTests = [ 18 | ("testNumberSubtractVariableAndReversed", testNumberSubtractVariableAndReversed), 19 | ("testNumberSubtractExponentiationAndReversed", testNumberSubtractExponentiationAndReversed), 20 | ("testNumberSubtractTermAndReversed", testNumberSubtractTermAndReversed), 21 | ("testNumberSubtractPolynomialAndReversed", testNumberSubtractPolynomialAndReversed), 22 | ("testVariableSubtractVariable", testVariableSubtractVariable), 23 | ("testXMinusX", testXMinusX), 24 | ("testX²MinusX²", testX²MinusX²), 25 | ("testXTimesXMinusXTimesX", testXTimesXMinusXTimesX), 26 | ("test2X", test2X), 27 | ("test2XMinusXTwice", test2XMinusXTwice), 28 | ("testVariableSubtractExponentiationAndReversed", testVariableSubtractExponentiationAndReversed), 29 | ("testVariableSubtractTermAndReversed", testVariableSubtractTermAndReversed), 30 | ("testControlIfDuplicates", testControlIfDuplicates), 31 | ("testVariableSubtractPolynomialAndReversed", testVariableSubtractPolynomialAndReversed), 32 | ("testExponentiationSubtractExponentiation", testExponentiationSubtractExponentiation), 33 | ("testExponentiationSubtractTermAndReversed", testExponentiationSubtractTermAndReversed), 34 | ("testExponentiationSubtractPolynomialAndReversed", testExponentiationSubtractPolynomialAndReversed), 35 | ("testTermSubtractTerm", testTermSubtractTerm), 36 | ("testTermSubtractPolynomialAndReversed", testTermSubtractPolynomialAndReversed), 37 | ("testPolynomialSubtractPolynomial", testPolynomialSubtractPolynomial), 38 | ] 39 | 40 | // MARK: - Number 41 | func testNumberSubtractVariableAndReversed() { 42 | XCTAssertEqual(x - 2, x - 2) // trivial 43 | XCTAssertEqual(x - 2, x - 2.0) 44 | 45 | // Inequality because of lack of commutativity 46 | XCTAssertNotEqual(x - 2, 2 - x) 47 | XCTAssertNotEqual(x - 2.5, 2.5 - x) 48 | 49 | XCTAssertNotEqual(x - 2, y - 2) 50 | 51 | XCTAssertNotEqual(x - 0.5, 0.4 - x) 52 | XCTAssertNotEqual(x - 1, 2 - x) 53 | } 54 | 55 | func testNumberSubtractExponentiationAndReversed() { 56 | XCTAssertEqual(Exponentiation(x, exponent: 2), x²) 57 | 58 | XCTAssertEqual(1 - x², 1 - x²) // trivial 59 | XCTAssertEqual(1.0 - x², 1 - x²) 60 | 61 | // Inequality because of lack of commutativity 62 | XCTAssertNotEqual(1 - x², x² - 1) 63 | XCTAssertNotEqual(1 - y², y² - 1) 64 | XCTAssertNotEqual(1 - x², 1 - y²) 65 | } 66 | 67 | func testNumberSubtractTermAndReversed() { 68 | let xy = x*y 69 | let yx = y*x 70 | XCTAssertEqual(yx, xy) // Multiplication is commutative 71 | 72 | XCTAssertEqual(xy - 2, xy - 2) // trivial 73 | 74 | // Inequality because of lack of commutativity 75 | XCTAssertNotEqual(xy - 2, 2 - xy) 76 | } 77 | 78 | func testNumberSubtractPolynomialAndReversed() { 79 | let eq = x + 2 80 | let eq2 = x + 5 81 | 82 | XCTAssertEqual(eq - 2, eq - 2) // trivial 83 | XCTAssertEqual(eq - 2, x + 0) 84 | XCTAssertEqual((eq - 3).description, "x - 1") 85 | 86 | // Inequality because of lack of commutativity 87 | XCTAssertNotEqual(3 - eq, eq2) 88 | XCTAssertNotEqual(1 - eq - 2, eq2) 89 | XCTAssertNotEqual(1.5 - eq - 1.5, eq2) 90 | } 91 | 92 | // MARK: - Variable 93 | func testVariableSubtractVariable() { 94 | XCTAssertEqual((y - x).asString(sorting: .coefficient), "y - x") 95 | XCTAssertEqual(x - x, x - x) // trivial 96 | XCTAssertEqual(x - y, x - y) // trivial 97 | XCTAssertEqual((x - y).asString(sorting: .coefficient), "x - y") 98 | XCTAssertNotEqual(x - y, y - x) 99 | } 100 | 101 | 102 | func testXMinusX() { 103 | XCTAssertEqual(x-x, y-y) 104 | } 105 | 106 | func testX²MinusX²() { 107 | XCTAssertEqual(x²-x², y-y) 108 | } 109 | 110 | func testXTimesXMinusXTimesX() { 111 | XCTAssertEqual(x*x-x*x, y-y) 112 | } 113 | 114 | func test2X() { 115 | XCTAssertEqual((2*x).description, "2x") 116 | } 117 | 118 | func test2XMinusXTwice() { 119 | XCTAssertEqual(2*x-x-x, y-y) 120 | } 121 | 122 | func testVariableSubtractExponentiationAndReversed() { 123 | let 𝑥²-𝑥 = x² - x 124 | let 𝑥-𝑥² = x - x² 125 | XCTAssertEqual(𝑥²-𝑥.evaluate() { x <- 2 }, 2) 126 | XCTAssertEqual(𝑥²-𝑥.evaluate() { x <- -2 }, 6) 127 | XCTAssertEqual(𝑥²-𝑥.evaluate() { x <- 3 }, 6) 128 | XCTAssertEqual(𝑥²-𝑥.evaluate() { x <- -3 }, 12) 129 | XCTAssertEqual(𝑥²-𝑥.evaluate() { x <- 4 }, 12) 130 | XCTAssertEqual(𝑥²-𝑥.evaluate() { x <- -4 }, 20) 131 | XCTAssertEqual(𝑥²-𝑥.evaluate() { x <- 5 }, 20) 132 | XCTAssertEqual(𝑥²-𝑥.evaluate() { x <- -5 }, 30) 133 | 134 | XCTAssertEqual(𝑥-𝑥².evaluate() { x <- 2 }, -2) 135 | XCTAssertEqual(𝑥-𝑥².evaluate() { x <- -2 }, -6) 136 | XCTAssertEqual(𝑥-𝑥².evaluate() { x <- 3 }, -6) 137 | XCTAssertEqual(𝑥-𝑥².evaluate() { x <- -3 }, -12) 138 | XCTAssertEqual(𝑥-𝑥².evaluate() { x <- 4 }, -12) 139 | XCTAssertEqual(𝑥-𝑥².evaluate() { x <- -4 }, -20) 140 | XCTAssertEqual(𝑥-𝑥².evaluate() { x <- 5 }, -20) 141 | XCTAssertEqual(𝑥-𝑥².evaluate() { x <- -5 }, -30 ) 142 | 143 | XCTAssertNotEqual(x² - x, x - x²) 144 | XCTAssertNotEqual(x² - x, x² - y) 145 | XCTAssertNotEqual(x² - x, x³ - x) 146 | } 147 | 148 | 149 | func testVariableSubtractTermAndReversed() { 150 | let xy = x*y 151 | 152 | let 𝑥-𝑥𝑦 = x - xy 153 | XCTAssertEqual(𝑥-𝑥𝑦.evaluate() {[ x <- 5, y <- 3 ]}, -10) 154 | XCTAssertEqual(𝑥-𝑥𝑦.evaluate() {[ x <- 5, y <- -3 ]}, 20) 155 | XCTAssertEqual(𝑥-𝑥𝑦.evaluate() {[ x <- 6, y <- -4 ]}, 30) 156 | 157 | 158 | let 𝑥𝑦-𝑥 = xy-x 159 | XCTAssertEqual(𝑥𝑦-𝑥.evaluate() {[ x <- 5, y <- 3 ]}, 10) 160 | XCTAssertEqual(𝑥𝑦-𝑥.evaluate() {[ x <- 7, y <- 11 ]}, 70) 161 | 162 | XCTAssertNotEqual(x - xy, xy - x) 163 | XCTAssertNotEqual(x - xy, y - xy) 164 | } 165 | 166 | func testControlIfDuplicates() { 167 | let constants = [Constant(x, value: 3), Constant(y, value: 1)] 168 | XCTAssertFalse(constants.containsDuplicates()) 169 | } 170 | 171 | func testVariableSubtractPolynomialAndReversed() { 172 | let eq = x - 2 173 | 174 | XCTAssertNotEqual(x - eq, eq - x) 175 | XCTAssertNotEqual(x - eq, y - eq) 176 | 177 | XCTAssertEqual(x - eq, 2) 178 | XCTAssertEqual((y - eq).asString(sorting: .coefficient), "y - x + 2") 179 | XCTAssertEqual((y - eq).evaluate() {[ x <- 3, y <- 1 ]}, 0) 180 | } 181 | 182 | // MARK: - Exponentiation 183 | func testExponentiationSubtractExponentiation() { 184 | XCTAssertEqual(x² - x², 0) 185 | XCTAssertEqual(x³ - x³, 0) 186 | XCTAssertEqual(y² - y², 0) 187 | XCTAssertEqual(y³ - y³, 0) 188 | 189 | XCTAssertNotEqual(x² - y², y² - x²) 190 | 191 | XCTAssertEqual((x² - y²).description, "x² - y²") 192 | XCTAssertNotEqual(x³ - y², y² - x³) 193 | XCTAssertEqual((x³ - y²).description, "x³ - y²") 194 | XCTAssertNotEqual(x² - y³, y³ - x²) 195 | XCTAssertNotEqual(x² - y², x² - y³) 196 | } 197 | 198 | 199 | func testExponentiationSubtractTermAndReversed() { 200 | let xy = x*y 201 | let yx = y*x 202 | 203 | XCTAssertNotEqual(x² - xy, xy - x²) 204 | XCTAssertNotEqual(y² - xy, xy - y²) 205 | XCTAssertEqual(x² - xy, x² - yx) 206 | XCTAssertNotEqual(x² - xy, y² - xy) 207 | } 208 | 209 | func testExponentiationSubtractPolynomialAndReversed() { 210 | let eq = x - 2 211 | 212 | XCTAssertEqual((x² - eq).description, "x² - x + 2") 213 | 214 | XCTAssertNotEqual(x² - eq, eq - x²) 215 | XCTAssertNotEqual(y² - eq, eq - y²) 216 | XCTAssertNotEqual(x² - eq, y² - eq) 217 | } 218 | 219 | // MARK: - Term 220 | func testTermSubtractTerm() { 221 | let xy = x*y 222 | let xz = x*z 223 | 224 | XCTAssertEqual((xy - xz).asString(sorting: .coefficient), "xy - xz") 225 | XCTAssertEqual((xz - xy).asString(sorting: .coefficient), "xz - xy") 226 | XCTAssertEqual(xy - xz, x*(y - z)) 227 | 228 | XCTAssertNotEqual(xy - xz, xz - xy) 229 | } 230 | 231 | func testTermSubtractPolynomialAndReversed() { 232 | let xy = x*y 233 | let xz = x*z 234 | let eq = x - 2 235 | let eq2 = x - 3 236 | 237 | XCTAssertEqual((xy - eq).asString(sorting: .coefficient), "xy - x + 2") 238 | XCTAssertEqual((eq - xy).asString(sorting: .coefficient), "x - xy - 2") 239 | 240 | XCTAssertNotEqual(xy - eq, eq - xy) 241 | XCTAssertNotEqual(xy - eq, xz - eq) 242 | XCTAssertNotEqual(xy - eq, xy - eq2) 243 | } 244 | 245 | // MARK: - Polynomial 246 | func testPolynomialSubtractPolynomial() { 247 | let eq = x - 2 248 | let eq2 = y - 3 249 | 250 | XCTAssertEqual((eq - eq2).asString(sorting: .coefficient), "x - y + 1") 251 | XCTAssertEqual((eq2 - eq).asString(sorting: .coefficient), "y - x - 1") 252 | 253 | XCTAssertNotEqual(eq - eq2, eq2 - eq) 254 | XCTAssertNotEqual(eq - eq2, x - y - 5) 255 | } 256 | } 257 | 258 | -------------------------------------------------------------------------------- /Tests/EquationKitSupportBigIntTests/Tests/MathematicalOperatorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MathematicalOperatorTests.swift 3 | // EquationKitBigIntTests 4 | // 5 | // Created by Alexander Cyon on 2018-09-01. 6 | // Copyright © 2018 Sajjon. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import XCTest 11 | 12 | @testable import EquationKit 13 | @testable import EquationKitBigIntSupport 14 | 15 | 16 | import BigInt 17 | 18 | // MARK: Test cases 19 | 20 | class MathematicalOperatorTests: XCTestCase { 21 | 22 | static var allTests = [ 23 | ("testSecp256k1Parameters", testSecp256k1Parameters), 24 | ("testSecp256r1Parameters", testSecp256r1Parameters), 25 | ("testSecp521r1", testSecp521r1), 26 | ("testInvalidParameters", testInvalidParameters), 27 | ("testAssignmentOperator", testAssignmentOperator), 28 | ("testEvaluationOperators", testEvaluationOperators), 29 | ("testCongruenceOperators", testCongruenceOperators), 30 | 31 | ] 32 | 33 | func testSecp256k1Parameters() { 34 | let a: BigInt = 0 35 | let b: BigInt = 7 36 | let p: BigInt = 2^^256 - 2^^32 - 977 37 | XCTAssertEqual(p.toHex(), "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F") 38 | XCTAssertTrue(validityOfShortWeierstraßCurveParameters(a: a, b: b, modulus: p)) 39 | } 40 | 41 | func testSecp256r1Parameters() throws { 42 | let a = try XCTUnwrap(BigInt("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", radix: 16)) 43 | let b = try XCTUnwrap(BigInt("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", radix: 16)) 44 | let p: BigInt = 2^^224 * (2^^32 - 1) + 2^^192 + 2^^96 - 1 45 | XCTAssertEqual(p.toHex(), "0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF") 46 | XCTAssertTrue(validityOfShortWeierstraßCurveParameters(a: a, b: b, modulus: p)) 47 | } 48 | 49 | func testSecp521r1() throws { 50 | 51 | let bigIntAHexString = """ 52 | 01FF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 53 | FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 54 | FFFFFFFF FFFFFFFF FFFFFFFC 55 | """.replacingOccurrences(of: "^\\s*", with: "", options: .regularExpression) 56 | let a = try XCTUnwrap(BigInt(bigIntAHexString.trimmed(), radix: 16)) 57 | 58 | XCTAssertEqual(a.description, "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057148") 59 | 60 | let p: BigInt = 2^^521 - 1 61 | XCTAssertEqual(p.description, "6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151") 62 | 63 | XCTAssertEqual(p.toHex(prefixWith0x: false), 64 | """ 65 | 01FF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 66 | FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 67 | FFFFFFFF FFFFFFFF FFFFFFFF 68 | """.trimmed().removeLeadingZeros() 69 | ) 70 | 71 | let bigIntBHexString = """ 72 | 0051 953EB961 8E1C9A1F 929A21A0 B68540EE A2DA725B 99B315F3 73 | B8B48991 8EF109E1 56193951 EC7E937B 1652C0BD 3BB1BF07 3573DF88 74 | 3D2C34F1 EF451FD4 6B503F00 75 | """.replacingOccurrences(of: "^\\s*", with: "", options: .regularExpression) 76 | 77 | let b = try XCTUnwrap(BigInt(bigIntBHexString.trimmed(), radix: 16)) 78 | 79 | XCTAssertEqual(b.description, "1093849038073734274511112390766805569936207598951683748994586394495953116150735016013708737573759623248592132296706313309438452531591012912142327488478985984") 80 | 81 | XCTAssertTrue(validityOfShortWeierstraßCurveParameters(a: a, b: b, modulus: p)) 82 | } 83 | 84 | func testInvalidParameters() { 85 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: -3, b: 2, modulus: 5)) 86 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: -3, b: -2, modulus: 5)) 87 | 88 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 16, modulus: 62)) 89 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 15, modulus: 47)) 90 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 12, modulus: 82)) 91 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 8, modulus: 92)) 92 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 8, modulus: 46)) 93 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 6, modulus: 67)) 94 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 6, modulus: 53)) 95 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 3, modulus: 89)) 96 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 19, b: 2, modulus: 88)) 97 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 18, b: 16, modulus: 96)) 98 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 16, b: -16, modulus: 64)) 99 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 16, b: -17, modulus: 67)) 100 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 12, b: 11, modulus: 39)) 101 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 12, b: -20, modulus: 82)) 102 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 11, b: -2, modulus: 97)) 103 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 9, b: 4, modulus: 93)) 104 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 9, b: -3, modulus: 81)) 105 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: 9, b: -13, modulus: 27)) 106 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: -15, b: -7, modulus: 99)) 107 | XCTAssertFalse(validityOfShortWeierstraßCurveParameters(a: -15, b: 18, modulus: 99)) 108 | } 109 | 110 | func testAssignmentOperator() { 111 | XCTAssertEqual(x ≔ 2, x <- 2) 112 | } 113 | 114 | func testEvaluationOperators() { 115 | let poly = x² + y 116 | 117 | XCTAssertTrue(poly == 5 ↤ [x ≔ 2, y ≔ 1 ]) 118 | XCTAssertFalse(poly == 1337 ↤ [x ≔ 2, y ≔ 1 ]) 119 | 120 | XCTAssertTrue(poly != 1337 ↤ [x ≔ 2, y ≔ 1 ]) 121 | XCTAssertFalse(poly != 5 ↤ [x ≔ 2, y ≔ 1 ]) 122 | XCTAssertTrue(poly ≠ 1337 ↤ [x ≔ 2, y ≔ 1 ]) 123 | XCTAssertFalse(poly ≠ 5 ↤ [x ≔ 2, y ≔ 1 ]) 124 | 125 | let constants = [x ≔ 123, y ≔ 987 ] 126 | let value: BigInt = 1 << 65 127 | XCTAssertEqual( 128 | poly != value ↤ constants, 129 | poly ≠ value ↤ constants 130 | ) 131 | } 132 | 133 | func testCongruenceOperators() { 134 | 135 | let poly = x² + y 136 | let op1 = 7 <-- [x <- 2, y <- 3] 137 | let op2 = 7 ↤ [x <- 2, y <- 3] 138 | XCTAssertTrue(type(of: op1) == NumberAndConstants.self) 139 | XCTAssertTrue(type(of: op2) == NumberAndConstants.self) 140 | 141 | let congruentEq = 0 % op1 142 | XCTAssertTrue(type(of: congruentEq) == CongruentEqualityOperand.self) 143 | 144 | 145 | XCTAssertEqual(poly =%= congruentEq, true) 146 | XCTAssertEqual(poly ≡≡ congruentEq, true) 147 | XCTAssertEqual(poly =%= congruentEq, poly ≡≡ congruentEq) 148 | let congruentNeq = 0 % 6 <-- [x <- 2, y <- 3] 149 | 150 | XCTAssertEqual(poly =%= congruentNeq, false) 151 | XCTAssertEqual(poly ≡≡ congruentNeq, false) 152 | XCTAssertEqual(poly =%= congruentNeq, poly ≡≡ congruentNeq) 153 | 154 | 155 | XCTAssertEqual(poly =!%= congruentEq, false) 156 | XCTAssertEqual(poly !≡ congruentEq, false) 157 | XCTAssertEqual(poly ≢ congruentEq, false) 158 | XCTAssertEqual(poly =!%= congruentEq, poly !≡ congruentEq) 159 | XCTAssertEqual(poly =!%= congruentEq, poly ≢ congruentEq) 160 | XCTAssertEqual(poly !≡ congruentEq, poly ≢ congruentEq) 161 | 162 | XCTAssertEqual(poly =!%= congruentNeq, true) 163 | XCTAssertEqual(poly !≡ congruentNeq, true) 164 | XCTAssertEqual(poly ≢ congruentNeq, true) 165 | XCTAssertEqual(poly =!%= congruentNeq, poly !≡ congruentNeq) 166 | XCTAssertEqual(poly =!%= congruentNeq, poly ≢ congruentNeq) 167 | XCTAssertEqual(poly !≡ congruentNeq, poly ≢ congruentNeq) 168 | } 169 | 170 | } 171 | 172 | // MARK: Private Extension 173 | private extension MathematicalOperatorTests { 174 | 175 | /// 𝑆: 𝑦² = 𝑥³ + 𝐴𝑥 + 𝐵 176 | /// - Requires: `𝟜𝑎³ + 𝟚𝟟𝑏² ≠ 𝟘 in 𝔽_𝑝 (mod 𝑝)` 177 | private func validityOfShortWeierstraßCurveParameters(a: BigInt, b: BigInt, modulus 𝑝: BigInt) -> Bool { 178 | let 𝟜𝑎³ = 4*𝑎³ 179 | let 𝟚𝟟𝑏² = 27*𝑏² 180 | let 𝟘: BigInt = 0 181 | 182 | return 𝟜𝑎³ + 𝟚𝟟𝑏² ≢ 𝟘 % 𝑝 ↤ [ 𝑎 ≔ a, 𝑏 ≔ b ] 183 | } 184 | } 185 | 186 | 187 | // MARK: Helpers 188 | private let 𝑎 = Variable("𝑎") 189 | private let 𝑏 = Variable("𝑏") 190 | private let 𝑎³ = Exponentiation(variable: 𝑎, exponent: 3) 191 | private let 𝑏² = Exponentiation(variable: 𝑏, exponent: 2) 192 | 193 | extension BigInt { 194 | func toString(uppercased: Bool = true, radix: Int) -> String { 195 | let stringRepresentation = String(self, radix: radix) 196 | guard uppercased else { return stringRepresentation } 197 | return stringRepresentation.uppercased() 198 | } 199 | 200 | func toHex(prefixWith0x: Bool = true) -> String { 201 | let prefix = prefixWith0x ? "0x" : "" 202 | return prefix + toString(radix: 16) 203 | } 204 | } 205 | 206 | extension String { 207 | var containsWhitespace: Bool { 208 | return(rangeOfCharacter(from: .whitespacesAndNewlines) != nil) 209 | } 210 | } 211 | 212 | extension BigInt { 213 | public init(stringLiteral value: StringLiteralType) { 214 | var hexString = value.trimmed() 215 | if hexString.starts(with: "0x") { 216 | hexString = String(hexString.dropFirst(2)) 217 | } 218 | self.init(hexString, radix: 16)! 219 | } 220 | } 221 | 222 | extension String { 223 | 224 | func trimmed() -> String { 225 | // As of Xcode 10 Beta 6 there is a bug (or strange feature?) using multiline strings (```) and `trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)`, which does not remove whitespace or newlines. Luckily, good old `replacingOccurrences` works however. 226 | return replacingOccurrences(of: "\n", with: "").replacingOccurrences(of: " ", with: "") 227 | } 228 | 229 | func removeLeadingZeros() -> String { 230 | var string = self 231 | while string.first == "0" { 232 | string = String(string.dropFirst()) 233 | } 234 | return string 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------