├── .gitignore
├── .slather.yml
├── .swiftformat
├── CHANGELOG.md
├── CODEOWNERS
├── Documentation
└── INSTALLATION_GUIDE.md
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── MathExpression.podspec
├── MathExpression.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcshareddata
│ ├── xcbaselines
│ │ └── 9F153DA0231E87A4005A6966.xcbaseline
│ │ │ ├── 93FBB218-913E-4092-9842-36EF602C8886.plist
│ │ │ ├── A2BA1546-25D3-486D-8D64-9A8BE5867597.plist
│ │ │ └── Info.plist
│ └── xcschemes
│ │ ├── MathExpression-iOS.xcscheme
│ │ ├── MathExpression-macOS.xcscheme
│ │ ├── MathExpression-tvOS.xcscheme
│ │ ├── MathExpressionExample.xcscheme
│ │ └── PerformanceTests.xcscheme
└── xcuserdata
│ └── peredaniel.xcuserdatad
│ ├── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes
│ └── xcschememanagement.plist
├── MathExpression
├── Info.plist
├── MathExpression.h
└── Source
│ ├── Extensions
│ ├── FloatingPoint+Extensions.swift
│ └── Formatter+Extensions.swift
│ ├── Models
│ ├── MathBrackets.swift
│ └── MathOperator.swift
│ └── Parser
│ ├── MathExpression.swift
│ └── MathFormula.swift
├── MathExpressionExample
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── arithmetic-1024.png
│ │ ├── arithmetic-120.png
│ │ ├── arithmetic-121.png
│ │ ├── arithmetic-152.png
│ │ ├── arithmetic-167.png
│ │ ├── arithmetic-180.png
│ │ ├── arithmetic-20.png
│ │ ├── arithmetic-29.png
│ │ ├── arithmetic-40.png
│ │ ├── arithmetic-41.png
│ │ ├── arithmetic-42.png
│ │ ├── arithmetic-58.png
│ │ ├── arithmetic-59.png
│ │ ├── arithmetic-60.png
│ │ ├── arithmetic-76.png
│ │ ├── arithmetic-80.png
│ │ ├── arithmetic-81.png
│ │ └── arithmetic-87.png
│ └── Contents.json
├── ExampleApp.swift
├── Extensions
│ ├── Formatter+MathExpression.swift
│ └── Int+Factorial.swift
├── Models
│ ├── MathTransformation.swift
│ └── ValidationError.swift
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
└── Screens
│ ├── Calculator
│ ├── CalculatorView.swift
│ ├── CalculatorViewModel.swift
│ └── Supporting views
│ │ └── CalculatorKeyboardView.swift
│ ├── Evaluator
│ ├── EvaluatorView.swift
│ └── EvaluatorViewModel.swift
│ └── MenuView.swift
├── MathExpressionPerformanceTests
├── Info.plist
└── Source
│ ├── (Q - {0}, *) group tests
│ ├── AbelianMultiplicativeGroupAxiomTests.swift
│ ├── DivisionPerformanceTests.swift
│ ├── ProductAndDivisionPerformanceTests.swift
│ └── ProductPerformanceTests.swift
│ ├── (Q, +) group tests
│ ├── AbelianAdditiveGroupAxiomsPerformanceTests.swift
│ ├── AdditionAndSubtractionPerformanceTests.swift
│ ├── AdditionPerformanceTests.swift
│ └── SubtractionPerformanceTests.swift
│ ├── (Q, +, *) field tests
│ ├── CombinedOperationsPerformanceTest.swift
│ └── FieldAxiomPerformanceTests.swift
│ ├── Non-trivial transformation tests
│ ├── CountTransformationPerformanceTests.swift
│ └── FactorialTransformationPerformanceTests.swift
│ ├── Stress tests
│ └── StressPerformanceTests.swift
│ └── Validation tests
│ └── ValidationPerformanceTests.swift
├── MathExpressionTestHelpers
├── Extensions
│ ├── Double+RoundedToPlaces.swift
│ ├── Numeric+Random.swift
│ ├── String+Random.swift
│ └── XCTestCase+AssertError.swift
└── Helpers
│ ├── Formulae.swift
│ ├── Operation.swift
│ └── RandomExpressionGenerator.swift
├── MathExpressionTests
├── Info.plist
└── Source
│ ├── (Q - {0}, *) group tests
│ ├── AbelianMultiplicativeGroupAxiomTests.swift
│ ├── DivisionTests.swift
│ ├── ProductAndDivisionTests.swift
│ └── ProductTests.swift
│ ├── (Q, +) group tests
│ ├── AbelianAdditiveGroupAxiomsTests.swift
│ ├── AdditionAndSubtractionTests.swift
│ ├── AdditionTests.swift
│ └── SubtractionTests.swift
│ ├── (Q, +, *) field tests
│ ├── CombinedOperationsTest.swift
│ └── FieldAxiomTests.swift
│ ├── Non-trivial transformation tests
│ ├── CountTransformationTests.swift
│ ├── ExponentialTransformationTests.swift
│ └── FactorialTransformationTests.swift
│ └── Validation tests
│ └── ValidationTests.swift
├── Package.swift
├── README.md
└── fastlane
├── Fastfile
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 | *.dSYM.zip
29 | *.dSYM
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | # Package.pins
40 | # Package.resolved
41 | .build/
42 |
43 | # CocoaPods
44 | #
45 | # We recommend against adding the Pods directory to your .gitignore. However
46 | # you should judge for yourself, the pros and cons are mentioned at:
47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48 | #
49 | # Pods/
50 |
51 | # Carthage
52 | #
53 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
54 | # Carthage/Checkouts
55 |
56 | Carthage/Build
57 |
58 | # fastlane
59 | #
60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
61 | # screenshots whenever they are needed.
62 | # For more information about the recommended setup visit:
63 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
64 |
65 | fastlane/report.xml
66 | fastlane/report.html
67 | fastlane/report.junit
68 | fastlane/Preview.html
69 | fastlane/screenshots/**/*.png
70 | fastlane/test_output
71 |
72 | .swiftpm/*
73 |
--------------------------------------------------------------------------------
/.slather.yml:
--------------------------------------------------------------------------------
1 | coverage_service: coveralls
2 | xcodeproj: MathExpression.xcodeproj
3 | scheme: MathExpression-iOS
4 | source_directory: MathExpression/*
5 | ignore:
6 | - MathExpressionPerformanceTests/*
7 | - MathExpressionTestHelpers/*
8 | - MathExpressionTests/*
--------------------------------------------------------------------------------
/.swiftformat:
--------------------------------------------------------------------------------
1 | --allman false
2 | --binarygrouping none
3 | --closingparen balanced
4 | --commas inline
5 | --conflictmarkers reject
6 | --decimalgrouping none
7 | --elseposition same-line
8 | --empty void
9 | --exponentcase lowercase
10 | --exponentgrouping disabled
11 | --fractiongrouping disabled
12 | --fragment false
13 | --header ignore
14 | --hexgrouping none
15 | --hexliteralcase uppercase
16 | --ifdef indent
17 | --importgrouping alphabetized
18 | --indent 4
19 | --indentcase false
20 | --linebreaks lf
21 | --octalgrouping none
22 | --operatorfunc spaced
23 | --patternlet inline
24 | --ranges no-space
25 | --self remove
26 | --selfrequired
27 | --semicolons never
28 | --stripunusedargs always
29 | --trailingclosures
30 | --trimwhitespace always
31 | --wraparguments before-first
32 | --wrapcollections before-first
33 | --xcodeindentation disabled
34 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 1.3.0
2 |
3 | * Bumped minimum Xcode version to 13.0+.
4 | * Bumped Swift version to 5.0 in `Package.swift`.
5 | * Bumped target platform minimum version to 12.0+ (iOS and tvOS) and 10.14 (macOS).
6 | * Improved errors thrown when validation fails due to invalid consecutive operators (on some cases the error was thrown but was of different type).
7 | * Improved tests implementation to check for correct error being thrown.
8 | * Removed Travis CI integration due to Open Source projects support being dropped.
9 | * Reimplemented example app using SwiftUI.
10 | * Improved documentation and added step-by-step installation guide.
11 |
12 | #### Breaking changes
13 |
14 | * `MathExpression.ValidationError.consecutiveMultiplicativeOperators(String)` has been renamed to `MathExpression.ValidationError.invalidConsecutiveOperators(String)`.
15 |
16 | ### 1.2.0
17 |
18 | * Added support for [Swift Package Manager](https://swift.org/package-manager/).
19 | * Added example app.
20 | * Improved documentation.
21 |
22 | ### 1.1.1
23 |
24 | * Fixed issue in operation priority.
25 | * Added further tests.
26 |
27 | ### 1.1.0
28 |
29 | * Added support for tvOS 10 or higher.
30 | * Addes support for macOS 10.10 or higher.
31 | * Improved CI setup.
32 |
33 | ### 1.0.1
34 |
35 | * Improved parentheses validation algorithm (validation between 20% and 80% faster).
36 | * Improved parentheses decomposition algorithm (evaluation between 15% and 85% faster).
37 | * Added stress performance tests.
38 |
39 | ### 1.0.0
40 |
41 | Initial Stable Release - Xcode 10.0+, Swift 4.2+, iOS 10+
42 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @peredaniel
--------------------------------------------------------------------------------
/Documentation/INSTALLATION_GUIDE.md:
--------------------------------------------------------------------------------
1 | In this documentation we provide a step-by-step installation guide, with screenshots and further details than the [general documentation](../README.md). Because no one starts app development by knowing how to do everything!
2 |
3 | We don't include step-by-step guide for Carthage since most developers using this package manager (usually do already know how to solve issues. In case you encounter an issue with Carthage, please open an Issue here in GitHub and we'll take a look.
4 |
5 | ## Using Cocoapods
6 |
7 | First you need to install Cocoapods using the terminal, create a `Podfile` inside your project and declare the module. Your Podfile should look like this:
8 |
9 | ```ruby
10 | # Uncomment the next line to define a global platform for your project
11 | platform :ios, '14.0'
12 | source 'https://cdn.cocoapods.org/'
13 |
14 | # Comment the next line if you don't want to use dynamic frameworks
15 | use_frameworks!
16 |
17 | target 'MyProject' do
18 | pod 'MathExpression'
19 | end
20 | ```
21 |
22 | Maybe specifying a version. Then you execute `pod install --repo-update` on the project root folder (where the Podfile is located). If everything goes right (no error), from now on you must use the `MyProject.xcworkspace` file to open your project instead of the `MyProject.xcodeproj` (in Finder, it's the one with the white icon instead of the one with the blue icon). This is due to the management from Cocoapods. If you open the `xcodeproj` file you'll have an empty `Pods` folder.
23 |
24 | ## Swift Package Manager
25 |
26 | Since Xcode 11, Swift Package Manager is embedded into Xcode, so we strongly recommend using the Xcode interface to add packages. It's just a few steps:
27 | 1. Open your project on Xcode.
28 | 2. Open `File` in the top menu, then `Add Packages...`, and you'll see a screen like this:
29 |
30 | 
31 |
32 | 3. On this new screen there is a search bar in the top-right corner. Type-in or paste the URL for this repository (`https://github.com/peredaniel/MathExpression.git`) and it will change to this:
33 |
34 | 
35 |
36 | 4. Change "Dependency Rule" to "Up to Next Major Version":
37 |
38 | 
39 |
40 | 5. Finally tap "Add Package" on the bottom-right corner of the screen.
41 | 6. This will fetch the `Package.swift` file in this repository, and a confirmation screen appears:
42 |
43 | 
44 |
45 | 7. Unless you are managing more than a single target (usually you don't), simply click "Add Package". Xcode will now fetch the files in the repository and add them to your project:
46 |
47 | 
48 |
49 | From then, you can use `import MathExpression` in any Swift file belonging to the same target.
50 |
51 | ### Manual installation
52 |
53 | Clone this repository into your hard-drive, or download it as a ZIP file and uncompress it. Then drag and drop the `Sources` folder into any group inside your Project **in Xcode**. The following screen should appears:
54 |
55 | 
56 |
57 | Make sure to check "Copy items if needed". We also recommend using "Create groups" instead of "Create folder references", but this is a personal choice. Your project structure should now look like this:
58 |
59 | 
60 |
61 | Now you can use any class, struct or extension as if they were defined in your project (since, in fact, they are!), so no need to add `import MathExpression` when you want to use it. In fact, the module does not exist: the files are treated as your own in your project, in the same way as any other file you add to it.
62 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gem 'fastlane', '~>2.207.0'
4 | gem 'cocoapods', '~>1.11.0'
5 | gem 'slather', '~> 2.7.0'
6 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.5)
5 | rexml
6 | activesupport (6.1.7.3)
7 | concurrent-ruby (~> 1.0, >= 1.0.2)
8 | i18n (>= 1.6, < 2)
9 | minitest (>= 5.1)
10 | tzinfo (~> 2.0)
11 | zeitwerk (~> 2.3)
12 | addressable (2.8.0)
13 | public_suffix (>= 2.0.2, < 5.0)
14 | algoliasearch (1.27.5)
15 | httpclient (~> 2.8, >= 2.8.3)
16 | json (>= 1.5.1)
17 | artifactory (3.0.15)
18 | atomos (0.1.3)
19 | aws-eventstream (1.2.0)
20 | aws-partitions (1.602.0)
21 | aws-sdk-core (3.131.2)
22 | aws-eventstream (~> 1, >= 1.0.2)
23 | aws-partitions (~> 1, >= 1.525.0)
24 | aws-sigv4 (~> 1.1)
25 | jmespath (~> 1, >= 1.6.1)
26 | aws-sdk-kms (1.57.0)
27 | aws-sdk-core (~> 3, >= 3.127.0)
28 | aws-sigv4 (~> 1.1)
29 | aws-sdk-s3 (1.114.0)
30 | aws-sdk-core (~> 3, >= 3.127.0)
31 | aws-sdk-kms (~> 1)
32 | aws-sigv4 (~> 1.4)
33 | aws-sigv4 (1.5.0)
34 | aws-eventstream (~> 1, >= 1.0.2)
35 | babosa (1.0.4)
36 | claide (1.1.0)
37 | clamp (1.3.2)
38 | cocoapods (1.11.3)
39 | addressable (~> 2.8)
40 | claide (>= 1.0.2, < 2.0)
41 | cocoapods-core (= 1.11.3)
42 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
43 | cocoapods-downloader (>= 1.4.0, < 2.0)
44 | cocoapods-plugins (>= 1.0.0, < 2.0)
45 | cocoapods-search (>= 1.0.0, < 2.0)
46 | cocoapods-trunk (>= 1.4.0, < 2.0)
47 | cocoapods-try (>= 1.1.0, < 2.0)
48 | colored2 (~> 3.1)
49 | escape (~> 0.0.4)
50 | fourflusher (>= 2.3.0, < 3.0)
51 | gh_inspector (~> 1.0)
52 | molinillo (~> 0.8.0)
53 | nap (~> 1.0)
54 | ruby-macho (>= 1.0, < 3.0)
55 | xcodeproj (>= 1.21.0, < 2.0)
56 | cocoapods-core (1.11.3)
57 | activesupport (>= 5.0, < 7)
58 | addressable (~> 2.8)
59 | algoliasearch (~> 1.0)
60 | concurrent-ruby (~> 1.1)
61 | fuzzy_match (~> 2.0.4)
62 | nap (~> 1.0)
63 | netrc (~> 0.11)
64 | public_suffix (~> 4.0)
65 | typhoeus (~> 1.0)
66 | cocoapods-deintegrate (1.0.5)
67 | cocoapods-downloader (1.6.3)
68 | cocoapods-plugins (1.0.0)
69 | nap
70 | cocoapods-search (1.0.1)
71 | cocoapods-trunk (1.6.0)
72 | nap (>= 0.8, < 2.0)
73 | netrc (~> 0.11)
74 | cocoapods-try (1.2.0)
75 | colored (1.2)
76 | colored2 (3.1.2)
77 | commander (4.6.0)
78 | highline (~> 2.0.0)
79 | concurrent-ruby (1.2.2)
80 | declarative (0.0.20)
81 | digest-crc (0.6.4)
82 | rake (>= 12.0.0, < 14.0.0)
83 | domain_name (0.5.20190701)
84 | unf (>= 0.0.5, < 1.0.0)
85 | dotenv (2.7.6)
86 | emoji_regex (3.2.3)
87 | escape (0.0.4)
88 | ethon (0.15.0)
89 | ffi (>= 1.15.0)
90 | excon (0.92.3)
91 | faraday (1.10.0)
92 | faraday-em_http (~> 1.0)
93 | faraday-em_synchrony (~> 1.0)
94 | faraday-excon (~> 1.1)
95 | faraday-httpclient (~> 1.0)
96 | faraday-multipart (~> 1.0)
97 | faraday-net_http (~> 1.0)
98 | faraday-net_http_persistent (~> 1.0)
99 | faraday-patron (~> 1.0)
100 | faraday-rack (~> 1.0)
101 | faraday-retry (~> 1.0)
102 | ruby2_keywords (>= 0.0.4)
103 | faraday-cookie_jar (0.0.7)
104 | faraday (>= 0.8.0)
105 | http-cookie (~> 1.0.0)
106 | faraday-em_http (1.0.0)
107 | faraday-em_synchrony (1.0.0)
108 | faraday-excon (1.1.0)
109 | faraday-httpclient (1.0.1)
110 | faraday-multipart (1.0.4)
111 | multipart-post (~> 2)
112 | faraday-net_http (1.0.1)
113 | faraday-net_http_persistent (1.2.0)
114 | faraday-patron (1.0.0)
115 | faraday-rack (1.0.0)
116 | faraday-retry (1.0.3)
117 | faraday_middleware (1.2.0)
118 | faraday (~> 1.0)
119 | fastimage (2.2.6)
120 | fastlane (2.207.0)
121 | CFPropertyList (>= 2.3, < 4.0.0)
122 | addressable (>= 2.8, < 3.0.0)
123 | artifactory (~> 3.0)
124 | aws-sdk-s3 (~> 1.0)
125 | babosa (>= 1.0.3, < 2.0.0)
126 | bundler (>= 1.12.0, < 3.0.0)
127 | colored
128 | commander (~> 4.6)
129 | dotenv (>= 2.1.1, < 3.0.0)
130 | emoji_regex (>= 0.1, < 4.0)
131 | excon (>= 0.71.0, < 1.0.0)
132 | faraday (~> 1.0)
133 | faraday-cookie_jar (~> 0.0.6)
134 | faraday_middleware (~> 1.0)
135 | fastimage (>= 2.1.0, < 3.0.0)
136 | gh_inspector (>= 1.1.2, < 2.0.0)
137 | google-apis-androidpublisher_v3 (~> 0.3)
138 | google-apis-playcustomapp_v1 (~> 0.1)
139 | google-cloud-storage (~> 1.31)
140 | highline (~> 2.0)
141 | json (< 3.0.0)
142 | jwt (>= 2.1.0, < 3)
143 | mini_magick (>= 4.9.4, < 5.0.0)
144 | multipart-post (~> 2.0.0)
145 | naturally (~> 2.2)
146 | optparse (~> 0.1.1)
147 | plist (>= 3.1.0, < 4.0.0)
148 | rubyzip (>= 2.0.0, < 3.0.0)
149 | security (= 0.1.3)
150 | simctl (~> 1.6.3)
151 | terminal-notifier (>= 2.0.0, < 3.0.0)
152 | terminal-table (>= 1.4.5, < 2.0.0)
153 | tty-screen (>= 0.6.3, < 1.0.0)
154 | tty-spinner (>= 0.8.0, < 1.0.0)
155 | word_wrap (~> 1.0.0)
156 | xcodeproj (>= 1.13.0, < 2.0.0)
157 | xcpretty (~> 0.3.0)
158 | xcpretty-travis-formatter (>= 0.0.3)
159 | ffi (1.15.5)
160 | fourflusher (2.3.1)
161 | fuzzy_match (2.0.4)
162 | gh_inspector (1.1.3)
163 | google-apis-androidpublisher_v3 (0.23.0)
164 | google-apis-core (>= 0.6, < 2.a)
165 | google-apis-core (0.7.0)
166 | addressable (~> 2.5, >= 2.5.1)
167 | googleauth (>= 0.16.2, < 2.a)
168 | httpclient (>= 2.8.1, < 3.a)
169 | mini_mime (~> 1.0)
170 | representable (~> 3.0)
171 | retriable (>= 2.0, < 4.a)
172 | rexml
173 | webrick
174 | google-apis-iamcredentials_v1 (0.12.0)
175 | google-apis-core (>= 0.6, < 2.a)
176 | google-apis-playcustomapp_v1 (0.9.0)
177 | google-apis-core (>= 0.6, < 2.a)
178 | google-apis-storage_v1 (0.16.0)
179 | google-apis-core (>= 0.6, < 2.a)
180 | google-cloud-core (1.6.0)
181 | google-cloud-env (~> 1.0)
182 | google-cloud-errors (~> 1.0)
183 | google-cloud-env (1.6.0)
184 | faraday (>= 0.17.3, < 3.0)
185 | google-cloud-errors (1.2.0)
186 | google-cloud-storage (1.37.0)
187 | addressable (~> 2.8)
188 | digest-crc (~> 0.4)
189 | google-apis-iamcredentials_v1 (~> 0.1)
190 | google-apis-storage_v1 (~> 0.1)
191 | google-cloud-core (~> 1.6)
192 | googleauth (>= 0.16.2, < 2.a)
193 | mini_mime (~> 1.0)
194 | googleauth (1.2.0)
195 | faraday (>= 0.17.3, < 3.a)
196 | jwt (>= 1.4, < 3.0)
197 | memoist (~> 0.16)
198 | multi_json (~> 1.11)
199 | os (>= 0.9, < 2.0)
200 | signet (>= 0.16, < 2.a)
201 | highline (2.0.3)
202 | http-cookie (1.0.5)
203 | domain_name (~> 0.5)
204 | httpclient (2.8.3)
205 | i18n (1.12.0)
206 | concurrent-ruby (~> 1.0)
207 | jmespath (1.6.1)
208 | json (2.6.2)
209 | jwt (2.4.1)
210 | memoist (0.16.2)
211 | mini_magick (4.11.0)
212 | mini_mime (1.1.2)
213 | mini_portile2 (2.8.1)
214 | minitest (5.18.0)
215 | molinillo (0.8.0)
216 | multi_json (1.15.0)
217 | multipart-post (2.0.0)
218 | nanaimo (0.3.0)
219 | nap (1.1.0)
220 | naturally (2.2.1)
221 | netrc (0.11.0)
222 | nokogiri (1.14.3)
223 | mini_portile2 (~> 2.8.0)
224 | racc (~> 1.4)
225 | optparse (0.1.1)
226 | os (1.1.4)
227 | plist (3.6.0)
228 | public_suffix (4.0.7)
229 | racc (1.6.2)
230 | rake (13.0.6)
231 | representable (3.2.0)
232 | declarative (< 0.1.0)
233 | trailblazer-option (>= 0.1.1, < 0.2.0)
234 | uber (< 0.2.0)
235 | retriable (3.1.2)
236 | rexml (3.2.5)
237 | rouge (2.0.7)
238 | ruby-macho (2.5.1)
239 | ruby2_keywords (0.0.5)
240 | rubyzip (2.3.2)
241 | security (0.1.3)
242 | signet (0.17.0)
243 | addressable (~> 2.8)
244 | faraday (>= 0.17.5, < 3.a)
245 | jwt (>= 1.5, < 3.0)
246 | multi_json (~> 1.10)
247 | simctl (1.6.8)
248 | CFPropertyList
249 | naturally
250 | slather (2.7.2)
251 | CFPropertyList (>= 2.2, < 4)
252 | activesupport
253 | clamp (~> 1.3)
254 | nokogiri (~> 1.12)
255 | xcodeproj (~> 1.21)
256 | terminal-notifier (2.0.0)
257 | terminal-table (1.8.0)
258 | unicode-display_width (~> 1.1, >= 1.1.1)
259 | trailblazer-option (0.1.2)
260 | tty-cursor (0.7.1)
261 | tty-screen (0.8.1)
262 | tty-spinner (0.9.3)
263 | tty-cursor (~> 0.7)
264 | typhoeus (1.4.0)
265 | ethon (>= 0.9.0)
266 | tzinfo (2.0.6)
267 | concurrent-ruby (~> 1.0)
268 | uber (0.1.0)
269 | unf (0.1.4)
270 | unf_ext
271 | unf_ext (0.0.8.2)
272 | unicode-display_width (1.8.0)
273 | webrick (1.7.0)
274 | word_wrap (1.0.0)
275 | xcodeproj (1.22.0)
276 | CFPropertyList (>= 2.3.3, < 4.0)
277 | atomos (~> 0.1.3)
278 | claide (>= 1.0.2, < 2.0)
279 | colored2 (~> 3.1)
280 | nanaimo (~> 0.3.0)
281 | rexml (~> 3.2.4)
282 | xcpretty (0.3.0)
283 | rouge (~> 2.0.7)
284 | xcpretty-travis-formatter (1.0.1)
285 | xcpretty (~> 0.2, >= 0.0.7)
286 | zeitwerk (2.6.7)
287 |
288 | PLATFORMS
289 | ruby
290 |
291 | DEPENDENCIES
292 | cocoapods (~> 1.11.0)
293 | fastlane (~> 2.207.0)
294 | slather (~> 2.7.0)
295 |
296 | BUNDLED WITH
297 | 1.17.2
298 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Pere Daniel Prieto
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/MathExpression.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'MathExpression'
3 | spec.version = '1.3.0'
4 | spec.summary = 'Framework to parse and evaluate arithmetic mathematical expressions given by a String'
5 | spec.description = 'This framework provides an algorithm and an API to easily evaluate arithmetic mathematical expressions given by a String. In addition to the basic arithmetic operators (addition, subtraction, product and division), we can pass in a transformation (in the form of a block) to add flexibility in the expressions provided.'
6 | spec.license = { :type => 'MIT', :file => 'LICENSE' }
7 | spec.homepage = 'https://github.com/peredaniel/MathExpression'
8 | spec.authors = { 'Pere Daniel Prieto' => 'math.pedro.daniel.prieto@gmail.com' }
9 | spec.source = { :git => 'https://github.com/peredaniel/MathExpression.git', :tag => spec.version }
10 |
11 | spec.platform = :ios, :tvos, :osx
12 | spec.ios.deployment_target = '12.0'
13 | spec.tvos.deployment_target = '12.0'
14 | spec.osx.deployment_target = '10.14'
15 |
16 | spec.swift_version = "5.0"
17 |
18 | spec.ios.source_files = ['MathExpression/**/*.{h,m,swift}']
19 | spec.tvos.source_files = ['MathExpression/**/*.{h,m,swift}']
20 | spec.osx.source_files = ['MathExpression/Source/**/*.{h,m,swift}']
21 | end
22 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/xcshareddata/xcbaselines/9F153DA0231E87A4005A6966.xcbaseline/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | runDestinationsByUUID
6 |
7 | 93FBB218-913E-4092-9842-36EF602C8886
8 |
9 | localComputer
10 |
11 | busSpeedInMHz
12 | 400
13 | cpuCount
14 | 1
15 | cpuKind
16 | 8-Core Intel Core i9
17 | cpuSpeedInMHz
18 | 2300
19 | logicalCPUCoresPerPackage
20 | 16
21 | modelCode
22 | MacBookPro16,1
23 | physicalCPUCoresPerPackage
24 | 8
25 | platformIdentifier
26 | com.apple.platform.macosx
27 |
28 | targetArchitecture
29 | x86_64
30 | targetDevice
31 |
32 | modelCode
33 | iPhone12,1
34 | platformIdentifier
35 | com.apple.platform.iphonesimulator
36 |
37 |
38 | A2BA1546-25D3-486D-8D64-9A8BE5867597
39 |
40 | localComputer
41 |
42 | busSpeedInMHz
43 | 100
44 | cpuCount
45 | 1
46 | cpuKind
47 | Intel Core i5
48 | cpuSpeedInMHz
49 | 2500
50 | logicalCPUCoresPerPackage
51 | 4
52 | modelCode
53 | MacBookPro9,2
54 | physicalCPUCoresPerPackage
55 | 2
56 | platformIdentifier
57 | com.apple.platform.macosx
58 |
59 | targetArchitecture
60 | x86_64
61 | targetDevice
62 |
63 | modelCode
64 | iPhone10,4
65 | platformIdentifier
66 | com.apple.platform.iphonesimulator
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/xcshareddata/xcschemes/MathExpression-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
38 |
39 |
40 |
41 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
64 |
70 |
71 |
72 |
73 |
79 |
80 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/xcshareddata/xcschemes/MathExpression-macOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
53 |
59 |
60 |
61 |
62 |
68 |
69 |
75 |
76 |
77 |
78 |
80 |
81 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/xcshareddata/xcschemes/MathExpression-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
55 |
61 |
62 |
63 |
64 |
65 |
66 |
72 |
73 |
79 |
80 |
81 |
82 |
84 |
85 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/xcshareddata/xcschemes/MathExpressionExample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/xcshareddata/xcschemes/PerformanceTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
54 |
55 |
61 |
62 |
63 |
64 |
70 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/xcuserdata/peredaniel.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/MathExpression.xcodeproj/xcuserdata/peredaniel.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MathExpression-iOS.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | MathExpression-macOS.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 1
16 |
17 | MathExpression-tvOS.xcscheme_^#shared#^_
18 |
19 | orderHint
20 | 2
21 |
22 | MathExpressionExample.xcscheme_^#shared#^_
23 |
24 | orderHint
25 | 3
26 |
27 | PerformanceTests.xcscheme_^#shared#^_
28 |
29 | orderHint
30 | 4
31 |
32 |
33 | SuppressBuildableAutocreation
34 |
35 | 9F153DA0231E87A4005A6966
36 |
37 | primary
38 |
39 |
40 | 9F1A2CE023166BAF009CEB51
41 |
42 | primary
43 |
44 |
45 | 9F1A2CE923166BAF009CEB51
46 |
47 | primary
48 |
49 |
50 | 9FA74AA02327D2630049E401
51 |
52 | primary
53 |
54 |
55 | 9FF96D9E2321399200FC90CA
56 |
57 | primary
58 |
59 |
60 | 9FF96DB023214AAF00FC90CA
61 |
62 | primary
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/MathExpression/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MathExpression/MathExpression.h:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | #import
4 |
5 | //! Project version number for MathExpression.
6 | FOUNDATION_EXPORT double MathExpressionVersionNumber;
7 |
8 | //! Project version string for MathExpression.
9 | FOUNDATION_EXPORT const unsigned char MathExpressionVersionString[];
10 |
11 | // In this header, you should import all the public headers of your framework using statements like #import
12 |
13 |
14 |
--------------------------------------------------------------------------------
/MathExpression/Source/Extensions/FloatingPoint+Extensions.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import Foundation
4 |
5 | extension FloatingPoint {
6 | var negative: Self {
7 | var negativeValue = self
8 | negativeValue.negate()
9 | return negativeValue
10 | }
11 |
12 | func avoidScientificNotation() -> String {
13 | Formatter.avoidScientificNotation.string(for: self) ?? ""
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/MathExpression/Source/Extensions/Formatter+Extensions.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import Foundation
4 |
5 | extension Formatter {
6 | static let avoidScientificNotation: NumberFormatter = {
7 | let numberFormatter = NumberFormatter()
8 | numberFormatter.maximumFractionDigits = 16
9 | numberFormatter.numberStyle = .decimal
10 | numberFormatter.decimalSeparator = "."
11 | numberFormatter.usesGroupingSeparator = false
12 | return numberFormatter
13 | }()
14 | }
15 |
--------------------------------------------------------------------------------
/MathExpression/Source/Models/MathBrackets.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | enum MathBrackets: CaseIterable {
4 | case parenthesis
5 |
6 | var opening: String {
7 | switch self {
8 | case .parenthesis: return "("
9 | }
10 | }
11 |
12 | var closing: String {
13 | switch self {
14 | case .parenthesis: return ")"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MathExpression/Source/Models/MathOperator.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import Foundation
4 |
5 | enum AdditiveOperator: String, CaseIterable {
6 | case sum = "+"
7 | case subtraction = "-"
8 |
9 | var character: Character {
10 | Character(rawValue)
11 | }
12 | }
13 |
14 | enum MathOperator: String {
15 | case negative = "_"
16 | case product = "*"
17 | case division = "/"
18 | case sum = "+"
19 | case subtraction = "-"
20 |
21 | static var evaluationCases: [MathOperator] {
22 | [.sum, .subtraction, .product, .division, .negative]
23 | }
24 |
25 | static var multiplicativeOperators: [MathOperator] {
26 | [.product, .division]
27 | }
28 |
29 | static var validationCases: [MathOperator] {
30 | [.product, .division, .sum, .subtraction]
31 | }
32 | }
33 |
34 | // MARK: - Static variables
35 |
36 | extension MathOperator {
37 | static var validConsecutiveOperators: [String: String] {
38 | [
39 | MathOperator.sum.rawValue + MathOperator.subtraction.rawValue: MathOperator.subtraction.rawValue,
40 | MathOperator.subtraction.rawValue + MathOperator.sum.rawValue: MathOperator.subtraction.rawValue,
41 | MathOperator.sum.rawValue + MathOperator.sum.rawValue: MathOperator.sum.rawValue,
42 | MathOperator.subtraction.rawValue + MathOperator.subtraction.rawValue: MathOperator.sum.rawValue
43 | ]
44 | }
45 |
46 | static var validConsecutiveOperatorsDuringEvaluation: [String: String] {
47 | [
48 | MathOperator.negative.rawValue + MathOperator.sum.rawValue: MathOperator.negative.rawValue,
49 | MathOperator.negative.rawValue + MathOperator.subtraction.rawValue: MathOperator.sum.rawValue,
50 | MathOperator.sum.rawValue + MathOperator.subtraction.rawValue: MathOperator.sum.rawValue + MathOperator.negative.rawValue,
51 | MathOperator.product.rawValue + MathOperator.subtraction.rawValue: MathOperator.product.rawValue + MathOperator.negative.rawValue,
52 | MathOperator.division.rawValue + MathOperator.subtraction.rawValue: MathOperator.division.rawValue + MathOperator.negative.rawValue
53 | ]
54 | }
55 |
56 | static var invalidConsecutiveOperators: [String] {
57 | [
58 | MathOperator.sum.rawValue + MathOperator.product.rawValue,
59 | MathOperator.sum.rawValue + MathOperator.division.rawValue,
60 | MathOperator.subtraction.rawValue + MathOperator.product.rawValue,
61 | MathOperator.subtraction.rawValue + MathOperator.division.rawValue,
62 | MathOperator.product.rawValue + MathOperator.product.rawValue,
63 | MathOperator.product.rawValue + MathOperator.division.rawValue,
64 | MathOperator.division.rawValue + MathOperator.product.rawValue,
65 | MathOperator.division.rawValue + MathOperator.division.rawValue
66 | ]
67 | }
68 | }
69 |
70 | // MARK: - Computed variables
71 |
72 | extension MathOperator {
73 | var character: Character {
74 | Character(rawValue)
75 | }
76 |
77 | func apply(to args: [Double]) -> Double {
78 | switch self {
79 | case .sum: return args.sum()
80 | case .subtraction: return args.subtract()
81 | case .product: return args.multiply()
82 | case .division: return args.divide()
83 | case .negative: return args.first?.negative ?? .zero
84 | }
85 | }
86 | }
87 |
88 | // MARK: - Helper extensions
89 |
90 | private extension Array where Element == Double {
91 | func sum() -> Element {
92 | guard let last = last else { return .zero }
93 | return last + dropLast().sum()
94 | }
95 |
96 | func subtract() -> Double {
97 | guard let first = first else { return .zero }
98 | return first + reversed().dropLast().map { $0.negative }.sum()
99 | }
100 |
101 | func multiply() -> Double {
102 | guard !contains(.zero) else { return .zero }
103 | guard let last = last else { return 1.0 }
104 | return last * dropLast().multiply()
105 | }
106 |
107 | func divide() -> Double {
108 | guard let numerator = first else { return 1.0 }
109 | return numerator / reversed().dropLast().multiply()
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/MathExpression/Source/Parser/MathExpression.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import Foundation
4 |
5 | public struct MathExpression {
6 | public enum ValidationError: Error {
7 | case emptyExpression
8 | case misplacedBrackets
9 | case unevenOpeningClosingBracketNumber
10 | case invalidConsecutiveOperators(String)
11 | case startsWithNonSumOrSubtractionOperator(String)
12 | case endsWithOperator(String)
13 | }
14 |
15 | private let formula: MathFormula
16 |
17 | public init(
18 | _ string: String,
19 | transformation: @escaping (String) -> Double = { Double($0) ?? .zero }
20 | ) throws {
21 | formula = try MathFormula(string, transformation: transformation)
22 | }
23 |
24 | internal init(_ formula: MathFormula) {
25 | self.formula = formula
26 | }
27 |
28 | public func evaluate() -> Double {
29 | switch formula.evaluationState() {
30 | case .isNumeric(let value):
31 | return value
32 | case .startsWithSymbol(let symbol):
33 | switch symbol {
34 | case .sum:
35 | return MathExpression(formula.dropingInitialValue()).evaluate()
36 | case .subtraction:
37 | return MathExpression(formula.replaceSubtractionByNegative()).evaluate()
38 | }
39 | case .containsBracket(let brackets):
40 | return MathExpression(formula.evaluatingExpression(between: brackets)).evaluate()
41 | case .canApplyOperator(let mathOperator):
42 | return mathOperator.apply(
43 | to: formula.decompose(with: mathOperator).map { MathExpression($0) }.evaluate()
44 | )
45 | case .canApplyTransformation:
46 | return formula.applyTransformation()
47 | }
48 | }
49 | }
50 |
51 | private extension Array where Element == MathExpression {
52 | func evaluate() -> [Double] {
53 | map { $0.evaluate() }
54 | }
55 | }
56 |
57 | extension MathExpression.ValidationError: Equatable {
58 | public static func == (lhs: MathExpression.ValidationError, rhs: MathExpression.ValidationError) -> Bool {
59 | switch (lhs, rhs) {
60 | case (.emptyExpression, .emptyExpression),
61 | (.misplacedBrackets, .misplacedBrackets),
62 | (.unevenOpeningClosingBracketNumber, .unevenOpeningClosingBracketNumber):
63 | return true
64 | case (.invalidConsecutiveOperators(let lhsValue), .invalidConsecutiveOperators(let rhsValue)),
65 | (.startsWithNonSumOrSubtractionOperator(let lhsValue), .startsWithNonSumOrSubtractionOperator(let rhsValue)),
66 | (.endsWithOperator(let lhsValue), .endsWithOperator(let rhsValue)):
67 | return lhsValue == rhsValue
68 | default:
69 | return false
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/MathExpression/Source/Parser/MathFormula.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | struct MathFormula {
4 | enum EvaluationState {
5 | case isNumeric(Double)
6 | case startsWithSymbol(AdditiveOperator)
7 | case containsBracket(MathBrackets)
8 | case canApplyOperator(MathOperator)
9 | case canApplyTransformation
10 | }
11 |
12 | private let string: String
13 | private let transformation: (String) -> Double
14 |
15 | init(
16 | _ string: String,
17 | transformation: @escaping (String) -> Double = { Double($0) ?? .zero }
18 | ) throws {
19 | self.string = string.trimmingWhiteSpacesAndDoubleMathOperators()
20 | self.transformation = transformation
21 | try validate()
22 | }
23 |
24 | init(
25 | validString string: String,
26 | transformation: @escaping (String) -> Double
27 | ) {
28 | self.string = string.trimmingWhiteSpacesAndDoubleMathOperators()
29 | self.transformation = transformation
30 | }
31 | }
32 |
33 | // MARK: - Computed properties
34 |
35 | extension MathFormula {
36 | var asDouble: Double? {
37 | Double(string)
38 | }
39 |
40 | func evaluationState(validating: Bool = false) -> EvaluationState {
41 | if let value = asDouble {
42 | return .isNumeric(value)
43 | }
44 |
45 | for additiveOperator in AdditiveOperator.allCases {
46 | if starts(with: additiveOperator) {
47 | return .startsWithSymbol(additiveOperator)
48 | }
49 | }
50 |
51 | for brackets in MathBrackets.allCases {
52 | if containsBracket(brackets) {
53 | return .containsBracket(brackets)
54 | }
55 | }
56 |
57 | if let firstOperator = getFirstOperator(validating: validating) {
58 | return .canApplyOperator(firstOperator)
59 | }
60 |
61 | return .canApplyTransformation
62 | }
63 |
64 | func getFirstOperator(validating: Bool = false) -> MathOperator? {
65 | let cases = validating ? MathOperator.validationCases : MathOperator.evaluationCases
66 | return cases.first { string.contains($0.rawValue) }
67 | }
68 | }
69 |
70 | // MARK: - Functions
71 |
72 | extension MathFormula {
73 | func applyTransformation() -> Double {
74 | transformation(string)
75 | }
76 |
77 | func decompose(with mathOperator: MathOperator) -> [MathFormula] {
78 | var finalString = string
79 | if let _ = MathOperator.validConsecutiveOperatorsDuringEvaluation.first(where: { string.contains($0.key) }) {
80 | for (doubleOperator, combinedOperator) in MathOperator.validConsecutiveOperatorsDuringEvaluation {
81 | finalString = finalString.replacingOccurrences(of: doubleOperator, with: combinedOperator)
82 | }
83 | }
84 | return finalString.split(separator: mathOperator.character).map {
85 | MathFormula(validString: String($0), transformation: transformation)
86 | }
87 | }
88 |
89 | func dropingInitialValue() -> MathFormula {
90 | MathFormula(
91 | validString: String(string.dropFirst()),
92 | transformation: transformation
93 | )
94 | }
95 |
96 | func evaluatingExpression(between bracket: MathBrackets) -> MathFormula {
97 | let stringWithBrackets = (try? getString(between: bracket)) ?? ""
98 |
99 | let value = MathExpression(
100 | MathFormula(
101 | validString: String(stringWithBrackets.dropFirst().dropLast()),
102 | transformation: transformation
103 | )
104 | ).evaluate()
105 | return MathFormula(
106 | validString: string.replacingOccurrences(of: stringWithBrackets, with: value.avoidScientificNotation()),
107 | transformation: transformation
108 | )
109 | }
110 |
111 | func replaceSubtractionByNegative() -> MathFormula {
112 | return MathFormula(
113 | validString: MathOperator.negative.rawValue + String(string.dropFirst()),
114 | transformation: transformation
115 | )
116 | }
117 | }
118 |
119 | // MARK: - Private API
120 |
121 | private extension MathFormula {
122 | func containsBracket(_ bracket: MathBrackets) -> Bool {
123 | string.contains(bracket.opening) || string.contains(bracket.closing)
124 | }
125 |
126 | func getString(between bracket: MathBrackets) throws -> String {
127 | try String(string.map { $0 }.characters(between: bracket))
128 | }
129 |
130 | func starts(with additiveOperator: AdditiveOperator) -> Bool {
131 | string.first == additiveOperator.character
132 | }
133 |
134 | func validate() throws {
135 | guard !string.isEmpty else {
136 | throw MathExpression.ValidationError.emptyExpression
137 | }
138 |
139 | try string.validateStartingAndEndingCharacters()
140 | try string.validateNoInvalidConsecutiveOperators()
141 |
142 | switch evaluationState(validating: true) {
143 | case .isNumeric:
144 | break
145 | case .startsWithSymbol(let symbol):
146 | switch symbol {
147 | case .sum:
148 | _ = try MathFormula(String(string.dropFirst()), transformation: transformation)
149 | case .subtraction:
150 | _ = try MathFormula(MathOperator.negative.rawValue + String(string.dropFirst()), transformation: transformation)
151 | }
152 | case .containsBracket(let brackets):
153 | let stringWithBrackets = try getString(between: brackets)
154 | _ = try MathFormula(
155 | String(stringWithBrackets.dropFirst().dropLast()),
156 | transformation: transformation
157 | )
158 | _ = try MathFormula(
159 | string.replacingOccurrences(of: stringWithBrackets, with: "0"),
160 | transformation: transformation
161 | )
162 |
163 | case .canApplyOperator(let mathOperator):
164 | _ = try string.split(separator: mathOperator.character).map {
165 | try MathFormula(String($0), transformation: transformation)
166 | }
167 | case .canApplyTransformation:
168 | break
169 | }
170 | }
171 | }
172 |
173 | // MARK: - Helper extensions
174 |
175 | private extension String {
176 | func trimmingWhiteSpacesAndDoubleMathOperators() -> String {
177 | var finalString = replacingOccurrences(of: " ", with: "")
178 | for (doubleOperator, combinedOperator) in MathOperator.validConsecutiveOperators {
179 | finalString = finalString.replacingOccurrences(of: doubleOperator, with: combinedOperator)
180 | }
181 | return finalString
182 | }
183 |
184 | func validateNoInvalidConsecutiveOperators() throws {
185 | for consecutiveOperators in MathOperator.invalidConsecutiveOperators {
186 | if let _ = range(of: consecutiveOperators) {
187 | throw MathExpression.ValidationError.invalidConsecutiveOperators(consecutiveOperators)
188 | }
189 | }
190 | }
191 |
192 | func validateStartingAndEndingCharacters() throws {
193 | guard let first = first, let last = last else { return }
194 | if MathOperator.multiplicativeOperators.map({ $0.rawValue }).contains(String(first)) {
195 | throw MathExpression.ValidationError.startsWithNonSumOrSubtractionOperator(String(first))
196 | }
197 |
198 | if MathOperator.validationCases.map({ $0.rawValue }).contains(String(last)) {
199 | throw MathExpression.ValidationError.endsWithOperator(String(last))
200 | }
201 | }
202 | }
203 |
204 | private extension Array where Element == String.Element {
205 | func characters(between brackets: MathBrackets) throws -> [Character] {
206 | guard let initialIndex = lastIndex(of: Character(brackets.opening)) else {
207 | throw MathExpression.ValidationError.unevenOpeningClosingBracketNumber
208 | }
209 |
210 | guard let finalIndex = self[initialIndex.. Int {
5 | guard self > 1 else { return 1 }
6 | return self * (self - 1).factorial()
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/MathExpressionExample/Models/MathTransformation.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | enum MathTransformation: CaseIterable {
4 | case numericValueOrZero
5 | case count
6 | case factorial
7 |
8 | var function: (String) -> Double {
9 | switch self {
10 | case .count:
11 | return { Double($0.count) }
12 | case .factorial:
13 | return {
14 | guard $0.last == "!", let number = Int($0.dropLast()) else { return .zero }
15 | return Double(number.factorial())
16 | }
17 | case .numericValueOrZero:
18 | return { Double($0) ?? .zero }
19 | }
20 | }
21 |
22 | var name: String {
23 | switch self {
24 | case .count: return "String count"
25 | case .factorial: return "Factorial (integers only)"
26 | case .numericValueOrZero: return "Numeric value or zero"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/MathExpressionExample/Models/ValidationError.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | struct ValidationError: Error {
4 | let description: String
5 |
6 | init(_ description: String) {
7 | self.description = description
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/MathExpressionExample/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/MathExpressionExample/Screens/Calculator/CalculatorView.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import SwiftUI
4 |
5 | struct CalculatorView: View {
6 | @StateObject var viewModel: CalculatorViewModel = .init()
7 |
8 | var body: some View {
9 | VStack(alignment: .trailing, spacing: 16) {
10 | Text(viewModel.expression)
11 | .font(.headline)
12 | .padding(.top, 24)
13 | Text(viewModel.operationResult)
14 | .font(.largeTitle)
15 | Spacer()
16 | CalculatorKeyboardView(
17 | clearButtonTitle: $viewModel.clearButtonTitle,
18 | lastKeyTapped: $viewModel.lastKeyTapped
19 | )
20 | }
21 | .padding(.horizontal)
22 | .padding(.vertical, 24)
23 | .navigationTitle("Calculator")
24 | .navigationBarTitleDisplayMode(.inline)
25 | .alert("Validation error", isPresented: $viewModel.shouldShowError) {
26 | Button("Ok") {
27 | viewModel.reset()
28 | }
29 | } message: {
30 | Text(viewModel.operationError?.description ?? "")
31 | }
32 | }
33 | }
34 |
35 | struct CalculatorView_Previews: PreviewProvider {
36 | static var previews: some View {
37 | CalculatorView()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/MathExpressionExample/Screens/Calculator/CalculatorViewModel.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import Foundation
4 | import MathExpression
5 |
6 | class CalculatorViewModel: ObservableObject {
7 | @Published var operationResult: String = "0"
8 | @Published var shouldShowError: Bool = false
9 | @Published var expression: String = ""
10 | @Published var clearButtonTitle: String = "AC"
11 | @Published var lastKeyTapped: String? {
12 | didSet { parseLastKeyTapped(lastKeyTapped) }
13 | }
14 |
15 | private(set) var operationError: ValidationError?
16 | private var shouldClearNewCharactersOnNextTap: Bool = true
17 |
18 | func reset() {
19 | shouldShowError = false
20 | expression = ""
21 | operationResult = "0"
22 | operationError = nil
23 | clearButtonTitle = "AC"
24 | shouldClearNewCharactersOnNextTap = true
25 | lastKeyTapped = nil
26 | }
27 | }
28 |
29 | private extension CalculatorViewModel {
30 | func parseLastKeyTapped(_ value: String?) {
31 | switch value {
32 | case "=":
33 | if !expression.isEmpty, expression.last != ")" {
34 | updateExpression(operationResult)
35 | }
36 | evaluateExpression()
37 | case "+/-":
38 | guard !operationResult.isEmpty, operationResult != "0" else { return }
39 | if operationResult.starts(with: "-") {
40 | operationResult = String(operationResult.dropFirst())
41 | } else {
42 | operationResult = "-\(operationResult)"
43 | }
44 | case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9":
45 | if operationResult == "0" || shouldClearNewCharactersOnNextTap {
46 | operationResult = value ?? ""
47 | clearButtonTitle = "C"
48 | shouldClearNewCharactersOnNextTap = false
49 | } else if expression.last == "=" {
50 | expression = ""
51 | } else {
52 | operationResult += (value ?? "")
53 | }
54 | case "+", "-", "*", "/":
55 | if shouldClearNewCharactersOnNextTap {
56 | expression = ""
57 | }
58 | if expression.last != ")", operationResult != "0" {
59 | updateExpression(operationResult)
60 | }
61 | updateExpression(value)
62 | shouldClearNewCharactersOnNextTap = true
63 | case "(", ")":
64 | if value == ")" {
65 | updateExpression(operationResult)
66 | } else {
67 | shouldClearNewCharactersOnNextTap = true
68 | }
69 | updateExpression(value)
70 | case "AC":
71 | operationResult = "0"
72 | expression = ""
73 | case "C":
74 | operationResult = "0"
75 | clearButtonTitle = "AC"
76 | case ".":
77 | operationResult += "."
78 | default:
79 | break
80 | }
81 | }
82 |
83 | func updateExpression(_ newCharacters: String?) {
84 | guard let newCharacters = newCharacters else { return }
85 | if !expression.isEmpty, expression.last != "(", newCharacters != ")" {
86 | expression += " \(newCharacters)"
87 | } else {
88 | expression += newCharacters
89 | }
90 | }
91 |
92 | func evaluateExpression() {
93 | do {
94 | let mathExpression = try MathExpression(expression, transformation: MathTransformation.numericValueOrZero.function)
95 | let result = Formatter.mathExpression.string(from: NSNumber(value: mathExpression.evaluate())) ?? ""
96 | operationResult = result
97 | updateExpression("=")
98 | shouldClearNewCharactersOnNextTap = true
99 | clearButtonTitle = "AC"
100 | } catch {
101 | guard let error = error as? MathExpression.ValidationError else {
102 | fatalError("[CalculatorViewModel] Expression \(expression) returned unknown error during validation!")
103 | }
104 | switch error {
105 | case .emptyExpression:
106 | operationError = ValidationError("The expression is empty!")
107 | case .misplacedBrackets:
108 | operationError = ValidationError("There is a bracket out of place.")
109 | case .unevenOpeningClosingBracketNumber:
110 | operationError = ValidationError("The number of opening and closing brackets is uneven!")
111 | case .invalidConsecutiveOperators(let value):
112 | operationError = ValidationError("The combination of operators \(value) is not valid.")
113 | case .startsWithNonSumOrSubtractionOperator(let value):
114 | operationError = ValidationError("The expression can not start with the \(value) operator.")
115 | case .endsWithOperator(let value):
116 | operationError = ValidationError("The expression can not end with the \(value) operator.")
117 | }
118 | shouldShowError = true
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/MathExpressionExample/Screens/Calculator/Supporting views/CalculatorKeyboardView.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import SwiftUI
4 |
5 | struct CalculatorKeyboardView: View {
6 | @Binding var clearButtonTitle: String
7 | @Binding var lastKeyTapped: String?
8 |
9 | private var data: [String] {
10 | [
11 | clearButtonTitle, "(", ")", "/",
12 | "7", "8", "9", "*",
13 | "4", "5", "6", "-",
14 | "1", "2", "3", "+",
15 | "0", ".", "+/-", "="
16 | ]
17 | }
18 |
19 | private let columns: [GridItem] = [
20 | .init(.flexible()),
21 | .init(.flexible()),
22 | .init(.flexible()),
23 | .init(.flexible())
24 | ]
25 |
26 | var body: some View {
27 | LazyVGrid(columns: columns, spacing: 48) {
28 | ForEach(data, id: \.self) { item in
29 | Button {
30 | lastKeyTapped = item
31 | } label: {
32 | Text(item)
33 | .font(.title)
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
40 | struct CalculatorKeyboardView_Previews: PreviewProvider {
41 | static var previews: some View {
42 | CalculatorKeyboardView(clearButtonTitle: .constant("C"), lastKeyTapped: .constant(""))
43 | .padding(.horizontal, 16)
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/MathExpressionExample/Screens/Evaluator/EvaluatorView.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import SwiftUI
4 |
5 | struct EvaluatorView: View {
6 | @StateObject var viewModel: EvaluatorViewModel = .init()
7 |
8 | var body: some View {
9 | VStack(alignment: .leading, spacing: 20) {
10 | VStack(alignment: .leading, spacing: 12) {
11 | Text("Expression to evaluate")
12 | .font(.subheadline)
13 | ZStack(alignment: .trailing) {
14 | TextField("Type in a mathematical expression", text: $viewModel.expression)
15 | .textFieldStyle(.roundedBorder)
16 | .multilineTextAlignment(.leading)
17 | Button {
18 | viewModel.expression = ""
19 | } label: {
20 | Image(systemName: "xmark.circle")
21 | }
22 | .padding(.trailing, 4)
23 | .foregroundColor(.gray)
24 |
25 | }
26 | }
27 | Divider()
28 | VStack(alignment: .leading, spacing: 12) {
29 | Text("Transformation to apply")
30 | Picker("", selection: $viewModel.transformation) {
31 | ForEach(MathTransformation.allCases, id: \.name) { transformation in
32 | Text(transformation.name)
33 | .tag(transformation)
34 | }
35 | }
36 | .pickerStyle(.menu)
37 | }
38 | Divider()
39 | if !viewModel.operationResult.isEmpty {
40 | Text(viewModel.operationResult)
41 | }
42 | HStack {
43 | Spacer()
44 | Button("Evaluate expression!") {
45 | viewModel.evaluateExpression()
46 | }
47 | Spacer()
48 | }
49 | Spacer()
50 | }
51 | .navigationTitle("Evaluator")
52 | .navigationBarTitleDisplayMode(.inline)
53 | .padding()
54 | .alert("Validation error", isPresented: $viewModel.shouldShowError) {
55 | Button("Ok") {
56 | viewModel.shouldShowError = false
57 | }
58 | } message: {
59 | Text(viewModel.operationError?.description ?? "")
60 | }
61 | }
62 | }
63 |
64 | struct EvaluatorView_Previews: PreviewProvider {
65 | static var previews: some View {
66 | EvaluatorView()
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/MathExpressionExample/Screens/Evaluator/EvaluatorViewModel.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import Foundation
4 | import MathExpression
5 |
6 | class EvaluatorViewModel: ObservableObject {
7 | @Published var operationResult: String = ""
8 | @Published var shouldShowError: Bool = false
9 |
10 | @Published var expression: String = "" {
11 | didSet { operationResult = "" }
12 | }
13 | @Published var transformation: MathTransformation = .numericValueOrZero
14 |
15 | private(set) var operationError: ValidationError?
16 |
17 | func evaluateExpression() {
18 | do {
19 | let mathExpression = try MathExpression(expression, transformation: transformation.function)
20 | let result = Formatter.mathExpression.string(from: NSNumber(value: mathExpression.evaluate())) ?? ""
21 | operationResult = "The evaluation result is: \(result)"
22 | } catch {
23 | guard let error = error as? MathExpression.ValidationError else {
24 | fatalError("[EvaluatorViewModel] Expression \(expression) returned unknown error during validation!")
25 | }
26 | switch error {
27 | case .emptyExpression:
28 | operationError = ValidationError("The expression is empty!")
29 | case .misplacedBrackets:
30 | operationError = ValidationError("There is a bracket out of place.")
31 | case .unevenOpeningClosingBracketNumber:
32 | operationError = ValidationError("The number of opening and closing brackets is uneven!")
33 | case .invalidConsecutiveOperators(let value):
34 | operationError = ValidationError("The combination of operators \(value) is not valid.")
35 | case .startsWithNonSumOrSubtractionOperator(let value):
36 | operationError = ValidationError("The expression can not start with the \(value) operator.")
37 | case .endsWithOperator(let value):
38 | operationError = ValidationError("The expression can not end with the \(value) operator.")
39 | }
40 | shouldShowError = true
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/MathExpressionExample/Screens/MenuView.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2022 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import SwiftUI
4 |
5 | struct MenuView: View {
6 | var body: some View {
7 | NavigationView {
8 | VStack(alignment: .leading, spacing: 20) {
9 | Text("The calculator is a simple calculator, similar to the built-in calculator app in iOS, enabling simple mathematical computations to be performed.")
10 | .font(.callout)
11 | .padding(.horizontal)
12 | .multilineTextAlignment(.leading)
13 | NavigationLink(destination: CalculatorView()) {
14 | HStack {
15 | Spacer()
16 | Text("Go to Calculator!")
17 | .foregroundColor(.accentColor)
18 | Spacer()
19 | }
20 | }
21 | Divider()
22 | .padding(.horizontal)
23 | Text("The evaluator enables to compute more complicated expressions, also giving the opportunity to use some pre-built transformations to convert plain String instances to numeric values.")
24 | .font(.callout)
25 | .padding(.horizontal)
26 | .multilineTextAlignment(.leading)
27 | NavigationLink(destination: EvaluatorView()) {
28 | HStack {
29 | Spacer()
30 | Text("Go to Evaluator!")
31 | .foregroundColor(.accentColor)
32 | Spacer()
33 | }
34 | }
35 | Spacer()
36 | }
37 | .padding(.top, 8)
38 | .navigationTitle("Example App")
39 | }
40 | }
41 | }
42 |
43 | struct MenuView_Previews: PreviewProvider {
44 | static var previews: some View {
45 | MenuView()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q - {0}, *) group tests/AbelianMultiplicativeGroupAxiomTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class AbelianMultiplicativeGroupAxiomPerformanceTests: XCTestCase {
7 | func testPerformanceNeutralElement() {
8 | let a = Int16.random()
9 | let neutralElement: Int16 = 1
10 |
11 | measure {
12 | let leftNeutralExpression = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.neutralElement, neutralElement, a))
13 | _ = leftNeutralExpression.evaluate()
14 |
15 | let rightNeutralExpression = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.neutralElement, a, neutralElement))
16 | _ = rightNeutralExpression.evaluate()
17 | }
18 | }
19 |
20 | func testPerformanceInverse() {
21 | let a = Int16.random()
22 |
23 | measure {
24 | let inverseExpression = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.inverseElement, a))
25 | _ = inverseExpression.evaluate()
26 |
27 | let leftProductWithInverse = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.leftInverse, a, a))
28 | _ = leftProductWithInverse.evaluate()
29 |
30 | let rightProductWithInverse = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.rightInverse, a, a))
31 | _ = rightProductWithInverse.evaluate()
32 | }
33 | }
34 |
35 | func testPerformanceAssociativity() {
36 | let a = Int16.random()
37 | let b = Int16.random()
38 | let c = Int16.random()
39 |
40 | measure {
41 | let leftAssociativityExpression = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.leftAssociativity, a, b, c))
42 | _ = leftAssociativityExpression.evaluate()
43 |
44 | let rightAssociativityExpression = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.rightAssociativity, a, b, c))
45 | _ = rightAssociativityExpression.evaluate()
46 | }
47 | }
48 |
49 | func testPerformanceCommutativity() {
50 | let a = Int16.random()
51 | let b = Int16.random()
52 |
53 | measure {
54 | let firstExpression = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.commutativity, a, b))
55 | _ = firstExpression.evaluate()
56 |
57 | let secondExpression = try! MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.commutativity, b, a))
58 | _ = secondExpression.evaluate()
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q - {0}, *) group tests/DivisionPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class DivisionPerformanceTests: XCTestCase {
7 | func testPerformanceSimpleDivision() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 |
11 | measure {
12 | let expression = try! MathExpression(String(format: Formula.DivisionTests.simpleDivision, a, b))
13 | _ = expression.evaluate()
14 | }
15 | }
16 |
17 | func testPerformanceSimpleDivisionWithParentheses() {
18 | let a = Int16.random()
19 | let b = Int16.random()
20 |
21 | measure {
22 | let expression = try! MathExpression(String(format: Formula.DivisionTests.simpleDivisionWithParentheses, a, b))
23 | _ = expression.evaluate()
24 | }
25 | }
26 |
27 | func testPerformanceSimpleDivisionWithParenthesesWithInitialDivision() {
28 | let a = Int16.random()
29 | let b = Int16.random()
30 |
31 | measure {
32 | let expression = try! MathExpression(String(format: Formula.DivisionTests.simpleDivisionWithParenthesesWithInitialDivision, a, b))
33 | _ = expression.evaluate()
34 | }
35 | }
36 |
37 | func testPerformanceComplicatedDivision() {
38 | let a = Int16.random()
39 | let b = Int16.random()
40 | let c = Int16.random()
41 |
42 | measure {
43 | let expression = try! MathExpression(String(format: Formula.DivisionTests.complicatedDivision, a, b, c))
44 | _ = expression.evaluate()
45 | }
46 | }
47 |
48 | func testPerformanceDivideSingleNumber() {
49 | let a = Int16.random()
50 |
51 | measure {
52 | let expression = try! MathExpression(String(format: Formula.DivisionTests.divideSingleNumber, a))
53 | _ = expression.evaluate()
54 | }
55 | }
56 |
57 | func testPerformanceConsecutiveDivisionExpressionWithInitialDivision() {
58 | let a = Int16.random()
59 | let b = Int16.random()
60 | let c = Int16.random()
61 |
62 | measure {
63 | let expression = try! MathExpression(String(format: Formula.DivisionTests.consecutiveDivisionExpressionWithInitialDivision, a, b, c))
64 | _ = expression.evaluate()
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q - {0}, *) group tests/ProductAndDivisionPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class ProductAndDivisionPerformanceTests: XCTestCase {
7 | func testPerformanceSimpleOperation() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 | let c = Int16.random()
11 |
12 | measure {
13 | let expression = try! MathExpression(String(format: Formula.ProductAndDivisionTests.simpleOperation, a, b, c))
14 | _ = expression.evaluate()
15 | }
16 | }
17 |
18 | func testPerformanceSimpleOperationWithParentheses() {
19 | let a = Int16.random()
20 | let b = Int16.random()
21 | let c = Int16.random()
22 |
23 | measure {
24 | let expression = try! MathExpression(String(format: Formula.ProductAndDivisionTests.simpleOperationWithParentheses, a, b, c))
25 | _ = expression.evaluate()
26 | }
27 | }
28 |
29 | func testPerformanceSimpleOperationWithParenthesesAndInitialDivision() {
30 | let a = Int16.random()
31 | let b = Int16.random()
32 | let c = Int16.random()
33 | let d = Int16.random()
34 |
35 | measure {
36 | let expression = try! MathExpression(String(format: Formula.ProductAndDivisionTests.simpleOperationWithParenthesesAndInitialDivision, a, b, c, d))
37 | _ = expression.evaluate()
38 | }
39 | }
40 |
41 | func testPerformanceComplicatedOperationWithParentheses() {
42 | let a = Int16.random()
43 | let b = Int16.random()
44 | let c = Int16.random()
45 | let d = Int16.random()
46 | let e = Int16.random()
47 | let f = Int16.random()
48 |
49 | measure {
50 | let expression = try! MathExpression(String(format: Formula.ProductAndDivisionTests.complicatedOperationWithParentheses, a, b, c, d, e, f))
51 | _ = expression.evaluate()
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q - {0}, *) group tests/ProductPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class ProductPerformanceTests: XCTestCase {
7 | func testPerformanceSimpleProduct() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 |
11 | measure {
12 | let expression = try! MathExpression(String(format: Formula.ProductTests.simpleProduct, a, b))
13 | _ = expression.evaluate()
14 | }
15 | }
16 |
17 | func testPerformanceComplicatedProduct() {
18 | let a = Int16.random()
19 | let b = Int16.random()
20 | let c = Int16.random()
21 | let d = Int16.random()
22 |
23 | measure {
24 | let expression = try! MathExpression(String(format: Formula.ProductTests.complicatedProduct, a, b, c, d))
25 | _ = expression.evaluate()
26 | }
27 | }
28 |
29 | func testPerformanceSimpleProductWithParentheses() {
30 | let a = Int16.random()
31 | let b = Int16.random()
32 |
33 | measure {
34 | let expression = try! MathExpression(String(format: Formula.ProductTests.simpleProductWithParentheses, a, b))
35 | _ = expression.evaluate()
36 | }
37 | }
38 |
39 | func testPerformanceComplicatedAdditionWithSingleParenthesis() {
40 | let a = Int16.random()
41 | let b = Int16.random()
42 | let c = Int16.random()
43 | let d = Int16.random()
44 |
45 | measure {
46 | let expression = try! MathExpression(String(format: Formula.ProductTests.complicatedProductWithSingleParenthesis, a, b, c, d))
47 | _ = expression.evaluate()
48 | }
49 | }
50 |
51 | func testPerformanceComplicatedProductWithMultipleParenthesis() {
52 | let a = Int16.random()
53 | let b = Int16.random()
54 | let c = Int16.random()
55 | let d = Int16.random()
56 |
57 | measure {
58 | let expression = try! MathExpression(String(format: Formula.ProductTests.complicatedProductWithMultipleParenthesis, a, b, c, d))
59 | _ = expression.evaluate()
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q, +) group tests/AbelianAdditiveGroupAxiomsPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class AbelianAdditiveGroupAxiomsPerformanceTests: XCTestCase {
7 | func testPerformanceNeutralElement() {
8 | let a = Int16.random()
9 | let neutralElement: Int16 = 0
10 |
11 | measure {
12 | let leftNeutralExpression = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.neutralElement, neutralElement, a))
13 | _ = leftNeutralExpression.evaluate()
14 |
15 | let rightNeutralExpression = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.neutralElement, a, neutralElement))
16 | _ = rightNeutralExpression.evaluate()
17 | }
18 | }
19 |
20 | func testPerformanceInverse() {
21 | let a = Int16.random()
22 |
23 | measure {
24 | let inverseExpression = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.inverseElement, a))
25 | _ = inverseExpression.evaluate()
26 |
27 | let leftProductWithInverse = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.leftInverse, a, a))
28 | _ = leftProductWithInverse.evaluate()
29 |
30 | let rightProductWithInverse = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.rightInverse, a, a))
31 | _ = rightProductWithInverse.evaluate()
32 | }
33 | }
34 |
35 | func testPerformanceAssociativity() {
36 | let a = Int16.random()
37 | let b = Int16.random()
38 | let c = Int16.random()
39 |
40 | measure {
41 | let leftAssociativityExpression = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.leftAssociativity, a, b, c))
42 | _ = leftAssociativityExpression.evaluate()
43 |
44 | let rightAssociativityExpression = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.rightAssociativity, a, b, c))
45 | _ = rightAssociativityExpression.evaluate()
46 | }
47 | }
48 |
49 | func testPerformanceCommutativity() {
50 | let a = Int16.random()
51 | let b = Int16.random()
52 |
53 | measure {
54 | let firstExpression = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.commutativity, a, b))
55 | _ = firstExpression.evaluate()
56 |
57 | let secondExpression = try! MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.commutativity, b, a))
58 | _ = secondExpression.evaluate()
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q, +) group tests/AdditionAndSubtractionPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class AdditionAndSubtractionPerformanceTests: XCTestCase {
7 | func testPerformanceSimpleOperation() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 | let c = Int16.random()
11 |
12 | measure {
13 | let expression = try! MathExpression(String(format: Formula.AdditionAndSubtractionTests.simpleOperation, a, b, c))
14 | _ = expression.evaluate()
15 | }
16 | }
17 |
18 | func testPerformanceSimpleOperationWithParentheses() {
19 | let a = Int16.random()
20 | let b = Int16.random()
21 | let c = Int16.random()
22 |
23 | measure {
24 | let expression = try! MathExpression(String(format: Formula.AdditionAndSubtractionTests.simpleOperationWithParentheses, a, b, c))
25 | _ = expression.evaluate()
26 | }
27 | }
28 |
29 | func testPerformanceSimpleOperationWithParenthesesAndInitialMinus() {
30 | let a = Int16.random()
31 | let b = Int16.random()
32 | let c = Int16.random()
33 | let d = Int16.random()
34 |
35 | measure {
36 | let expression = try! MathExpression(String(format: Formula.AdditionAndSubtractionTests.simpleOperationWithParenthesesAndInitialMinus, a, b, c, d))
37 | _ = expression.evaluate()
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q, +) group tests/AdditionPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class AdditionPerformanceTests: XCTestCase {
7 | func testPerformanceSimpleAddition() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 |
11 | measure {
12 | let expression = try! MathExpression(String(format: Formula.AdditionTests.simpleAddition, a, b))
13 | _ = expression.evaluate()
14 | }
15 | }
16 |
17 | func testPerformanceComplicatedAddition() {
18 | let a = Int16.random()
19 | let b = Int16.random()
20 | let c = Int16.random()
21 | let d = Int16.random()
22 |
23 | measure {
24 | let expression = try! MathExpression(String(format: Formula.AdditionTests.complicatedAddition, a, b, c, d))
25 | _ = expression.evaluate()
26 | }
27 | }
28 |
29 | func testPerformanceSimpleAdditionWithParentheses() {
30 | let a = Int16.random()
31 | let b = Int16.random()
32 |
33 | measure {
34 | let expression = try! MathExpression(String(format: Formula.AdditionTests.simpleAdditionWithParentheses, a, b))
35 | _ = expression.evaluate()
36 | }
37 | }
38 |
39 | func testPerformanceComplicatedAdditionWithSingleParenthesis() {
40 | let a = Int16.random()
41 | let b = Int16.random()
42 | let c = Int16.random()
43 | let d = Int16.random()
44 |
45 | measure {
46 | let expression = try! MathExpression(String(format: Formula.AdditionTests.complicatedAdditionWithSingleParenthesis, a, b, c, d))
47 | _ = expression.evaluate()
48 | }
49 | }
50 |
51 | func testPerformanceComplicatedAdditionWithMultipleParenthesis() {
52 | let a = Int16.random()
53 | let b = Int16.random()
54 | let c = Int16.random()
55 | let d = Int16.random()
56 | let e = Int16.random()
57 | let f = Int16.random()
58 |
59 | measure {
60 | let expression = try! MathExpression(String(format: Formula.AdditionTests.complicatedAdditionWithMultipleParenthesis, a, b, c, d, e, f))
61 | _ = expression.evaluate()
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q, +) group tests/SubtractionPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class SubtractionPerformanceTests: XCTestCase {
7 | func testPerformanceSimpleSubtraction() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 |
11 | measure {
12 | let expression = try! MathExpression(String(format: Formula.SubtractionTests.simpleSubtraction, a, b))
13 | _ = expression.evaluate()
14 | }
15 | }
16 |
17 | func testPerformanceSimpleSubtractionWithParentheses() {
18 | let a = Int16.random()
19 | let b = Int16.random()
20 |
21 | measure {
22 | let expression = try! MathExpression(String(format: Formula.SubtractionTests.simpleSubtractionWithParentheses, a, b))
23 | _ = expression.evaluate()
24 | }
25 | }
26 |
27 | func testPerformanceSimpleSubtractionWithParenthesesWithInitialMinus() {
28 | let a = Int16.random()
29 | let b = Int16.random()
30 |
31 | measure {
32 | let expression = try! MathExpression(String(format: Formula.SubtractionTests.simpleSubtractionWithParenthesesWithInitialMinus, a, b))
33 | _ = expression.evaluate()
34 | }
35 | }
36 |
37 | func testPerformanceComplicatedSubtraction() {
38 | let a = Int16.random()
39 | let b = Int16.random()
40 | let c = Int16.random()
41 |
42 | measure {
43 | let expression = try! MathExpression(String(format: Formula.SubtractionTests.complicatedSubtraction, a, b, c))
44 | _ = expression.evaluate()
45 | }
46 | }
47 |
48 | func testPerformanceNegativeSingleNumber() {
49 | let a = Int16.random()
50 |
51 | measure {
52 | let expression = try! MathExpression(String(format: Formula.SubtractionTests.negativeSingleNumber, a))
53 | _ = expression.evaluate()
54 | }
55 | }
56 |
57 | func testPerformanceNegativeExpression() {
58 | let a = Int16.random()
59 | let b = Int16.random()
60 | let c = Int16.random()
61 |
62 | measure {
63 | let expression = try! MathExpression(String(format: Formula.SubtractionTests.negativeExpressionWithInitialMinus, a, b, c))
64 | _ = expression.evaluate()
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q, +, *) field tests/CombinedOperationsPerformanceTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class CombinedOperationsPerformanceTests: XCTestCase {
7 | func testPerformanceProductOfAddition() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 | let c = Int16.random()
11 | let d = Int16.random()
12 |
13 | measure {
14 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.productOfAddition, a, b, c, d))
15 | _ = expression.evaluate()
16 | }
17 | }
18 |
19 | func testPerformanceSquareOfAddition() {
20 | let a = Int16.random()
21 | let b = Int16.random()
22 |
23 | measure {
24 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.productOfAddition, a, b, a, b))
25 | _ = expression.evaluate()
26 | }
27 | }
28 |
29 | func testPerformanceProductOfSubtraction() {
30 | let a = Int16.random()
31 | let b = Int16.random()
32 | let c = Int16.random()
33 | let d = Int16.random()
34 |
35 | measure {
36 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.productOfSubtraction, a, b, c, d))
37 | _ = expression.evaluate()
38 | }
39 | }
40 |
41 | func testPerformanceSquareOfSubtraction() {
42 | let a = Int16.random()
43 | let b = Int16.random()
44 |
45 | measure {
46 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.productOfSubtraction, a, b, a, b))
47 | _ = expression.evaluate()
48 | }
49 | }
50 |
51 | func testPerformanceProductOfAdditionWithSubtraction() {
52 | let a = Int16.random()
53 | let b = Int16.random()
54 | let c = Int16.random()
55 | let d = Int16.randomExcluding(c)
56 |
57 | measure {
58 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.productOfAdditionWithSubtraction, a, b, c, d))
59 | _ = expression.evaluate()
60 | }
61 | }
62 |
63 | func testPerformanceProductOfAdditionWithSubtractionIdentity() {
64 | let a = Int16.random()
65 | let b = Int16.random()
66 |
67 | measure {
68 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.productOfAdditionWithSubtraction, a, b, a, b))
69 | _ = expression.evaluate()
70 | }
71 | }
72 |
73 | func testPerformanceDivisionOfAdditionWithSubtraction() {
74 | let a = Int16.random()
75 | let b = Int16.random()
76 | let c = Int16.random()
77 | let d = Int16.randomExcluding(c)
78 |
79 | measure {
80 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.divisionOfAdditionWithSubtraction, a, b, c, d))
81 | _ = expression.evaluate()
82 | }
83 | }
84 |
85 | func testPerformanceDivisionOfProductOfAdditionsWithProductOfSubtractions() {
86 | let a = Int16.random()
87 | let b = Int16.random()
88 | let c = Int16.random()
89 | let d = Int16.random()
90 | let e = Int16.random()
91 | let f = Int16.randomExcluding(e)
92 | let g = Int16.random()
93 | let h = Int16.randomExcluding(g)
94 |
95 | measure {
96 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.divisionOfProductOfAdditionsWithProductOfSubtractions, a, b, c, d, e, f, g, h))
97 | _ = expression.evaluate()
98 | }
99 | }
100 |
101 | func testPerformanceDivisionOfCrossedProducts() {
102 | let a = Int16.random()
103 | let b = Int16.random()
104 | let c = Int16.random()
105 | let d = Int16.random()
106 | let e = Int16.random()
107 | let f = Int16.random()
108 | let g = Int16.random()
109 | let h = Int16.randomExcluding(g)
110 |
111 | measure {
112 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.divisionOfCrossedProducts, a, b, c, d, e, f, g, h))
113 | _ = expression.evaluate()
114 | }
115 | }
116 |
117 | func testPerformanceAdditionWithProductNoParentheses() {
118 | let a = Int16.random()
119 | let b = Int16.random()
120 | let c = Int16.random()
121 |
122 | measure {
123 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.additionWithProductNoParentheses, a, b, c))
124 | _ = expression.evaluate()
125 | }
126 | }
127 |
128 | func testPerformanceAdditionWithDivisionNoParentheses() {
129 | let a = Int16.random()
130 | let b = Int16.random()
131 | let c = Int16.random()
132 |
133 | measure {
134 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.additionWithDivisionNoParentheses, a, b, c))
135 | _ = expression.evaluate()
136 | }
137 | }
138 |
139 | func testPerformanceSubtractionWithProductNoParentheses() {
140 | let a = Int16.random()
141 | let b = Int16.random()
142 | let c = Int16.random()
143 |
144 | measure {
145 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.subtractionWithProductNoParentheses, a, b, c))
146 | _ = expression.evaluate()
147 | }
148 | }
149 |
150 | func testPerformanceSubtractionWithDivisionNoParentheses() {
151 | let a = Int16.random()
152 | let b = Int16.random()
153 | let c = Int16.random()
154 |
155 | measure {
156 | let expression = try! MathExpression(String(format: Formula.CombinedOperationsTests.subtractionWithDivisionNoParentheses, a, b, c))
157 | _ = expression.evaluate()
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/(Q, +, *) field tests/FieldAxiomPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class FieldAxiomPerformanceTests: XCTestCase {
7 | func testPerformanceDistributivityOfProductOverAddition() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 | let c = Int16.random()
11 |
12 | measure {
13 | let leftDistributivityFactoredExpression = try! MathExpression(String(format: Formula.FieldAxiomTests.leftDistributivityFactored, a, b, c))
14 | _ = leftDistributivityFactoredExpression.evaluate()
15 |
16 | let rightDistributivityFactoredExpression = try! MathExpression(String(format: Formula.FieldAxiomTests.rightDistributivityFactored, a, b, c))
17 | _ = rightDistributivityFactoredExpression.evaluate()
18 |
19 | let distributivityExpandedExpression = try! MathExpression(String(format: Formula.FieldAxiomTests.distributivityExpanded, a, c, b, c))
20 | _ = distributivityExpandedExpression.evaluate()
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/Non-trivial transformation tests/CountTransformationPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class CountTransformationPerformanceTests: XCTestCase {
7 | let transformation: (String) -> Double = { Double($0.count) }
8 |
9 | func testPerformanceProductOfAddition() {
10 | let a = Int16.random()
11 | let b = String.random(length: Int.random(in: 5...20))
12 | let c = Int16.random()
13 | let d = String.random(length: Int.random(in: 5...20))
14 | let formula = String(format: Formula.TransformationTests.productOfAddition, a, b, c, d)
15 |
16 | measure {
17 | let expression = try! MathExpression(formula, transformation: transformation)
18 | _ = expression.evaluate()
19 | }
20 | }
21 |
22 | func testPerformanceSquareOfAddition() {
23 | let a = Int16.random()
24 | let b = String.random(length: Int.random(in: 5...20))
25 | let formula = String(format: Formula.TransformationTests.productOfAddition, a, b, a, b)
26 |
27 | measure {
28 | let expression = try! MathExpression(formula, transformation: transformation)
29 | _ = expression.evaluate()
30 | }
31 | }
32 |
33 | func testPerformanceProductOfSubtraction() {
34 | let a = Int16.random()
35 | let b = String.random(length: Int.random(in: 5...20))
36 | let c = Int16.random()
37 | let d = String.random(length: Int.random(in: 5...20))
38 | let formula = String(format: Formula.TransformationTests.productOfSubtraction, a, b, c, d)
39 |
40 | measure {
41 | let expression = try! MathExpression(formula, transformation: transformation)
42 | _ = expression.evaluate()
43 | }
44 | }
45 |
46 | func testPerformanceSquareOfSubtraction() {
47 | let a = Int16.random()
48 | let b = String.random(length: Int.random(in: 5...20))
49 | let formula = String(format: Formula.TransformationTests.productOfSubtraction, a, b, a, b)
50 |
51 | measure {
52 | let expression = try! MathExpression(formula, transformation: transformation)
53 | _ = expression.evaluate()
54 | }
55 | }
56 |
57 | func testPerformanceProductOfAdditionWithSubtraction() {
58 | let a = Int16.random()
59 | let b = String.random(length: Int.random(in: 5...20))
60 | let c = Int16.random()
61 | let d = String.random(length: Int.random(in: 5...20))
62 | let formula = String(format: Formula.TransformationTests.productOfAdditionWithSubtraction, a, b, c, d)
63 |
64 | measure {
65 | let expression = try! MathExpression(formula, transformation: transformation)
66 | _ = expression.evaluate()
67 | }
68 | }
69 |
70 | func testPerformanceProductOfAdditionWithSubtractionIdentity() {
71 | let a = Int16.random()
72 | let b = String.random(length: Int.random(in: 5...20))
73 | let formula = String(format: Formula.TransformationTests.productOfAdditionWithSubtraction, a, b, a, b)
74 |
75 | measure {
76 | let expression = try! MathExpression(formula, transformation: transformation)
77 | _ = expression.evaluate()
78 | }
79 | }
80 |
81 | func testPerformanceDivisionOfAdditionWithSubtraction() {
82 | let a = Int16.random()
83 | let b = String.random(length: Int.random(in: 5...20))
84 | let c = Int16.random()
85 | let d = String.random(length: Int.random(in: 5...20))
86 | let formula = String(format: Formula.TransformationTests.divisionOfAdditionWithSubtraction, a, b, c, d)
87 |
88 | measure {
89 | let expression = try! MathExpression(formula, transformation: transformation)
90 | _ = expression.evaluate()
91 | }
92 | }
93 |
94 | func testPerformanceDivisionOfProductOfAdditionsWithProductOfSubtractions() {
95 | let a = Int16.random()
96 | let b = String.random(length: Int.random(in: 5...20))
97 | let c = Int16.random()
98 | let d = String.random(length: Int.random(in: 5...20))
99 | let e = Int16.random()
100 | let f = String.random(length: Int.random(in: 5...20))
101 | let g = Int16.random()
102 | let h = String.random(length: Int.random(in: 5...20))
103 | let formula = String(format: Formula.TransformationTests.divisionOfProductOfAdditionsWithProductOfSubtractions, a, b, c, d, e, f, g, h)
104 |
105 | measure {
106 | let expression = try! MathExpression(formula, transformation: transformation)
107 | _ = expression.evaluate()
108 | }
109 | }
110 |
111 | func testPerformanceDivisionOfCrossedProducts() {
112 | let a = Int16.random()
113 | let b = String.random(length: Int.random(in: 5...20))
114 | let c = Int16.random()
115 | let d = String.random(length: Int.random(in: 5...20))
116 | let e = Int16.random()
117 | let f = String.random(length: Int.random(in: 5...20))
118 | let g = Int16.random()
119 | let h = String.random(length: Int.random(in: 5...20))
120 | let formula = String(format: Formula.TransformationTests.divisionOfCrossedProducts, a, b, c, d, e, f, g, h)
121 |
122 | measure {
123 | let expression = try! MathExpression(formula, transformation: transformation)
124 | _ = expression.evaluate()
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/Non-trivial transformation tests/FactorialTransformationPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class FactorialTransformationPerformanceTests: XCTestCase {
7 | let transformation: (String) -> Double = { string in
8 | guard string.last == "!", var number = Int(string.dropLast()) else { return .zero }
9 | return Double(number.factorial())
10 | }
11 |
12 | func testPerformanceProductOfAddition() {
13 | let a = Int16.random()
14 | let b = Int.random(in: 0...10)
15 | let c = Int16.random()
16 | let d = Int.random(in: 0...10)
17 | let formula = String(format: Formula.TransformationTests.productOfAddition, a, "\(b)!", c, "\(d)!")
18 |
19 | measure {
20 | let expression = try! MathExpression(formula, transformation: transformation)
21 | _ = expression.evaluate()
22 | }
23 | }
24 |
25 | func testPerformanceSquareOfAddition() {
26 | let a = Int16.random()
27 | let b = Int.random(in: 0...10)
28 | let formula = String(format: Formula.TransformationTests.productOfAddition, a, "\(b)!", a, "\(b)!")
29 |
30 | measure {
31 | let expression = try! MathExpression(formula, transformation: transformation)
32 | _ = expression.evaluate()
33 | }
34 | }
35 |
36 | func testPerformanceProductOfSubtraction() {
37 | let a = Int16.random()
38 | let b = Int.random(in: 0...10)
39 | let c = Int16.random()
40 | let d = Int.random(in: 0...10)
41 | let formula = String(format: Formula.TransformationTests.productOfSubtraction, a, "\(b)!", c, "\(d)!")
42 |
43 | measure {
44 | let expression = try! MathExpression(formula, transformation: transformation)
45 | _ = expression.evaluate()
46 | }
47 | }
48 |
49 | func testPerformanceSquareOfSubtraction() {
50 | let a = Int16.random()
51 | let b = Int.random(in: 0...10)
52 | let formula = String(format: Formula.TransformationTests.productOfSubtraction, a, "\(b)!", a, "\(b)!")
53 |
54 | measure {
55 | let expression = try! MathExpression(formula, transformation: transformation)
56 | _ = expression.evaluate()
57 | }
58 | }
59 |
60 | func testPerformanceProductOfAdditionWithSubtraction() {
61 | let a = Int16.random()
62 | let b = Int.random(in: 0...10)
63 | let c = Int16.random()
64 | let d = Int.random(in: 0...10)
65 | let formula = String(format: Formula.TransformationTests.productOfAdditionWithSubtraction, a, "\(b)!", c, "\(d)!")
66 |
67 | measure {
68 | let expression = try! MathExpression(formula, transformation: transformation)
69 | _ = expression.evaluate()
70 | }
71 | }
72 |
73 | func testPerformanceProductOfAdditionWithSubtractionIdentity() {
74 | let a = Int16.random()
75 | let b = Int.random(in: 0...10)
76 | let formula = String(format: Formula.TransformationTests.productOfAdditionWithSubtraction, a, "\(b)!", a, "\(b)!")
77 |
78 | measure {
79 | let expression = try! MathExpression(formula, transformation: transformation)
80 | _ = expression.evaluate()
81 | }
82 | }
83 |
84 | func testPerformanceDivisionOfAdditionWithSubtraction() {
85 | let a = Int16.random()
86 | let b = Int.random(in: 0...10)
87 | let c = Int16.random()
88 | let d = Int.random(in: 0...10)
89 | let formula = String(format: Formula.TransformationTests.divisionOfAdditionWithSubtraction, a, "\(b)!", c, "\(d)!")
90 |
91 | measure {
92 | let expression = try! MathExpression(formula, transformation: transformation)
93 | _ = expression.evaluate()
94 | }
95 | }
96 |
97 | func testPerformanceDivisionOfProductOfAdditionsWithProductOfSubtractions() {
98 | let a = Int16.random()
99 | let b = Int.random(in: 0...10)
100 | let c = Int16.random()
101 | let d = Int.random(in: 0...10)
102 | let e = Int16.random()
103 | let f = Int.random(in: 0...10)
104 | let g = Int16.random()
105 | let h = Int.random(in: 0...10)
106 | let formula = String(format: Formula.TransformationTests.divisionOfProductOfAdditionsWithProductOfSubtractions, a, "\(b)!", c, "\(d)!", e, "\(f)!", g, "\(h)!")
107 |
108 | measure {
109 | let expression = try! MathExpression(formula, transformation: transformation)
110 | _ = expression.evaluate()
111 | }
112 | }
113 |
114 | func testPerformanceDivisionOfCrossedProducts() {
115 | let a = Int16.random()
116 | let b = Int.random(in: 0...10)
117 | let c = Int16.random()
118 | let d = Int.random(in: 0...10)
119 | let e = Int16.random()
120 | let f = Int.random(in: 0...10)
121 | let g = Int16.random()
122 | let h = Int.random(in: 0...10)
123 | let formula = String(format: Formula.TransformationTests.divisionOfCrossedProducts, a, "\(b)!", c, "\(d)!", e, "\(f)!", g, "\(h)!")
124 |
125 | measure {
126 | let expression = try! MathExpression(formula, transformation: transformation)
127 | _ = expression.evaluate()
128 | }
129 | }
130 | }
131 |
132 | private extension Int {
133 | func factorial() -> Int {
134 | guard self > 1 else { return 1 }
135 | return self * (self - 1).factorial()
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/MathExpressionPerformanceTests/Source/Stress tests/StressPerformanceTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class StressPerformanceTests: XCTestCase {
7 | func testPerformanceFullParenthesesSingleValue() {
8 | let numberOfParentheses = 500
9 | var expression = String(repeating: Character(MathBrackets.parenthesis.opening), count: numberOfParentheses)
10 | expression += "10"
11 | expression += String(repeating: Character(MathBrackets.parenthesis.closing), count: numberOfParentheses)
12 |
13 | measure {
14 | let mathExpression = try! MathExpression(expression)
15 | _ = mathExpression.evaluate()
16 | }
17 | }
18 |
19 | func testPerformanceFullParenthesesSumAndProduct() {
20 | let numberOfParentheses = 500
21 | var expression = ""
22 | for _ in 0.. Double {
10 | let divisor = pow(10.0, Double(places))
11 | return (self * divisor).rounded(roundingRule) / divisor
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/MathExpressionTestHelpers/Extensions/Numeric+Random.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | extension Int16 {
4 | static func random() -> Int16 {
5 | Int16.random(in: Int16.min...Int16.max)
6 | }
7 |
8 | static func randomEven(in range: ClosedRange) -> Int16 {
9 | let randomValue = random(in: range)
10 | if randomValue.quotientAndRemainder(dividingBy: 2).remainder == 0 {
11 | return randomValue
12 | } else if randomValue > 0 {
13 | return randomValue - 1
14 | } else {
15 | return randomValue + 1
16 | }
17 | }
18 |
19 | static func randomOdd(in range: ClosedRange) -> Int16 {
20 | let evenNumber = randomEven(in: range)
21 | return evenNumber > .zero ? evenNumber - 1 : evenNumber + 1
22 | }
23 |
24 | static func randomExcludingZero() -> Int16 {
25 | randomExcluding(.zero)
26 | }
27 |
28 | static func randomExcluding(_ excluded: Int16) -> Int16 {
29 | let randomValue = random()
30 | return randomValue == excluded ? randomExcluding(excluded) : randomValue
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/MathExpressionTestHelpers/Extensions/String+Random.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | extension String {
4 | static func random(length: Int) -> String {
5 | let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
6 | return String((0..(
7 | _ expression: @autoclosure () throws -> T,
8 | throws error: E,
9 | in file: StaticString = #file,
10 | line: UInt = #line
11 | ) {
12 | var thrownError: Error?
13 |
14 | XCTAssertThrowsError(try expression(), file: file, line: line) { error in
15 | thrownError = error
16 | }
17 |
18 | XCTAssertTrue(thrownError is E, "Unexpected error type: \(type(of: thrownError))", file: file, line: line)
19 | XCTAssertEqual(thrownError as? E, error, file: file, line: line)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/MathExpressionTestHelpers/Helpers/Formulae.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | enum Formula {
4 | enum AbelianAdditiveGroupAxiomsTests {
5 | static let neutralElement = "%d + %d"
6 | static let inverseElement = "- %d"
7 | static let leftInverse = "- %d + %d"
8 | static let rightInverse = "%d + (- %d)"
9 | static let leftAssociativity = "(%d + %d) + %d"
10 | static let rightAssociativity = "%d + (%d + %d)"
11 | static let commutativity = "%d + %d"
12 | }
13 |
14 | enum AbelianMultiplicativeGroupAxiomTests {
15 | static let neutralElement = "%d * %d"
16 | static let inverseElement = "1 / %d"
17 | static let leftInverse = "(1 / %d) * %d"
18 | static let rightInverse = "%d * (1 / %d)"
19 | static let leftAssociativity = "(%d * %d) * %d"
20 | static let rightAssociativity = "%d * (%d * %d)"
21 | static let commutativity = "%d * %d"
22 | }
23 |
24 | enum AdditionAndSubtractionTests {
25 | static let simpleOperation = "%d - %d + %d"
26 | static let simpleOperationWithParentheses = "%d - (-%d + %d)"
27 | static let simpleOperationWithParenthesesAndInitialMinus = "- (%d - %d) - %d + %d"
28 | }
29 |
30 | enum AdditionTests {
31 | static let simpleAddition = "%d + %d"
32 | static let complicatedAddition = "%d + %d + %d + %d"
33 | static let simpleAdditionWithParentheses = "(%d + %d)"
34 | static let complicatedAdditionWithSingleParenthesis = "%d + (%d + %d) + %d"
35 | static let complicatedAdditionWithMultipleParenthesis = "(%d + %d) + %d + ((%d + %d) + %d)"
36 | }
37 |
38 | enum CombinedOperationsTests {
39 | static let productOfAddition = "(%d + %d) * (%d + %d)"
40 | static let productOfSubtraction = "(%d - %d) * (%d - %d)"
41 | static let productOfAdditionWithSubtraction = "(%d + %d) * (%d - %d)"
42 | static let divisionOfAdditionWithSubtraction = "(%d + %d) / (%d - %d)"
43 | static let divisionOfProductOfAdditionsWithProductOfSubtractions = "(%d + %d) * (%d + %d) / ((%d - %d) * (%d - %d))"
44 | static let divisionOfCrossedProducts = "(%d + %d) * (%d - %d) / ((%d + %d) * (%d - %d))"
45 | static let additionWithProductNoParentheses = "%d + %d * %d"
46 | static let additionWithDivisionNoParentheses = "%d + %d / %d"
47 | static let subtractionWithProductNoParentheses = "%d * %d - %d"
48 | static let subtractionWithDivisionNoParentheses = "%d - %d / %d"
49 | }
50 |
51 | enum DivisionTests {
52 | static let simpleDivision = "%d / %d"
53 | static let simpleDivisionWithParentheses = "(%d / %d)"
54 | static let simpleDivisionWithParenthesesWithInitialDivision = "1 / (%d / %d)"
55 | static let complicatedDivision = "%d / %d / %d"
56 | static let divideSingleNumber = "1 / %d"
57 | static let consecutiveDivisionExpressionWithInitialDivision = "1 / %d / %d / %d"
58 | }
59 |
60 | enum FieldAxiomTests {
61 | static let leftDistributivityFactored = "%d * (%d + %d)"
62 | static let rightDistributivityFactored = "(%d + %d) * %d"
63 | static let distributivityExpanded = "(%d * %d) + (%d * %d)"
64 | }
65 |
66 | enum ProductAndDivisionTests {
67 | static let simpleOperation = "%d / %d * %d"
68 | static let simpleOperationWithParentheses = "%d / (1 / %d * %d)"
69 | static let simpleOperationWithParenthesesAndInitialDivision = "1 / (%d / %d) / %d * %d"
70 | static let complicatedOperationWithParentheses = "(1 / %d * (%d / %d) * %d) / (%d * %d)"
71 | }
72 |
73 | enum ProductTests {
74 | static let simpleProduct = "%d * %d"
75 | static let complicatedProduct = "%d * %d * %d * %d"
76 | static let simpleProductWithParentheses = "(%d * %d)"
77 | static let complicatedProductWithSingleParenthesis = "%d * (%d * %d) * %d"
78 | static let complicatedProductWithMultipleParenthesis = "(%d * %d) * (%d * %d)"
79 | }
80 |
81 | enum SubtractionTests {
82 | static let simpleSubtraction = "%d - %d"
83 | static let simpleSubtractionWithParentheses = "(%d - %d)"
84 | static let simpleSubtractionWithParenthesesWithInitialMinus = "- (%d - %d)"
85 | static let complicatedSubtraction = "%d - %d - %d"
86 | static let negativeSingleNumber = "- %d"
87 | static let negativeExpressionWithInitialMinus = "- %d - %d - %d"
88 | }
89 |
90 | enum TransformationTests {
91 | static let productOfAddition = "(%d + %@) * (%d + %@)"
92 | static let productOfSubtraction = "(%d - %@) * (%d - %@)"
93 | static let productOfAdditionWithSubtraction = "(%d + %@) * (%d - %@)"
94 | static let divisionOfAdditionWithSubtraction = "(%d + %@) / (%d - %@)"
95 | static let divisionOfProductOfAdditionsWithProductOfSubtractions = "(%d + %@) * (%d + %@) / ((%d - %@) * (%d - %@))"
96 | static let divisionOfCrossedProducts = "(%d + %@) * (%d - %@) / ((%d + %@) * (%d - %@))"
97 | }
98 |
99 | enum ValidationTests {
100 | enum Invalid {
101 | static let unevenBracketNumber = "(%d + %d) * %d / %d - %d)"
102 | static let misplacedBrackets = ")%d) + %d) * (%d / (%d - %d)"
103 | static let misplacedSumAndClosingBracket = "(%d + %d +) * (%d / (%d - %d))"
104 | static let misplacedSubtractionAndClosingBracket = "(%d + %d -) * (%d / (%d - %d))"
105 | static let misplacedProductAndClosingBracket = "(%d + %d *) * (%d / (%d - %d))"
106 | static let misplacedDivisionAndClosingBracket = "(%d + %d /) * (%d / (%d - %d))"
107 | static let misplacedOpeningBracketAndProduct = "(%d + %d) * (%d / (* %d - %d))"
108 | static let misplacedOpeningBracketAndDivision = "(%d + %d) * (%d / (/ %d - %d))"
109 | static let productFollowedByDivision = "(%d + %d) * / (%d / (%d - %d))"
110 | static let divisionFollowedByProduct = "(%d + %d) / * (%d / (%d - %d))"
111 | static let consecutiveProducts = "(%d + %d) * * (%d / (%d - %d))"
112 | static let consecutiveDivisions = "(%d + %d) / / (%d / (%d - %d))"
113 | static let sumFollowedByProduct = "(%d + %d) + * (%d / (%d - %d))"
114 | static let sumFollowedByDivision = "(%d + %d) + / (%d / (%d - %d))"
115 | static let subtractionFollowedByProduct = "(%d + %d) - * (%d / (%d - %d))"
116 | static let subtractionFollowedByDivision = "(%d + %d) - / (%d / (%d - %d))"
117 | static let startsWithProduct = "* (%d + %d) / (%d / (%d - %d))"
118 | static let startsWithDivision = "/ (%d + %d) * (%d / (%d - %d))"
119 | static let endsWithSum = "(%d + %d) * (%d / (%d - %d)) +"
120 | static let endsWithSubtraction = "(%d + %d) * (%d / (%d - %d)) -"
121 | static let endsWithProduct = "(%d + %d) * (%d / (%d - %d)) *"
122 | static let endsWithDivision = "(%d + %d) * (%d / (%d - %d)) /"
123 | }
124 |
125 | enum Valid {
126 | static let generic = "(%d + %d) * (%d / (%d - %d))"
127 | static let consecutiveSubtractions = "(%d - - %d) * (%d / (%d - %d))"
128 | static let consecutiveSums = "(%d + + %d) * (%d / (%d - %d))"
129 | static let consecutiveSumAndSubtraction = "(%d + %d) * (%d / (%d + - %d))"
130 | static let consecutiveSubtractionAndSum = "(%d + %d) * (%d / (%d - + %d))"
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/MathExpressionTestHelpers/Helpers/Operation.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import MathExpression
4 |
5 | struct Operation {
6 | private let expression: MathExpression
7 | let expectedResult: Double
8 |
9 | init(_ expression: String, expectedResult: Double) {
10 | self.expression = try! MathExpression(expression)
11 | self.expectedResult = expectedResult
12 | }
13 |
14 | func compute() -> Double {
15 | return expression.evaluate()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MathExpressionTestHelpers/Helpers/RandomExpressionGenerator.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import Foundation
4 | @testable import MathExpression
5 |
6 | class RandomExpressionGenerator {
7 | let inputs: [String: Int] = [
8 | Formula.AdditionTests.simpleAddition: 2,
9 | Formula.AdditionTests.complicatedAddition: 4,
10 | Formula.AdditionTests.simpleAdditionWithParentheses: 2,
11 | Formula.AdditionTests.complicatedAdditionWithSingleParenthesis: 4,
12 | Formula.AdditionTests.complicatedAdditionWithMultipleParenthesis: 6,
13 | Formula.AdditionAndSubtractionTests.simpleOperation: 3,
14 | Formula.AdditionAndSubtractionTests.simpleOperationWithParentheses: 3,
15 | Formula.AdditionAndSubtractionTests.simpleOperationWithParenthesesAndInitialMinus: 4,
16 | Formula.CombinedOperationsTests.productOfAddition: 4,
17 | Formula.CombinedOperationsTests.productOfSubtraction: 4,
18 | Formula.CombinedOperationsTests.productOfAdditionWithSubtraction: 4,
19 | Formula.CombinedOperationsTests.divisionOfAdditionWithSubtraction: 4,
20 | Formula.CombinedOperationsTests.divisionOfProductOfAdditionsWithProductOfSubtractions: 8,
21 | Formula.CombinedOperationsTests.divisionOfCrossedProducts: 8,
22 | Formula.DivisionTests.simpleDivision: 2,
23 | Formula.DivisionTests.simpleDivisionWithParentheses: 2,
24 | Formula.DivisionTests.simpleDivisionWithParenthesesWithInitialDivision: 2,
25 | Formula.DivisionTests.complicatedDivision: 3,
26 | Formula.DivisionTests.divideSingleNumber: 1,
27 | Formula.DivisionTests.consecutiveDivisionExpressionWithInitialDivision: 3,
28 | Formula.ProductAndDivisionTests.simpleOperation: 3,
29 | Formula.ProductAndDivisionTests.simpleOperationWithParentheses: 3,
30 | Formula.ProductAndDivisionTests.simpleOperationWithParenthesesAndInitialDivision: 4,
31 | Formula.ProductAndDivisionTests.complicatedOperationWithParentheses: 6,
32 | Formula.ProductTests.simpleProduct: 2,
33 | Formula.ProductTests.complicatedProduct: 4,
34 | Formula.ProductTests.simpleProductWithParentheses: 2,
35 | Formula.ProductTests.complicatedProductWithSingleParenthesis: 4,
36 | Formula.ProductTests.complicatedProductWithMultipleParenthesis: 4,
37 | Formula.SubtractionTests.simpleSubtraction: 2,
38 | Formula.SubtractionTests.simpleSubtractionWithParentheses: 2,
39 | Formula.SubtractionTests.simpleSubtractionWithParenthesesWithInitialMinus: 2,
40 | Formula.SubtractionTests.complicatedSubtraction: 3,
41 | Formula.SubtractionTests.negativeSingleNumber: 1,
42 | Formula.SubtractionTests.negativeExpressionWithInitialMinus: 3
43 | ]
44 |
45 | func generateRandomExpression(combining numberOfOperationsToCombine: Int) -> String {
46 | var expression = ""
47 | var openingBrackets = 0
48 |
49 | for index in 0.. 0 {
55 | expression += MathOperator.validationCases.randomElement()!.rawValue
56 | }
57 |
58 | // We generate the formula string with random number values using the auxiliary function.
59 | let formula = generateString(for: formulaPair)
60 |
61 | // We randomly add an opening bracket (30% chance), or we add it if there's a "-" at the beginning of the
62 | // formula to prevent a validation failure.
63 | if UInt8.random(in: 0...100) <= 30 || formula.first == Character(MathOperator.subtraction.rawValue) {
64 | expression += MathBrackets.parenthesis.opening
65 | openingBrackets += 1
66 | }
67 |
68 | expression += formula
69 |
70 | // If there's any open bracket, we randomly add a closing bracket (30% chance).
71 | if openingBrackets > 0, UInt8.random(in: 0...10) <= 30 {
72 | expression += MathBrackets.parenthesis.closing
73 | openingBrackets -= 1
74 | }
75 | }
76 |
77 | // In case there is still any open bracket without counterpart, we add closing brackets to match.
78 | expression += String(repeating: Character(MathBrackets.parenthesis.closing), count: openingBrackets)
79 |
80 | return expression
81 | }
82 |
83 | private func generateString(for formula: (key: String, value: Int)) -> String {
84 | var args: [Int16] = []
85 |
86 | for _ in 0..
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q - {0}, *) group tests/AbelianMultiplicativeGroupAxiomTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class AbelianMultiplicativeGroupAxiomTests: XCTestCase {
7 | func testNeutralElement() throws {
8 | let a = Int16.random()
9 | let neutralElement: Int16 = 1
10 |
11 | let leftNeutralExpression = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.neutralElement, neutralElement, a))
12 | let rightNeutralExpression = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.neutralElement, a, neutralElement))
13 | let expectedResult = Double(a)
14 |
15 | XCTAssertEqual(leftNeutralExpression.evaluate(), rightNeutralExpression.evaluate())
16 | XCTAssertEqual(leftNeutralExpression.evaluate(), expectedResult)
17 | }
18 |
19 | func testInverse() throws {
20 | let a = Int16.random()
21 |
22 | let inverseExpression = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.inverseElement, a))
23 | let leftProductWithInverse = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.leftInverse, a, a))
24 | let rightProductWithInverse = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.rightInverse, a, a))
25 | let expectedResult = 1.0 / Double(a)
26 |
27 | XCTAssertEqual(inverseExpression.evaluate(), expectedResult)
28 | XCTAssertEqual(leftProductWithInverse.evaluate(), rightProductWithInverse.evaluate())
29 | // We need to round the evaluation result to catch cases in which the result is off 1 by 10^(-6) or less,
30 | // such as 0.99999999999953 or 1.0000000000005138
31 | // These errors are acceptable minor errors inherent to floating value computations and not the
32 | // algorithm itself.
33 | XCTAssertEqual(leftProductWithInverse.evaluate().rounded(.toNearestOrAwayFromZero), 1.0)
34 | }
35 |
36 | func testAssociativity() throws {
37 | let a = Int16.random()
38 | let b = Int16.random()
39 | let c = Int16.random()
40 |
41 | let leftAssociativityExpression = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.leftAssociativity, a, b, c))
42 | let rightAssociativityExpression = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.rightAssociativity, a, b, c))
43 | let expectedResult = Double(a) * Double(b) * Double(c)
44 |
45 | XCTAssertEqual(leftAssociativityExpression.evaluate(), rightAssociativityExpression.evaluate())
46 | XCTAssertEqual(leftAssociativityExpression.evaluate(), expectedResult)
47 | }
48 |
49 | func testCommutativity() throws {
50 | let a = Int16.random()
51 | let b = Int16.random()
52 |
53 | let firstExpression = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.commutativity, a, b))
54 | let secondExpression = try MathExpression(String(format: Formula.AbelianMultiplicativeGroupAxiomTests.commutativity, b, a))
55 | let expectedResult = Double(a) * Double(b)
56 |
57 | XCTAssertEqual(firstExpression.evaluate(), secondExpression.evaluate())
58 | XCTAssertEqual(firstExpression.evaluate(), expectedResult)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q - {0}, *) group tests/DivisionTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | // Most operation results in this class are rounded to avoid intrinsic computation errors.
7 | // We consider that results which are off by 10^(-6) or less are acceptable errors inherent
8 | // to floating value computations and not the algorithm itself.
9 |
10 | class DivisionTests: XCTestCase {
11 | func testSimpleDivision() {
12 | let a = Int16.random()
13 | let b = Int16.random()
14 | let operation = Operation(
15 | String(format: Formula.DivisionTests.simpleDivision, a, b),
16 | expectedResult: Double(a) / Double(b)
17 | )
18 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
19 | }
20 |
21 | func testSimpleDivisionWithParentheses() {
22 | let a = Int16.random()
23 | let b = Int16.random()
24 | let operation = Operation(
25 | String(format: Formula.DivisionTests.simpleDivisionWithParentheses, a, b),
26 | expectedResult: Double(a) / Double(b)
27 | )
28 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
29 | }
30 |
31 | func testSimpleDivisionWithParenthesesWithInitialDivision() {
32 | let a = Int16.random()
33 | let b = Int16.random()
34 | let operation = Operation(
35 | String(format: Formula.DivisionTests.simpleDivisionWithParenthesesWithInitialDivision, a, b),
36 | expectedResult: 1.0 / (Double(a) / Double(b))
37 | )
38 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
39 | }
40 |
41 | func testComplicatedDivision() {
42 | let a = Int16.random()
43 | let b = Int16.random()
44 | let c = Int16.random()
45 | let operation = Operation(
46 | String(format: Formula.DivisionTests.complicatedDivision, a, b, c),
47 | expectedResult: Double(a) / Double(b) / Double(c)
48 | )
49 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
50 | }
51 |
52 | func testDivideSingleNumber() {
53 | let a = Int16.random()
54 | let operation = Operation(
55 | String(format: Formula.DivisionTests.divideSingleNumber, a),
56 | expectedResult: 1.0 / Double(a)
57 | )
58 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
59 | }
60 |
61 | func testConsecutiveDivisionExpressionWithInitialDivision() {
62 | let a = Int16.random()
63 | let b = Int16.random()
64 | let c = Int16.random()
65 | let operation = Operation(
66 | String(format: Formula.DivisionTests.consecutiveDivisionExpressionWithInitialDivision, a, b, c),
67 | expectedResult: 1.0 / Double(a) / Double(b) / Double(c)
68 | )
69 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q - {0}, *) group tests/ProductAndDivisionTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class ProductAndDivisionTests: XCTestCase {
7 | func testSimpleOperation() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 | let c = Int16.random()
11 | let operation = Operation(
12 | String(format: Formula.ProductAndDivisionTests.simpleOperation, a, b, c),
13 | expectedResult: Double(a) / Double(b) * Double(c)
14 | )
15 | XCTAssertEqual(operation.compute(), operation.expectedResult)
16 | }
17 |
18 | func testSimpleOperationWithParentheses() {
19 | let a = Int16.random()
20 | let b = Int16.random()
21 | let c = Int16.random()
22 | let operation = Operation(
23 | String(format: Formula.ProductAndDivisionTests.simpleOperationWithParentheses, a, b, c),
24 | expectedResult: Double(a) / (Double(c) / Double(b))
25 | )
26 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
27 | }
28 |
29 | func testSimpleOperationWithParenthesesAndInitialDivision() {
30 | let a = Int16.random()
31 | let b = Int16.random()
32 | let c = Int16.random()
33 | let d = Int16.random()
34 | let operation = Operation(
35 | String(format: Formula.ProductAndDivisionTests.simpleOperationWithParenthesesAndInitialDivision, a, b, c, d),
36 | expectedResult: 1.0 / (Double(a) / Double(b)) / Double(c) * Double(d)
37 | )
38 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
39 | }
40 |
41 | func testComplicatedOperationWithParentheses() {
42 | let a = Int16.random()
43 | let b = Int16.random()
44 | let c = Int16.random()
45 | let d = Int16.random()
46 | let e = Int16.random()
47 | let f = Int16.random()
48 |
49 | let operation = Operation(
50 | String(format: Formula.ProductAndDivisionTests.complicatedOperationWithParentheses, a, b, c, d, e, f),
51 | expectedResult: (1.0 / Double(a) * (Double(b) / Double(c)) * Double(d)) / (Double(e) * Double(f))
52 | )
53 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q - {0}, *) group tests/ProductTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import XCTest
4 |
5 | class ProductTests: XCTestCase {
6 | func testSimpleProduct() {
7 | let a = Int16.random()
8 | let b = Int16.random()
9 | let operation = Operation(
10 | String(format: Formula.ProductTests.simpleProduct, a, b),
11 | expectedResult: Double(a) * Double(b)
12 | )
13 | XCTAssertEqual(operation.compute(), operation.expectedResult)
14 | }
15 |
16 | func testComplicatedProduct() {
17 | let a = Int16.random()
18 | let b = Int16.random()
19 | let c = Int16.random()
20 | let d = Int16.random()
21 | let operation = Operation(
22 | String(format: Formula.ProductTests.complicatedProduct, a, b, c, d),
23 | expectedResult: Double(a) * Double(b) * Double(c) * Double(d)
24 | )
25 | XCTAssertEqual(operation.compute(), operation.expectedResult)
26 | }
27 |
28 | func testSimpleProductWithParentheses() {
29 | let a = Int16.random()
30 | let b = Int16.random()
31 | let operation = Operation(
32 | String(format: Formula.ProductTests.simpleProductWithParentheses, a, b),
33 | expectedResult: Double(a) * Double(b)
34 | )
35 | XCTAssertEqual(operation.compute(), operation.expectedResult)
36 | }
37 |
38 | func testComplicatedAdditionWithSingleParenthesis() {
39 | let a = Int16.random()
40 | let b = Int16.random()
41 | let c = Int16.random()
42 | let d = Int16.random()
43 | let operation = Operation(
44 | String(format: Formula.ProductTests.complicatedProductWithSingleParenthesis, a, b, c, d),
45 | expectedResult: Double(a) * Double(b) * Double(c) * Double(d)
46 | )
47 | XCTAssertEqual(operation.compute(), operation.expectedResult)
48 | }
49 |
50 | func testComplicatedProductWithMultipleParenthesis() {
51 | let a = Int16.random()
52 | let b = Int16.random()
53 | let c = Int16.random()
54 | let d = Int16.random()
55 | let operation = Operation(
56 | String(format: Formula.ProductTests.complicatedProductWithMultipleParenthesis, a, b, c, d),
57 | expectedResult: Double(a) * Double(b) * Double(c) * Double(d)
58 | )
59 | XCTAssertEqual(operation.compute(), operation.expectedResult)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q, +) group tests/AbelianAdditiveGroupAxiomsTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class AbelianAdditiveGroupAxiomsTests: XCTestCase {
7 | func testNeutralElement() throws {
8 | let a = Int16.random()
9 | let neutralElement: Int16 = .zero
10 |
11 | let leftNeutralExpression = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.neutralElement, neutralElement, a))
12 | let rightNeutralExpression = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.neutralElement, a, neutralElement))
13 | let expectedResult = Double(a)
14 |
15 | XCTAssertEqual(leftNeutralExpression.evaluate(), rightNeutralExpression.evaluate())
16 | XCTAssertEqual(leftNeutralExpression.evaluate(), expectedResult)
17 | }
18 |
19 | func testInverse() throws {
20 | let a = Int16.random()
21 |
22 | let inverseExpression = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.inverseElement, a))
23 | let leftSumWithInverse = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.leftInverse, a, a))
24 | let rightSumWithInverse = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.rightInverse, a, a))
25 | let expectedResult = Double(a).negative
26 |
27 | XCTAssertEqual(inverseExpression.evaluate(), expectedResult)
28 | XCTAssertEqual(leftSumWithInverse.evaluate(), rightSumWithInverse.evaluate())
29 | XCTAssertEqual(leftSumWithInverse.evaluate(), .zero)
30 | }
31 |
32 | func testAssociativity() throws {
33 | let a = Int16.random()
34 | let b = Int16.random()
35 | let c = Int16.random()
36 |
37 | let leftAssociativityExpression = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.leftAssociativity, a, b, c))
38 | let rightAssociativityExpression = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.rightAssociativity, a, b, c))
39 | let expectedResult = Double(a) + Double(b) + Double(c)
40 |
41 | XCTAssertEqual(leftAssociativityExpression.evaluate(), rightAssociativityExpression.evaluate())
42 | XCTAssertEqual(leftAssociativityExpression.evaluate(), expectedResult)
43 | }
44 |
45 | func testCommutativity() throws {
46 | let a = Int16.random()
47 | let b = Int16.random()
48 |
49 | let firstExpression = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.commutativity, a, b))
50 | let secondExpression = try MathExpression(String(format: Formula.AbelianAdditiveGroupAxiomsTests.commutativity, b, a))
51 | let expectedResult = Double(a) + Double(b)
52 |
53 | XCTAssertEqual(firstExpression.evaluate(), secondExpression.evaluate())
54 | XCTAssertEqual(firstExpression.evaluate(), expectedResult)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q, +) group tests/AdditionAndSubtractionTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class AdditionAndSubtractionTests: XCTestCase {
7 | func testSimpleOperation() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 | let c = Int16.random()
11 | let operation = Operation(
12 | String(format: Formula.AdditionAndSubtractionTests.simpleOperation, a, b, c),
13 | expectedResult: Double(a) - Double(b) + Double(c)
14 | )
15 | XCTAssertEqual(operation.compute(), operation.expectedResult)
16 | }
17 |
18 | func testSimpleOperationWithParentheses() {
19 | let a = Int16.random()
20 | let b = Int16.random()
21 | let c = Int16.random()
22 | let operation = Operation(
23 | String(format: Formula.AdditionAndSubtractionTests.simpleOperationWithParentheses, a, b, c),
24 | expectedResult: Double(a) + Double(b) - Double(c)
25 | )
26 | XCTAssertEqual(operation.compute(), operation.expectedResult)
27 | }
28 |
29 | func testSimpleOperationWithParenthesesAndInitialMinus() {
30 | let a = Int16.random()
31 | let b = Int16.random()
32 | let c = Int16.random()
33 | let d = Int16.random()
34 | let operation = Operation(
35 | String(format: Formula.AdditionAndSubtractionTests.simpleOperationWithParenthesesAndInitialMinus, a, b, c, d),
36 | expectedResult: (Double(a) - Double(b)).negative - Double(c) + Double(d)
37 | )
38 | XCTAssertEqual(operation.compute(), operation.expectedResult)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q, +) group tests/AdditionTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class AdditionTests: XCTestCase {
7 | func testSimpleAddition() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 | let operation = Operation(
11 | String(format: Formula.AdditionTests.simpleAddition, a, b),
12 | expectedResult: Double(a) + Double(b)
13 | )
14 | XCTAssertEqual(operation.compute(), operation.expectedResult)
15 | }
16 |
17 | func testComplicatedAddition() {
18 | let a = Int16.random()
19 | let b = Int16.random()
20 | let c = Int16.random()
21 | let d = Int16.random()
22 | let operation = Operation(
23 | String(format: Formula.AdditionTests.complicatedAddition, a, b, c, d),
24 | expectedResult: Double(a) + Double(b) + Double(c) + Double(d)
25 | )
26 | XCTAssertEqual(operation.compute(), operation.expectedResult)
27 | }
28 |
29 | func testSimpleAdditionWithParentheses() {
30 | let a = Int16.random()
31 | let b = Int16.random()
32 | let operation = Operation(
33 | String(format: Formula.AdditionTests.simpleAdditionWithParentheses, a, b),
34 | expectedResult: Double(a) + Double(b)
35 | )
36 | XCTAssertEqual(operation.compute(), operation.expectedResult)
37 | }
38 |
39 | func testComplicatedAdditionWithSingleParenthesis() {
40 | let a = Int16.random()
41 | let b = Int16.random()
42 | let c = Int16.random()
43 | let d = Int16.random()
44 | let operation = Operation(
45 | String(format: Formula.AdditionTests.complicatedAdditionWithSingleParenthesis, a, b, c, d),
46 | expectedResult: Double(a) + Double(b) + Double(c) + Double(d)
47 | )
48 | XCTAssertEqual(operation.compute(), operation.expectedResult)
49 | }
50 |
51 | func testComplicatedAdditionWithMultipleParenthesis() {
52 | let a = Int16.random()
53 | let b = Int16.random()
54 | let c = Int16.random()
55 | let d = Int16.random()
56 | let e = Int16.random()
57 | let f = Int16.random()
58 | let operation = Operation(
59 | String(format: Formula.AdditionTests.complicatedAdditionWithMultipleParenthesis, a, b, c, d, e, f),
60 | expectedResult: Double(a) + Double(b) + Double(c) + Double(d) + Double(e) + Double(f)
61 | )
62 | XCTAssertEqual(operation.compute(), operation.expectedResult)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q, +) group tests/SubtractionTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class SubtractionTests: XCTestCase {
7 | func testSimpleSubtraction() {
8 | let a = Int16.random()
9 | let b = Int16.random()
10 | let operation = Operation(
11 | String(format: Formula.SubtractionTests.simpleSubtraction, a, b),
12 | expectedResult: Double(a) - Double(b)
13 | )
14 | XCTAssertEqual(operation.compute(), operation.expectedResult)
15 | }
16 |
17 | func testSimpleSubtractionWithParentheses() {
18 | let a = Int16.random()
19 | let b = Int16.random()
20 | let operation = Operation(
21 | String(format: Formula.SubtractionTests.simpleSubtractionWithParentheses, a, b),
22 | expectedResult: Double(a) - Double(b)
23 | )
24 | XCTAssertEqual(operation.compute(), operation.expectedResult)
25 | }
26 |
27 | func testSimpleSubtractionWithParenthesesWithInitialMinus() {
28 | let a = Int16.random()
29 | let b = Int16.random()
30 | let operation = Operation(
31 | String(format: Formula.SubtractionTests.simpleSubtractionWithParenthesesWithInitialMinus, a, b),
32 | expectedResult: Double(a).negative + Double(b)
33 | )
34 | XCTAssertEqual(operation.compute(), operation.expectedResult)
35 | }
36 |
37 | func testComplicatedSubtraction() {
38 | let a = Int16.random()
39 | let b = Int16.random()
40 | let c = Int16.random()
41 | let operation = Operation(
42 | String(format: Formula.SubtractionTests.complicatedSubtraction, a, b, c),
43 | expectedResult: Double(a) - Double(b) - Double(c)
44 | )
45 | XCTAssertEqual(operation.compute(), operation.expectedResult)
46 | }
47 |
48 | func testNegativeSingleNumber() {
49 | let a = Int16.random()
50 | let operation = Operation(
51 | String(format: Formula.SubtractionTests.negativeSingleNumber, a),
52 | expectedResult: Double(a).negative
53 | )
54 | XCTAssertEqual(operation.compute(), operation.expectedResult)
55 | }
56 |
57 | func testNegativeExpression() {
58 | let a = Int16.random()
59 | let b = Int16.random()
60 | let c = Int16.random()
61 | let operation = Operation(
62 | String(format: Formula.SubtractionTests.negativeExpressionWithInitialMinus, a, b, c),
63 | expectedResult: Double(a).negative - Double(b) - Double(c)
64 | )
65 | XCTAssertEqual(operation.compute(), operation.expectedResult)
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q, +, *) field tests/CombinedOperationsTest.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | import XCTest
4 |
5 | class CombinedOperationsTests: XCTestCase {
6 | func testProductOfAddition() {
7 | let a = Int16.random()
8 | let b = Int16.random()
9 | let c = Int16.random()
10 | let d = Int16.random()
11 | let operation = Operation(
12 | String(format: Formula.CombinedOperationsTests.productOfAddition, a, b, c, d),
13 | expectedResult: (Double(a) + Double(b)) * (Double(c) + Double(d))
14 | )
15 | XCTAssertEqual(operation.compute(), operation.expectedResult)
16 | }
17 |
18 | func testSquareOfAddition() {
19 | let a = Int16.random()
20 | let b = Int16.random()
21 | let operation = Operation(
22 | String(format: Formula.CombinedOperationsTests.productOfAddition, a, b, a, b),
23 | expectedResult: pow(Double(a) + Double(b), 2.0)
24 | )
25 | XCTAssertEqual(operation.compute(), operation.expectedResult)
26 | }
27 |
28 | func testProductOfSubtraction() {
29 | let a = Int16.random()
30 | let b = Int16.random()
31 | let c = Int16.random()
32 | let d = Int16.random()
33 | let operation = Operation(
34 | String(format: Formula.CombinedOperationsTests.productOfSubtraction, a, b, c, d),
35 | expectedResult: (Double(a) - Double(b)) * (Double(c) - Double(d))
36 | )
37 | XCTAssertEqual(operation.compute(), operation.expectedResult)
38 | }
39 |
40 | func testSquareOfSubtraction() {
41 | let a = Int16.random()
42 | let b = Int16.random()
43 | let operation = Operation(
44 | String(format: Formula.CombinedOperationsTests.productOfSubtraction, a, b, a, b),
45 | expectedResult: pow(Double(a) - Double(b), 2.0)
46 | )
47 | XCTAssertEqual(operation.compute(), operation.expectedResult)
48 | }
49 |
50 | func testProductOfAdditionWithSubtraction() {
51 | let a = Int16.random()
52 | let b = Int16.random()
53 | let c = Int16.random()
54 | let d = Int16.randomExcluding(c)
55 | let operation = Operation(
56 | String(format: Formula.CombinedOperationsTests.productOfAdditionWithSubtraction, a, b, c, d),
57 | expectedResult: (Double(a) + Double(b)) * (Double(c) - Double(d))
58 | )
59 | XCTAssertEqual(operation.compute(), operation.expectedResult)
60 | }
61 |
62 | func testProductOfAdditionWithSubtractionIdentity() {
63 | let a = Int16.random()
64 | let b = Int16.random()
65 | let operation = Operation(
66 | String(format: Formula.CombinedOperationsTests.productOfAdditionWithSubtraction, a, b, a, b),
67 | expectedResult: pow(Double(a), 2.0) - pow(Double(b), 2.0)
68 | )
69 | XCTAssertEqual(operation.compute(), operation.expectedResult)
70 | }
71 |
72 | func testDivisionOfAdditionWithSubtraction() {
73 | let a = Int16.random()
74 | let b = Int16.random()
75 | let c = Int16.random()
76 | let d = Int16.randomExcluding(c)
77 | let operation = Operation(
78 | String(format: Formula.CombinedOperationsTests.divisionOfAdditionWithSubtraction, a, b, c, d),
79 | expectedResult: (Double(a) + Double(b)) / (Double(c) - Double(d))
80 | )
81 | XCTAssertEqual(operation.compute(), operation.expectedResult)
82 | }
83 |
84 | func testDivisionOfProductOfAdditionsWithProductOfSubtractions() {
85 | let a = Int16.random()
86 | let b = Int16.random()
87 | let c = Int16.random()
88 | let d = Int16.random()
89 | let e = Int16.random()
90 | let f = Int16.randomExcluding(e)
91 | let g = Int16.random()
92 | let h = Int16.randomExcluding(g)
93 | let operation = Operation(
94 | String(format: Formula.CombinedOperationsTests.divisionOfProductOfAdditionsWithProductOfSubtractions, a, b, c, d, e, f, g, h),
95 | expectedResult: (Double(a) + Double(b)) * (Double(c) + Double(d)) / ((Double(e) - Double(f)) * (Double(g) - Double(h)))
96 | )
97 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
98 | }
99 |
100 | func testDivisionOfCrossedProducts() {
101 | let a = Int16.random()
102 | let b = Int16.random()
103 | let c = Int16.random()
104 | let d = Int16.random()
105 | let e = Int16.random()
106 | let f = Int16.random()
107 | let g = Int16.random()
108 | let h = Int16.randomExcluding(g)
109 | let operation = Operation(
110 | String(format: Formula.CombinedOperationsTests.divisionOfCrossedProducts, a, b, c, d, e, f, g, h),
111 | expectedResult: (Double(a) + Double(b)) * (Double(c) - Double(d)) / ((Double(e) + Double(f)) * (Double(g) - Double(h)))
112 | )
113 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
114 | }
115 |
116 | func testAdditionWithProductNoParentheses() {
117 | let a = Int16.random()
118 | let b = Int16.random()
119 | let c = Int16.random()
120 | let operation = Operation(
121 | String(format: Formula.CombinedOperationsTests.additionWithProductNoParentheses, a, b, c),
122 | expectedResult: Double(a) + Double(b) * Double(c)
123 | )
124 | XCTAssertEqual(operation.compute(), operation.expectedResult)
125 | }
126 |
127 | func testAdditionWithDivisionNoParentheses() {
128 | let a = Int16.random()
129 | let b = Int16.random()
130 | let c = Int16.random()
131 | let operation = Operation(
132 | String(format: Formula.CombinedOperationsTests.additionWithDivisionNoParentheses, a, b, c),
133 | expectedResult: Double(a) + Double(b) / Double(c)
134 | )
135 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
136 | }
137 |
138 | func testSubtractionWithProductNoParentheses() {
139 | let a = Int16.random()
140 | let b = Int16.random()
141 | let c = Int16.random()
142 | let operation = Operation(
143 | String(format: Formula.CombinedOperationsTests.subtractionWithProductNoParentheses, a, b, c),
144 | expectedResult: Double(a) * Double(b) - Double(c)
145 | )
146 | XCTAssertEqual(operation.compute(), operation.expectedResult)
147 | }
148 |
149 | func testSubtractionWithDivisionNoParentheses() {
150 | let a = Int16.random()
151 | let b = Int16.random()
152 | let c = Int16.random()
153 | let operation = Operation(
154 | String(format: Formula.CombinedOperationsTests.subtractionWithDivisionNoParentheses, a, b, c),
155 | expectedResult: Double(a) - Double(b) / Double(c)
156 | )
157 | XCTAssertEqual(operation.compute().rounded(toPlaces: 6), operation.expectedResult.rounded(toPlaces: 6))
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/(Q, +, *) field tests/FieldAxiomTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class FieldAxiomTests: XCTestCase {
7 | // We test the distributivity of product over the addition, since the remaining field axioms
8 | // are tested in 'AbelianAdditiveGroupAxiomsTests' and 'AbelianMultiplicativeGroupAxiomTests'.
9 |
10 | func testLeftDistributivityOfProductOverAddition() throws {
11 | let a = Int16.random()
12 | let b = Int16.random()
13 | let c = Int16.random()
14 |
15 | let distributivityFactoredExpression = try MathExpression(String(format: Formula.FieldAxiomTests.leftDistributivityFactored, a, b, c))
16 | let distributivityExpandedExpression = try MathExpression(String(format: Formula.FieldAxiomTests.distributivityExpanded, a, b, a, c))
17 | let expectedResult = Double(a) * Double(b) + Double(a) * Double(c)
18 |
19 | XCTAssertEqual(distributivityFactoredExpression.evaluate(), distributivityExpandedExpression.evaluate())
20 | XCTAssertEqual(distributivityFactoredExpression.evaluate(), expectedResult)
21 | }
22 |
23 | func testRightDistributivityOfProductOverAddition() throws {
24 | let a = Int16.random()
25 | let b = Int16.random()
26 | let c = Int16.random()
27 |
28 | let distributivityFactoredExpression = try MathExpression(String(format: Formula.FieldAxiomTests.rightDistributivityFactored, a, b, c))
29 | let distributivityExpandedExpression = try MathExpression(String(format: Formula.FieldAxiomTests.distributivityExpanded, a, c, b, c))
30 | let expectedResult = Double(a) * Double(c) + Double(b) * Double(c)
31 |
32 | XCTAssertEqual(distributivityFactoredExpression.evaluate(), distributivityExpandedExpression.evaluate())
33 | XCTAssertEqual(distributivityFactoredExpression.evaluate(), expectedResult)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/Non-trivial transformation tests/CountTransformationTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class CountTransformationTests: XCTestCase {
7 | let transformation: (String) -> Double = { Double($0.count) }
8 |
9 | func testProductOfAddition() throws {
10 | let a = Int16.random()
11 | let b = String.random(length: Int.random(in: 5...20))
12 | let c = Int16.random()
13 | let d = String.random(length: Int.random(in: 5...20))
14 |
15 | let formula = String(format: Formula.TransformationTests.productOfAddition, a, b, c, d)
16 | let expression = try MathExpression(formula, transformation: transformation)
17 | let expectedResult = (Double(a) + Double(b.count)) * (Double(c) + Double(d.count))
18 |
19 | XCTAssertEqual(expression.evaluate(), expectedResult)
20 | }
21 |
22 | func testSquareOfAddition() throws {
23 | let a = Int16.random()
24 | let b = String.random(length: Int.random(in: 5...20))
25 |
26 | let formula = String(format: Formula.TransformationTests.productOfAddition, a, b, a, b)
27 | let expression = try MathExpression(formula, transformation: transformation)
28 | let expectedResult = pow(Double(a) + Double(b.count), 2.0)
29 |
30 | XCTAssertEqual(expression.evaluate(), expectedResult)
31 | }
32 |
33 | func testProductOfSubtraction() throws {
34 | let a = Int16.random()
35 | let b = String.random(length: Int.random(in: 5...20))
36 | let c = Int16.random()
37 | let d = String.random(length: Int.random(in: 5...20))
38 |
39 | let formula = String(format: Formula.TransformationTests.productOfSubtraction, a, b, c, d)
40 | let expression = try MathExpression(formula, transformation: transformation)
41 | let expectedResult = (Double(a) - Double(b.count)) * (Double(c) - Double(d.count))
42 |
43 | XCTAssertEqual(expression.evaluate(), expectedResult)
44 | }
45 |
46 | func testSquareOfSubtraction() throws {
47 | let a = Int16.random()
48 | let b = String.random(length: Int.random(in: 5...20))
49 |
50 | let formula = String(format: Formula.TransformationTests.productOfSubtraction, a, b, a, b)
51 | let expression = try MathExpression(formula, transformation: transformation)
52 | let expectedResult = pow(Double(a) - Double(b.count), 2.0)
53 |
54 | XCTAssertEqual(expression.evaluate(), expectedResult)
55 | }
56 |
57 | func testProductOfAdditionWithSubtraction() throws {
58 | let a = Int16.random()
59 | let b = String.random(length: Int.random(in: 5...20))
60 | let c = Int16.random()
61 | let d = String.random(length: Int.random(in: 5...20))
62 |
63 | let formula = String(format: Formula.TransformationTests.productOfAdditionWithSubtraction, a, b, c, d)
64 | let expression = try MathExpression(formula, transformation: transformation)
65 | let expectedResult = (Double(a) + Double(b.count)) * (Double(c) - Double(d.count))
66 |
67 | XCTAssertEqual(expression.evaluate(), expectedResult)
68 | }
69 |
70 | func testProductOfAdditionWithSubtractionIdentity() throws {
71 | let a = Int16.random()
72 | let b = String.random(length: Int.random(in: 5...20))
73 |
74 | let formula = String(format: Formula.TransformationTests.productOfAdditionWithSubtraction, a, b, a, b)
75 | let expression = try MathExpression(formula, transformation: transformation)
76 | let expectedResult = pow(Double(a), 2.0) - pow(Double(b.count), 2.0)
77 |
78 | XCTAssertEqual(expression.evaluate(), expectedResult)
79 | }
80 |
81 | func testDivisionOfAdditionWithSubtraction() throws {
82 | let a = Int16.random()
83 | let b = String.random(length: Int.random(in: 5...20))
84 | let c = Int16.random()
85 | let d = String.random(length: Int.random(in: 5...20))
86 |
87 | let formula = String(format: Formula.TransformationTests.divisionOfAdditionWithSubtraction, a, b, c, d)
88 | let expression = try MathExpression(formula, transformation: transformation)
89 | let expectedResult = (Double(a) + Double(b.count)) / (Double(c) - Double(d.count))
90 |
91 | XCTAssertEqual(expression.evaluate(), expectedResult)
92 | }
93 |
94 | func testDivisionOfProductOfAdditionsWithProductOfSubtractions() throws {
95 | let a = Int16.random()
96 | let b = String.random(length: Int.random(in: 5...20))
97 | let c = Int16.random()
98 | let d = String.random(length: Int.random(in: 5...20))
99 | let e = Int16.random()
100 | let f = String.random(length: Int.random(in: 5...20))
101 | let g = Int16.random()
102 | let h = String.random(length: Int.random(in: 5...20))
103 |
104 | let formula = String(format: Formula.TransformationTests.divisionOfProductOfAdditionsWithProductOfSubtractions, a, b, c, d, e, f, g, h)
105 | let expression = try MathExpression(formula, transformation: transformation)
106 | let expectedResult = (Double(a) + Double(b.count)) * (Double(c) + Double(d.count)) / ((Double(e) - Double(f.count)) * (Double(g) - Double(h.count)))
107 |
108 | XCTAssertEqual(expression.evaluate().rounded(toPlaces: 6), expectedResult.rounded(toPlaces: 6))
109 | }
110 |
111 | func testDivisionOfCrossedProducts() throws {
112 | let a = Int16.random()
113 | let b = String.random(length: Int.random(in: 5...20))
114 | let c = Int16.random()
115 | let d = String.random(length: Int.random(in: 5...20))
116 | let e = Int16.random()
117 | let f = String.random(length: Int.random(in: 5...20))
118 | let g = Int16.random()
119 | let h = String.random(length: Int.random(in: 5...20))
120 |
121 | let formula = String(format: Formula.TransformationTests.divisionOfCrossedProducts, a, b, c, d, e, f, g, h)
122 | let expression = try MathExpression(formula, transformation: transformation)
123 | let expectedResult = (Double(a) + Double(b.count)) * (Double(c) - Double(d.count)) / ((Double(e) + Double(f.count)) * (Double(g) - Double(h.count)))
124 |
125 | XCTAssertEqual(expression.evaluate().rounded(toPlaces: 6), expectedResult.rounded(toPlaces: 6))
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/Non-trivial transformation tests/ExponentialTransformationTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | // These tests intend to show that we can define mathematical functions as the transformation passed to the
7 | // expression... with some drawbacks. The most important fact to remember is that transformations are applied
8 | // LAST, that is, when the string is not a number and there are no operators or parentheses, then the parser
9 | // calls the transformation function.
10 | //
11 | // This means that, depending on the transformation, we can have limitations or even get wrong results.
12 | // In these tests, we use an exponentiation transformation to prove these facts. Due to the order in which
13 | // operations are performed, it only works properly with non-negative bases and non-negative exponents.
14 | //
15 | // Negative bases will always return a negative result, independently of the exponent, since subtractions are processed first.
16 | // Negative exponents must be explicitly expressed as '1 / (a ^ b)', with b >= 0.
17 |
18 | class ExponentialTransformationTests: XCTestCase {
19 | let transformation: (String) -> Double = { string in
20 | let splitString = string.split(separator: "^").map { String($0) }
21 | guard splitString.count == 2,
22 | let base = try? MathExpression(splitString.first ?? ""),
23 | let exponent = try? MathExpression(splitString.last ?? "") else { return .zero }
24 | return pow(base.evaluate(), exponent.evaluate())
25 | }
26 |
27 | // MARK: - Satisfied identities
28 |
29 | func testSimpleAdditionOfExponentials() throws {
30 | let a = Int16.random(in: 0...100)
31 | let b = Int16.random(in: 1...4)
32 | let c = Int16.random(in: 0...8)
33 | let d = Int16.random(in: 1...15)
34 |
35 | let expression = try MathExpression("(\(a) ^ \(b)) + (\(c) ^ \(d))", transformation: transformation)
36 | let expectedResult = pow(Double(a), Double(b)) + pow(Double(c), Double(d))
37 | XCTAssertEqual(expression.evaluate(), expectedResult)
38 | }
39 |
40 | func testAdditionOfExponents() throws {
41 | let a = Int16.random(in: 0...10)
42 | let b = Int16.random(in: 1...7)
43 | let c = Int16.random(in: 1...7)
44 |
45 | let expression = try MathExpression("(\(a)^\(b)) * (\(a)^\(c))", transformation: transformation)
46 | let expandedExpression = try MathExpression("\(a) ^ (\(b) + \(c))", transformation: transformation)
47 | let expectedResult = pow(Double(a), Double(b) + Double(c))
48 | XCTAssertEqual(expression.evaluate(), expandedExpression.evaluate())
49 | XCTAssertEqual(expression.evaluate(), expectedResult)
50 | }
51 |
52 | func testAdditionOfBases() throws {
53 | let a = Int16.random(in: 0...50)
54 | let b = Int16.random(in: 0...50)
55 | let c = Int16.random(in: 1...5)
56 |
57 | let expression = try MathExpression("(\(a) + \(b)) ^ \(c)", transformation: transformation)
58 | let expectedResult = pow(Double(a) + Double(b), Double(c))
59 | XCTAssertEqual(expression.evaluate(), expectedResult)
60 | }
61 |
62 | func testSimpleProductOfBasesExponentials() throws {
63 | let a = Int16.random(in: 0...50)
64 | let b = Int16.random(in: 1...5)
65 | let c = Int16.random(in: 0...10)
66 | let d = Int16.random(in: 1...8)
67 |
68 | let expression = try MathExpression("(\(a)^\(b)) * (\(c)^\(d))", transformation: transformation)
69 | let expectedResult = pow(Double(a), Double(b)) * pow(Double(c), Double(d))
70 | XCTAssertEqual(expression.evaluate(), expectedResult)
71 | }
72 |
73 | func testExponentialOfExponential() throws {
74 | let a = Int16.random(in: 0...10)
75 | let b = Int16.random(in: 0...7)
76 | let c = Int16.random(in: 1...7)
77 |
78 | let expression = try MathExpression("(\(a) ^ \(b)) ^ \(c)", transformation: transformation)
79 | let expandedExpression = try MathExpression("\(a) ^ (\(b) * \(c))", transformation: transformation)
80 | let expectedResult = pow(pow(Double(a), Double(b)), Double(c))
81 | XCTAssertEqual(expression.evaluate(), expandedExpression.evaluate())
82 | XCTAssertEqual(expression.evaluate(), expectedResult)
83 | }
84 |
85 | // MARK: - Non-satisfied idendities
86 |
87 | func testNegativeBaseWithEvenExponentGivesWrongResult() throws {
88 | let a = Int16.random(in: -20...0) - 1
89 | let b = Int16.randomEven(in: 1...6)
90 |
91 | let expression = try MathExpression("\(a)^\(b)", transformation: transformation)
92 | let expectedResult = pow(Double(a), Double(b))
93 | XCTAssertNotEqual(expression.evaluate(), expectedResult)
94 | }
95 |
96 | func testNegativeExponentGivesWrongResult() throws {
97 | let a = Int16.random(in: 0...20)
98 | let b = Int16.random(in: 1...6)
99 |
100 | let expression = try MathExpression("\(a)^(-\(b))", transformation: transformation)
101 | let expandedExpression = try MathExpression("1 / (\(a)^\(b))", transformation: transformation)
102 | let expectedResult = Double(1) / pow(Double(a), Double(b))
103 | XCTAssertNotEqual(expression.evaluate(), expandedExpression.evaluate())
104 | XCTAssertNotEqual(expression.evaluate(), expectedResult)
105 | XCTAssertEqual(expandedExpression.evaluate(), expectedResult)
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/Non-trivial transformation tests/FactorialTransformationTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class FactorialTransformationTests: XCTestCase {
7 | let transformation: (String) -> Double = { string in
8 | guard string.last == "!", let number = Int(string.dropLast()) else { return .zero }
9 | return Double(number.factorial())
10 | }
11 |
12 | func testProductOfAddition() throws {
13 | let a = Int16.random()
14 | let b = Int.random(in: 0...10)
15 | let c = Int16.random()
16 | let d = Int.random(in: 0...10)
17 |
18 | let formula = String(format: Formula.TransformationTests.productOfAddition, a, "\(b)!", c, "\(d)!")
19 | let expression = try MathExpression(formula, transformation: transformation)
20 | let expectedResult = (Double(a) + Double(b.factorial())) * (Double(c) + Double(d.factorial()))
21 |
22 | XCTAssertEqual(expression.evaluate(), expectedResult)
23 | }
24 |
25 | func testSquareOfAddition() throws {
26 | let a = Int16.random()
27 | let b = Int.random(in: 0...10)
28 |
29 | let formula = String(format: Formula.TransformationTests.productOfAddition, a, "\(b)!", a, "\(b)!")
30 | let expression = try MathExpression(formula, transformation: transformation)
31 | let expectedResult = pow(Double(a) + Double(b.factorial()), 2.0)
32 |
33 | XCTAssertEqual(expression.evaluate(), expectedResult)
34 | }
35 |
36 | func testProductOfSubtraction() throws {
37 | let a = Int16.random()
38 | let b = Int.random(in: 0...10)
39 | let c = Int16.random()
40 | let d = Int.random(in: 0...10)
41 |
42 | let formula = String(format: Formula.TransformationTests.productOfSubtraction, a, "\(b)!", c, "\(d)!")
43 | let expression = try MathExpression(formula, transformation: transformation)
44 | let expectedResult = (Double(a) - Double(b.factorial())) * (Double(c) - Double(d.factorial()))
45 |
46 | XCTAssertEqual(expression.evaluate(), expectedResult)
47 | }
48 |
49 | func testSquareOfSubtraction() throws {
50 | let a = Int16.random()
51 | let b = Int.random(in: 0...10)
52 |
53 | let formula = String(format: Formula.TransformationTests.productOfSubtraction, a, "\(b)!", a, "\(b)!")
54 | let expression = try MathExpression(formula, transformation: transformation)
55 | let expectedResult = pow(Double(a) - Double(b.factorial()), 2.0)
56 |
57 | XCTAssertEqual(expression.evaluate(), expectedResult)
58 | }
59 |
60 | func testProductOfAdditionWithSubtraction() throws {
61 | let a = Int16.random()
62 | let b = Int.random(in: 0...10)
63 | let c = Int16.random()
64 | let d = Int.random(in: 0...10)
65 |
66 | let formula = String(format: Formula.TransformationTests.productOfAdditionWithSubtraction, a, "\(b)!", c, "\(d)!")
67 | let expression = try MathExpression(formula, transformation: transformation)
68 | let expectedResult = (Double(a) + Double(b.factorial())) * (Double(c) - Double(d.factorial()))
69 |
70 | XCTAssertEqual(expression.evaluate(), expectedResult)
71 | }
72 |
73 | func testProductOfAdditionWithSubtractionIdentity() throws {
74 | let a = Int16.random()
75 | let b = Int.random(in: 0...10)
76 |
77 | let formula = String(format: Formula.TransformationTests.productOfAdditionWithSubtraction, a, "\(b)!", a, "\(b)!")
78 | let expression = try MathExpression(formula, transformation: transformation)
79 | let expectedResult = pow(Double(a), 2.0) - pow(Double(b.factorial()), 2.0)
80 |
81 | XCTAssertEqual(expression.evaluate(), expectedResult)
82 | }
83 |
84 | func testDivisionOfAdditionWithSubtraction() throws {
85 | let a = Int16.random()
86 | let b = Int.random(in: 0...10)
87 | let c = Int16.random()
88 | let d = Int.random(in: 0...10)
89 |
90 | let formula = String(format: Formula.TransformationTests.divisionOfAdditionWithSubtraction, a, "\(b)!", c, "\(d)!")
91 | let expression = try MathExpression(formula, transformation: transformation)
92 | let expectedResult = (Double(a) + Double(b.factorial())) / (Double(c) - Double(d.factorial()))
93 |
94 | XCTAssertEqual(expression.evaluate(), expectedResult)
95 | }
96 |
97 | func testDivisionOfProductOfAdditionsWithProductOfSubtractions() throws {
98 | let a = Int16.random()
99 | let b = Int.random(in: 0...10)
100 | let c = Int16.random()
101 | let d = Int.random(in: 0...10)
102 | let e = Int16.random()
103 | let f = Int.random(in: 0...10)
104 | let g = Int16.random()
105 | let h = Int.random(in: 0...10)
106 |
107 | let formula = String(format: Formula.TransformationTests.divisionOfProductOfAdditionsWithProductOfSubtractions, a, "\(b)!", c, "\(d)!", e, "\(f)!", g, "\(h)!")
108 | let expression = try MathExpression(formula, transformation: transformation)
109 | let expectedResult = (Double(a) + Double(b.factorial())) * (Double(c) + Double(d.factorial())) / ((Double(e) - Double(f.factorial())) * (Double(g) - Double(h.factorial())))
110 |
111 | XCTAssertEqual(expression.evaluate().rounded(toPlaces: 6), expectedResult.rounded(toPlaces: 6))
112 | }
113 |
114 | func testDivisionOfCrossedProducts() throws {
115 | let a = Int16.random()
116 | let b = Int.random(in: 0...10)
117 | let c = Int16.random()
118 | let d = Int.random(in: 0...10)
119 | let e = Int16.random()
120 | let f = Int.random(in: 0...10)
121 | let g = Int16.random()
122 | let h = Int.random(in: 0...10)
123 |
124 | let formula = String(format: Formula.TransformationTests.divisionOfCrossedProducts, a, "\(b)!", c, "\(d)!", e, "\(f)!", g, "\(h)!")
125 | let expression = try MathExpression(formula, transformation: transformation)
126 | let expectedResult = (Double(a) + Double(b.factorial())) * (Double(c) - Double(d.factorial())) / ((Double(e) + Double(f.factorial())) * (Double(g) - Double(h.factorial())))
127 |
128 | XCTAssertEqual(expression.evaluate().rounded(toPlaces: 6), expectedResult.rounded(toPlaces: 6))
129 | }
130 | }
131 |
132 | private extension Int {
133 | func factorial() -> Int {
134 | guard self > 1 else { return 1 }
135 | return self * (self - 1).factorial()
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/MathExpressionTests/Source/Validation tests/ValidationTests.swift:
--------------------------------------------------------------------------------
1 | // Copyright © 2019 Pedro Daniel Prieto Martínez. Distributed under MIT License.
2 |
3 | @testable import MathExpression
4 | import XCTest
5 |
6 | class ValidationTests: XCTestCase {
7 | var a: Int16!
8 | var b: Int16!
9 | var c: Int16!
10 | var d: Int16!
11 | var e: Int16!
12 |
13 | override func setUp() {
14 | super.setUp()
15 | a = Int16.random()
16 | b = Int16.random()
17 | c = Int16.random()
18 | d = Int16.random()
19 | e = Int16.randomExcluding(d)
20 | }
21 |
22 | override func tearDown() {
23 | a = nil
24 | b = nil
25 | c = nil
26 | d = nil
27 | e = nil
28 | }
29 |
30 | func testInvalidFormulaThrowsError_emptyExpression() {
31 | assert(try MathExpression(""), throws: MathExpression.ValidationError.emptyExpression)
32 | }
33 |
34 | func testInvalidFormulaThrowsError_unevenBracketNumber() {
35 | let formula = String(format: Formula.ValidationTests.Invalid.unevenBracketNumber, a, b, c, d, e)
36 |
37 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.unevenOpeningClosingBracketNumber)
38 | }
39 |
40 | func testInvalidFormulaThrowsError_misplacedBrackets() {
41 | let formula = String(format: Formula.ValidationTests.Invalid.misplacedBrackets, a, b, c, d, e)
42 |
43 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.misplacedBrackets)
44 | }
45 |
46 | func testInvalidFormulaThrowsError_misplacedSumAndClosingBracket() {
47 | let formula = String(format: Formula.ValidationTests.Invalid.misplacedSumAndClosingBracket, a, b, c, d, e)
48 |
49 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.endsWithOperator("+"))
50 | }
51 |
52 | func testInvalidFormulaThrowsError_misplacedSubtractionAndClosingBracket() {
53 | let formula = String(format: Formula.ValidationTests.Invalid.misplacedSubtractionAndClosingBracket, a, b, c, d, e)
54 |
55 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.endsWithOperator("-"))
56 | }
57 |
58 | func testInvalidFormulaThrowsError_misplacedProductAndClosingBracket() {
59 | let formula = String(format: Formula.ValidationTests.Invalid.misplacedProductAndClosingBracket, a, b, c, d, e)
60 |
61 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.endsWithOperator("*"))
62 | }
63 |
64 | func testInvalidFormulaThrowsError_misplacedDivisionAndClosingBracket() {
65 | let formula = String(format: Formula.ValidationTests.Invalid.misplacedDivisionAndClosingBracket, a, b, c, d, e)
66 |
67 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.endsWithOperator("/"))
68 | }
69 |
70 | func testInvalidFormulaThrowsError_misplacedOpeningBracketAndProduct() {
71 | let formula = String(format: Formula.ValidationTests.Invalid.misplacedOpeningBracketAndProduct, a, b, c, d, e)
72 |
73 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.startsWithNonSumOrSubtractionOperator("*"))
74 | }
75 |
76 | func testInvalidFormulaThrowsError_misplacedOpeningBracketAndDivision() {
77 | let formula = String(format: Formula.ValidationTests.Invalid.misplacedOpeningBracketAndDivision, a, b, c, d, e)
78 |
79 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.startsWithNonSumOrSubtractionOperator("/"))
80 | }
81 |
82 | func testInvalidFormulaThrowsError_productFollowedByDivision() {
83 | let formula = String(format: Formula.ValidationTests.Invalid.productFollowedByDivision, a, b, c, d, e)
84 |
85 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.invalidConsecutiveOperators("*/"))
86 | }
87 |
88 | func testInvalidFormulaThrowsError_divisionFollowedByProduct() {
89 | let formula = String(format: Formula.ValidationTests.Invalid.divisionFollowedByProduct, a, b, c, d, e)
90 |
91 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.invalidConsecutiveOperators("/*"))
92 | }
93 |
94 | func testInvalidFormulaThrowsError_consecutiveProducts() {
95 | let formula = String(format: Formula.ValidationTests.Invalid.consecutiveProducts, a, b, c, d, e)
96 |
97 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.invalidConsecutiveOperators("**"))
98 | }
99 |
100 | func testInvalidFormulaThrowsError_consecutiveDivisions() {
101 | let formula = String(format: Formula.ValidationTests.Invalid.consecutiveDivisions, a, b, c, d, e)
102 |
103 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.invalidConsecutiveOperators("//"))
104 | }
105 |
106 | func testInvalidFormulaThrowsError_sumFollowedByProduct() {
107 | let formula = String(format: Formula.ValidationTests.Invalid.sumFollowedByProduct, a, b, c, d, e)
108 |
109 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.invalidConsecutiveOperators("+*"))
110 | }
111 |
112 | func testInvalidFormulaThrowsError_sumFollowedByDivision() {
113 | let formula = String(format: Formula.ValidationTests.Invalid.sumFollowedByDivision, a, b, c, d, e)
114 |
115 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.invalidConsecutiveOperators("+/"))
116 | }
117 |
118 | func testInvalidFormulaThrowsError_subtractionFollowedByProduct() {
119 | let formula = String(format: Formula.ValidationTests.Invalid.subtractionFollowedByProduct, a, b, c, d, e)
120 |
121 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.invalidConsecutiveOperators("-*"))
122 | }
123 |
124 | func testInvalidFormulaThrowsError_subtractionFollowedByDivision() {
125 | let formula = String(format: Formula.ValidationTests.Invalid.subtractionFollowedByDivision, a, b, c, d, e)
126 |
127 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.invalidConsecutiveOperators("-/"))
128 | }
129 |
130 | func testInvalidFormulaThrowsError_startsWithProduct() {
131 | let formula = String(format: Formula.ValidationTests.Invalid.startsWithProduct, a, b, c, d, e)
132 |
133 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.startsWithNonSumOrSubtractionOperator("*"))
134 | }
135 |
136 | func testInvalidFormulaThrowsError_startsWithDivision() {
137 | let formula = String(format: Formula.ValidationTests.Invalid.startsWithDivision, a, b, c, d, e)
138 |
139 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.startsWithNonSumOrSubtractionOperator("/"))
140 | }
141 |
142 | func testInvalidFormulaThrowsError_endsWithSum() {
143 | let formula = String(format: Formula.ValidationTests.Invalid.endsWithSum, a, b, c, d, e)
144 |
145 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.endsWithOperator("+"))
146 | }
147 |
148 | func testInvalidFormulaThrowsError_endsWithSubtraction() {
149 | let formula = String(format: Formula.ValidationTests.Invalid.endsWithSubtraction, a, b, c, d, e)
150 |
151 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.endsWithOperator("-"))
152 | }
153 |
154 | func testInvalidFormulaThrowsError_endsWithProduct() {
155 | let formula = String(format: Formula.ValidationTests.Invalid.endsWithProduct, a, b, c, d, e)
156 |
157 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.endsWithOperator("*"))
158 | }
159 |
160 | func testInvalidFormulaThrowsError_endsWithDivision() {
161 | let formula = String(format: Formula.ValidationTests.Invalid.endsWithDivision, a, b, c, d, e)
162 |
163 | assert(try MathExpression(formula), throws: MathExpression.ValidationError.endsWithOperator("/"))
164 | }
165 |
166 | func testValidFormulaNotThrowingError_generic() {
167 | let formula = String(format: Formula.ValidationTests.Valid.generic, a, b, c, d, e)
168 |
169 | XCTAssertNoThrow(try MathExpression(formula))
170 | }
171 |
172 | func testValidFormulaNotThrowingError_consecutiveSubtractions() {
173 | let formula = String(format: Formula.ValidationTests.Valid.consecutiveSubtractions, a, b, c, d, e)
174 |
175 | XCTAssertNoThrow(try MathExpression(formula))
176 | }
177 |
178 | func testValidFormulaNotThrowingError_consecutiveSums() {
179 | let formula = String(format: Formula.ValidationTests.Valid.consecutiveSums, a, b, c, d, e)
180 |
181 | XCTAssertNoThrow(try MathExpression(formula))
182 | }
183 |
184 | func testValidFormulaNotThrowingError_consecutiveSumAndSubtraction() {
185 | let formula = String(format: Formula.ValidationTests.Valid.consecutiveSumAndSubtraction, a, b, c, d, e)
186 |
187 | XCTAssertNoThrow(try MathExpression(formula))
188 | }
189 |
190 | func testValidFormulaNotThrowingError_consecutiveSubtractionAndSum() {
191 | let formula = String(format: Formula.ValidationTests.Valid.consecutiveSubtractionAndSum, a, b, c, d, e)
192 |
193 | XCTAssertNoThrow(try MathExpression(formula))
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | import PackageDescription
3 |
4 | let package = Package(
5 | name: "MathExpression",
6 | platforms: [
7 | .macOS(.v10_14), .iOS(.v12), .tvOS(.v12)
8 | ],
9 | products: [
10 | .library(name: "MathExpression", targets: ["MathExpression"]),
11 | ],
12 | targets: [
13 | .target(name: "MathExpression", path: "MathExpression/Source"),
14 | ]
15 | )
16 |
--------------------------------------------------------------------------------
/fastlane/Fastfile:
--------------------------------------------------------------------------------
1 | default_platform(:ios)
2 |
3 | ####################################################################################################
4 | ####################################################################################################
5 |
6 | ###################
7 | # Deployment lane #
8 | ###################
9 |
10 | desc "Deploys the podspec file to Trunk"
11 | desc "Usage example: fastlane deploy_pod"
12 | lane :deploy_pod do
13 | pod_push(
14 | path: "MathExpression.podspec",
15 | verbose: false,
16 | swift_version: "5.0"
17 | )
18 | end
19 |
20 | ##############
21 | # Test lanes #
22 | ##############
23 |
24 | platform :ios do
25 | desc "Runs framework's unit tests in the specified device."
26 | desc "Usage example: fastlane ios test device:'iPhone 8'"
27 | lane :test do |options|
28 | raise "Missing 'device' parameter. Usage: fastlane ios test device:DEVICE" unless options[:device]
29 | scan(
30 | scheme: "MathExpression-iOS",
31 | device: options[:device],
32 | clean: true,
33 | disable_concurrent_testing: true
34 | )
35 | end
36 |
37 | lane :test_performance do |options|
38 | raise "Missing 'device' parameter. Usage: fastlane ios test_performance device:DEVICE" unless options[:device]
39 | scan(
40 | scheme: "PerformanceTests",
41 | device: options[:device],
42 | clean: true,
43 | disable_concurrent_testing: true
44 | )
45 | end
46 | end
47 |
48 |
49 | ###############
50 | # Build lanes #
51 | ###############
52 |
53 | desc "Builds the framework for the specified platform (either 'iOS', 'tvOS' or 'macOS')."
54 | desc "This lane is to make sure that all platforms build correctly and there are no breaking changes. No tests are executed."
55 | desc "Usage example: fastlane build_framework platform:'iOS'"
56 | lane :build_framework do |options|
57 | raise "Missing 'platform' parameter. Usage: fastlane build_framework platform:PLATFORM" unless options[:platform]
58 | platform = options[:platform]
59 | xcbuild(
60 | scheme: "MathExpression-" + "#{platform}",
61 | clean: true
62 | )
63 | end
64 |
65 | desc "Builds the example app for the specified iOS version."
66 | desc "This lane is to make sure that the example app builds correctly and that breaking API changes are detected before deployment."
67 | desc "Usage example: fastlane build_example_app ios_version:'12.4'"
68 | lane :build_example_app do |options|
69 | raise "Missing 'ios_version' parameter. Usage: fastlane build_example_app ios_version:IOS_VERSION" unless options[:ios_version]
70 | ios_version = options[:ios_version]
71 | xcbuild(
72 | scheme: "MathExpressionExample",
73 | sdk: "iphonesimulator" + "#{ios_version}",
74 | clean: true
75 | )
76 | end
77 |
--------------------------------------------------------------------------------
/fastlane/README.md:
--------------------------------------------------------------------------------
1 | fastlane documentation
2 | ================
3 | # Installation
4 |
5 | Make sure you have the latest version of the Xcode command line tools installed:
6 |
7 | ```
8 | xcode-select --install
9 | ```
10 |
11 | Install _fastlane_ using
12 | ```
13 | [sudo] gem install fastlane -NV
14 | ```
15 | or alternatively using `brew cask install fastlane`
16 |
17 | # Available Actions
18 | ### deploy_pod
19 | ```
20 | fastlane deploy_pod
21 | ```
22 | Deploys the podspec file to Trunk
23 | ### build_framework
24 | ```
25 | fastlane build_framework
26 | ```
27 | Builds the framework for the specified platform (either 'iOS', 'tvOS' or 'macOS').
28 |
29 | This lane is to make sure that all platforms build correctly and there are no breaking changes. No tests are executed.
30 |
31 | Usage example: fastlane build_framework platform:'iOS'
32 | ### build_example_app
33 | ```
34 | fastlane build_example_app
35 | ```
36 | Builds the example app for the specified iOS version.
37 |
38 | This lane is to make sure that the example app builds correctly and that breaking API changes are detected before deployment.
39 |
40 | Usage example: fastlane build_example_app ios_version:'12.4'
41 |
42 | ----
43 |
44 | ## iOS
45 | ### ios test
46 | ```
47 | fastlane ios test
48 | ```
49 | Runs framework's unit tests in the specified device.
50 |
51 | Usage example: fastlane ios test device:'iPhone 8'
52 | ### ios test_performance
53 | ```
54 | fastlane ios test_performance
55 | ```
56 |
57 |
58 | ----
59 |
60 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
61 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
62 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
63 |
--------------------------------------------------------------------------------