Releases: soulverteam/SoulverCore
3.0.0
Dynamic place queries
-
A new placeDataProvider property on
EngineCustomization
supports natural language functions that include places that are dynamically resolved over the internet. -
Ensure the
dynamicPlaceQueries
flag is enabled onEngineFeatureFlags
to enable this feature (the default is off) -
Dynamic places are supported in natural language functions, for example:
- "time in Ubud, Bali"
- "distance between Bellingen & Nowra"
- "weather in Assisi, Perugia"
- "temperature in Narva, Estonia"
A PlaceDataProvider
implementation asynchronously geocodes a string query. You can optionally also enable distance calculations between by implementing another function.
protocol PlaceDataProvider {
/// Geocode a given string request
/// - For example, this might be a place like "Spoleto, Umbria, Italy"
/// - On Apple platforms, you can use Core Location's `CLGeocoder/geocodeAddressString` class for this
func placeDataFor(request: String) async throws -> PlaceData?
/// Calculate the distance between two GPS locations
/// On Apple platforms, you can use Core Location's `CLLocation/distance(from:)` function to calculate this
/// - This is a synchronous call, because network access is not required to calculate this
/// - Implement this function to enable "distance from (place) to (place)" style word functions
/// - Return a unit expression of length (miles or km, up to you)
func distanceBetween(location1: PlaceData.Coordinates, location2: PlaceData.Coordinates) -> UnitExpression?
}
Historical currency conversions
- The
CurrencyRateProvider
protocol offers a new function to fetch historical currency rates:
func fetchRateInBackgroundFor(request: CurrencyRateRequest) async -> Decimal?
- Get the conversion date from the request's new
date
property and dispatch a query to a web-based data source that supports historical currency conversions (like CurrencyLayer). - The synchronous variant
rateFor(request: CurrencyRateRequest) -> Decimal?
is polled first, to let you return a cached historical rate if available. - Ensure the
historicalCurrencyQueries
flag is enabled onEngineFeatureFlags
to enable this feature (the default is off)
Historical weather queries
WeatherDataRequest
now includes an optionaldatestamp
property.- Ensure the
historicalWeatherQueries
flag is enabled onEngineFeatureFlags
to enable this feature (the default is off)
Alternative Result Generator
- A new
AlternativeResultGenerator
class can intelligently convert a givenCalculationResult
into relevant alternative forms - For example, a decimal number might be converted into hex, binary and octal, or if below zero, into a fraction, and if above a certain threshold, into scientific notation
- A unit of time will be converted into a laptime (hh:mm:ss) and timespan (days, hours, minutes, seconds)
- A date will be converted into ISO8601 format, and a unix timestamp
Date Interval Parsing from String
- A new
dateIntervalFor(_ expression: String) -> DateInterval
onCalculator
offers intelligent date interval (start and end date) parsing that might be used to power a natural language event scheduling feature
Loading engine content from an external bundle
SoulverCore now supports loading its content files (containing places, units & functions, etc) from an externally located bundle directory. Previous versions always loaded resources included in the framework's resource folder.
To load resources from an external source:
- First make a resources bundle
ResourceBundle(url: URL)?
with a URL pointing to your resources bundle. - This initializer is failable to ensure that your resources bundle is valid
- Use the dedicated initializer on
EngineCustomization
that takes a ResourcesBundle:init(resourcesBundle: ResourceBundle, locale: Locale)
Breaking API changes
In connection with allowing an EngineCustomization to be loaded from an external bundle, we've moved away from all singletons in the framework, including CurrencyList
and StaticResources
.
- Functions for getting lists of content primitives (currencies & places) formally provided by these objects are now available on
EngineCustomization
Minor features
- Additional percentage shorthand: "5 of 50" (as a shorthand of "5 as a % of 50")
- Convert time to decimal: "10:45 to decimal" (= 10.75)
- Support for interpreting sub-zero numbers like
0.001
as0,001
in an EU locale (withMisplacedThousandsSeparatorBehavior
set tointerpretAsDecimalPoint
)
Bug fixes
- Fixed a crash when doing
fact(99999.999)
- Fixed a bug with auto converting a conversion in parentheses while using answer auto conversion mode
- Fixed a bug where you couldn't convert rates of fluid oz/acre into mL/ha (because the first rate was being converted into meters)
2.7.1
Time & Date Calculations
- Support for adding timespans with the form hh:mm with more than 24 hours in the hour component
- Added phrases to get
day of month
andweek of month
date components from the current day or a particular date
Bug fixes
- Fixed an issue with the automatic result conversion mode incorrectly invoking with a single phrase function
- Fixed an issue affecting stale variable dependencies in rare cases in
LineCollection
2.7.0
This version of SoulverCore has been built for Swift 6 with Xcode 16
New Functions
Data Transfer Functions
New function makes it easier to work out how long files will take to download or upload.
- "time to 3GB at 10 MB/s" (= 300 s)
- Or specify an explicit unit: "minutes to 35GB at 200MB/s" (= 2.92 min)
Speeding Up Time Functions
New function to speed up time by a multiplier:
- "1 hour 30 minutes at 1.5x" (= 1 hour)
You can also calculate the time saved:
- "time saved 1 hour 30 minutes at 1.5x" (= 30 minutes)
Financial Functions
Added a new function for working out how much money you need in the bank to ensure a certain amount of interest:
- "deposit needed for $42k/year at 7.5%" (= $560k)
- "savings required for $10k/month @ 5.6% (=$2.143M)
Added additional variants for compounding monthly and quarterly:
- "interest on $100 after 3 years at 10% compounding monthly"
- "interest on $100 for 3 years at 10% compounding quarterly"
Percentage functions
New functions to display the percentage change from one number by an amount
- "3k on 50k" (= 6%)
- "50 off 150" (= 33%)
Lunar day
- Get the current lunar day: "lunar day" or "moon day"
- Or the lunar day on a particular date: "lunar day on March 12, 2025"
Units
- Added support for unit expressions with a fractional value, like "1 1/2 pounds"
- Added cubic micrometer & square nanometer units
- Improved behaviour of certain unit multiplications, where they would not return in the most relevant form: "3 min × 15 mph" now returns "0.75 mi" rather than 1,207 m
Currencies
- Polygon symbol (formally MATIC) updated to POL
- Added sub-domination ETH currencies: Wei and Gwei
Conversions
- Support for automatic conversion of units without explicit value: "usd eur" will be interpreted as "1 usd in eur"
- More flexible unit conversion for ostensibly incompatible units: "3 mph to minutes" (convert to miles/minute), "10 cubic centimeters to meters" (convert to cubic meters)
- Support for conversion from & into imperial volume units explicitly, like "1 liter in imperial pints"
Other
General
- Added power phrase functions like: "3 to the power of 9", "4 raised to 5"
- Support for min/max functions with percentages
- Combination and permutation functions now support numerically larger parameters
- Added additional trigonometry functions that take their parameter in degrees: "asind", "acosd", "atand" and "tand"
- Added (sample) standard deviation function: "standard deviation of 10, 20, 30 and 40"
- You can also use "std dev of …" as a shorthand
Time zones
- Support for "Korea" as an alias of "South Korea"
Bug fixes
- Fixed a date parsing issue where a number like "1.003.020" was being misinterpreted as a date ("01.03.20")
- Fixed a lexing issue with feet units that are written with a trailing apostrophe, like 30.334'
- Fixed an issue with min/max list functions and negative currencies
API
Breaking changes
- We're working towards only depending on API available in the modern Swift FoundationEssentials & FoundationInternationalization,
- This version takes the first step in this direction by weaning off uses of
NS
prefixed classes (NSNumber
,NSString
,NSRegularExpression
etc). - For our public API, this change only affects the
range
property onToken
:- It is now typed to a similar
Token.Range
struct, ratherNSRange
. - You can still get an
NSRange
for a token if needed usingtoken.range.nsRange
- It is now typed to a similar
ECBCurrencyRateProvider
now uses swift async/await for rate updates, rather than a closure.
Parsing options
- Added a
DateParsingOptions
toEngineFeatureFlags
that lets you specify the default time for dates without times (either midday or midnight)
Places
- Cites, airports and countries now offer a 2-letter ISO country code property
2.6.3
Percentage functions
- New percentage function: "3 as % with 7, 8 and 9" (= 11.11%)
- This function gets the percentage of one number of a whole (including the number), a shorthand for "3 as % of (3 + 7 + 8 + 9)".
Future date seeking mode
- Compound units of time are now supported in date seeking mode (for example "in 1 hour 20 minutes" will return a date 1 hour and 20 minutes from now)
Automatic conversion mode
- Added a case for nanoseconds to automatically convert into milliseconds
Bug fixes & improvements:
- Fixed a crash introduced in the previous build when doing certain date interval calculations
- Fixed a rare issue with fractions potentially being incorrectly formatted
- Fixed an issue with pitch to hz conversion not enough precision for rounded results in some cases
2.6.2
Improvements
- Support for rates in lists (like "mean of 0.09615
$/kWh and 0.10965 $ /kWh") - The base unit for volume units is now
cubic meters
(rather thanliters
). This enables SoulverCore to return a result of meters for expressions like "200000 L / 125 m^2" - Support for "dec" as a synonym of "decimal"
- Fixed a hang related to using 0 as in a list with the lcm function
- Fixed a bug with the future date seeking mode, for dates in the past that had a specified time of day
2.6.1
Places
- Added new properties
country
andcity
onPlace
. These will return the associated country for aCity
, and the capital city of aCountry
. It will also return the associated city with anAirport
place - Added some additional places: Mauritius, French Guiana, Martinique, La Réunion, & Guadeloupe
Units
- Support for generic custom units (defined not in terms of an existing unit)
- Use
undefined
as yourequivalentUnitIdentifier
Linear growth functions
- Linear growth functions can now omit the starting quantity (it will be assumed to be 0)
- For instance "time to $100 at $4 every hour" (= 25 hours)
Misc
- CSS units (em, rem & px) will now always be formatted using a standard US decimal character (.) so they can be immediately pasted into CSS files.
- Phrases like "days left in 2024" now use round numbers
- Fixed an issue with a spaceless compound quantity of time not being correctly recognized, like "2hr1min"
- You can now convert timespans into "months and days"
2.6.0
Currency symbols
- New
CurrencySymbolSettings
enum onEngineCustomization
- Set to
.automatic
(the default) for the most logical currency symbol settings for the current locale - Added "tenges" as a synonym for Kazakhstani Tenges
Bug fixes
- "tonne" now maps to metric ton, rather than short tons.
- iso8601 time stamps with sub-second components and negative timezone offsets are now recognized correctly.
- Fixed an issue with hex/binary/octal numbers not working with log functions
- Fixed an issue where raising area and volume units to fractional powers would not perform the correct operation
- Fixed an issued where single letter prefix currency symbols were not parsing correctly (like "R100", used in South Africa)
2.5.0
Calendar Calculations
Timespans
- Added support for time spans expressed in two units "in minutes and seconds", "in hours and minutes", "in days and hours", "in weeks and days"
- Support for "as time span" as a synonym for "as timespan"
- Timespans, rather than single time units, are now used after more operations on single quantities of time
- For example, "25 min / 12" = (2 min 5 second), "1h20m + 45m" = (2 hours 5 min)
- We now use fixed time units (weeks, days, minutes, etc), not variable ones (years, months), when "as timespan" is specified for the result
Format dates using format patterns
- For example, "March 12, 2023 as EEEE, MMM d, yyyy" (= Sunday, Mar 12, 2023)
- Or "March 12, 2023 as MM/dd/yy" (= 03/12/23)
- The pattern language is defined by the Unicode Technical Standard #35
- Here is a good cheat sheet (RIP Alex Hay)
Timezone codes
- Added a new
TimezoneCodeCorrectionBehavior
enum that can be used on anEngineFeatureFlags
on anEngineCustomization
- This includes a new option to only "correct" non-summer timezone codes
Other
- Add support for dates with the form "13 March 23" (the year is assumed to be the (20)23, and the day 13)
- Workaround an issue in Foundation's
Calendar
which sometimes interprets "Feb 29" as an invalid date (even in a leap year)
General
Pounds & ounces
- New "Pounds & ounces" output format: "87 ounces" will return "5 lb 7 oz"
- Explicitly convert with "in pounds and ounces" or "in lb oz"
Locales
- Fixed an issue introduced in 2.4.7 where the
.
symbol was not being correctly interpreted as a decimal point in locales like French & Russian, in which this symbol is not defined as either a decimal point or thousands separator - Support for automatic 3 dp of accuracy in currency results, when used in locales like French and Russian: €12,995 - €1,747 = €11,248 (the result is 11, not 11 thousand)
Other
- Support for applying SI scalars (
k
,M
,G
, etc) to sub-expressions (500)k and line references inLineCollection
- With the variable declarations feature flag disabled, expressions that include the
=
symbol will no longer return results
Engine Configuration
EngineFeatureFlags (note: possibly source breaking change)
- The
EngineFeatureFlags
object has been tidied up and certain flags removed EngineFeatureFlags
had become a bit of a catch-all object over time, with many legacy properties. We felt it was time to make it more friendly for newcomer to the framework, and also more ergonomic for everyone- It is unlikely you were using any of the removed feature flags. However, please email us or open a discussion a flag you were depending on is no longer available
Commenting
- Added a new
CommentingOptions
object (set thecommentingOptions
property onLineCollection
) to customize which commenting features you want to support in multi-line calculations Calculator
no longer supports commenting features (they continue to be available onLineCollection
)
2.4.7
Thousands separator as decimal point
- A new property
misplacedThousandsSeparatorBehavior
onEngineFeatureFlags
and can be set to control how numbers with thousands separators in the wrong position should be handled (allow
,disallow
orinterpretAsDecimalPoint
) - With the
interpretAsDecimalPoint
option, Europeans that use,
as a decimal separator can now also use.
in unambiguous cases (like "1,3 + 1.3 = 2,6")
Number formatting
- Crypto-currencies no longer round to 2 dp (they're not regular currencies to exhibit this behaviour)
- Numbers formatted with SI notation now respect the
dp
property inFormattingPreferences
- For Europeans, SoulverCore will now automatically show 3 dp of accuracy in results when 3 dp is also used in a currency expression (for example "€12,995 - €1,747 = €11,248"). Note that this result is not eleven thousand
Calendar calculations
- Support for time interval expressions with year stamps, like
today - 1973
Lists
- Support for subexpressions inside lists, like "max (3+4), 5, 3" (= 7)
Bug fixes
- Workaround an issue in Foundation where NSDecimalPower() can return incorrect results (like for
0.1^1000
) - Fixed an issue with future date seeking mode, when inputing unambiguous dates earlier in the year
2.4.6
Timezones
- Added some missing timezones for some small countries (Brunei, the Maldives, and some others)
- You can use the "Europe" timezone (like "time in Europe"). We fetch the time in Brussels (often considered the capital of Europe)
Sinh function
- Fixed an issue with the
sinh
function incorrectly callingcosh
instead - Fixed an issue where
sinh(10000)
could crash SoulverCore