Arithmosophi is a set of missing protocols that simplify arithmetic and statistics on generic objects or functions.
As Equatable
define the ==
operator , Addable
will define the +
operator.
protocol Addable {
func + (lhs: Self, rhs: Self) -> Self
}
[1, 2, 3, 4].sum // 1 + 2 + 3 + 4
[0, 1, 2, 3, 4].average // 2
[13, 2.4, 3, 4].varianceSample
- As you might guess
Substractable
define-
operator,Multiplicatable
define*
operator, etc..., all defined in Arithmosophi.swift
Take a look at sumOf
function
func sumOf<T where T:Addable, T:Initializable>(input : [T]) -> T {
return reduce(input, T()) {$0 + $1}
}
Array of Int
, Double
and even String
could be passed as argument to this function. Any Addable
objects.
No need to implement a function for Double
, one for Float
, one more for Int
, etc...
sumOf
and productOf
functions are available in Arithmosophi.swift
This framework contains some useful extensions on CollectionType
[1, 2, 3, 4].sum // 1 + 2 + 3 + 4
[1, 2, 3, 4].product // 1 * 2 * 3 * 4
["a","b","c","d"].sum // "abcd" same as joinWithSeparator("")
[["a","b"],["c"],["d"]].sum // ["a","b","c","d"] same as flatMap{$0}
with MesosOros.swift
Computes arithmetic average/mean
[1, 2, 3, 4].average // (1 + 2 + 3 + 4) / 4
A type is Averagable
if it can be dividable by an Int
and define an operator to do that
func /(lhs: Self, rhs: Int) -> Self
All arithmetic type conform to this protocol and you can get an average for a CollectionType
P.S. You can conform to this protocol and Addable
to make a custom average.
with MesosOros.swift
Get the median value from the array
- Returns the average of the two middle values if there is an even number of elements in the
CollectionType
.
[1, 11, 19, 4, -7].median // 4
- Returns the lower of the two middle values if there is an even number of elements in the
CollectionType
.
[1.0, 11, 19.5, 4, 12, -7].medianLow // 4
- Returns the higher of the two middle values if there is an even number of elements in the
CollectionType
.
[1, 11, 19, 4, 12, -7].medianHigh // 11
with Sigma.swift
Computes variance.
- The sample variance: Σ( (Element - average)^2 ) / (count - 1)
[1.0, 11, 19.5, 4, 12, -7].varianceSample
- The population variance: Σ( (Element - average)^2 ) / count
[1.0, 11, 19.5, 4, 12, -7].variancePopulation
with Sigma.swift
Computes standard deviation.
[1.0, 11, 19.5, 4, 12, -7].standardDeviationSample
[[1.0, 11, 19.5, 4, 12, -7].standardDeviationPopulation
with Sigma.swift
Computes skewness.
[1.0, 11, 19.5, 4, 12, -7].skewness // or .moment.skewness
with Sigma.swift
Computes kurtosis.
[1.0, 11, 19.5, 4, 12, -7].kurtosis // or .moment.kurtosis
with Sigma.swift
Computes covariance with another CollectionType
[1, 2, 3.5, 3.7, 8, 12].covarianceSample([0.5, 1, 2.1, 3.4, 3.4, 4])
- population covariance
[1, 2, 3.5, 3.7, 8, 12].covariancePopulation([0.5, 1, 2.1, 3.4, 3.4, 4])
[1, 2, 3.5, 3.7, 8, 12].pearson([0.5, 1, 2.1, 3.4, 3.4, 4])
with Complex.swift
Complex
is a struct of two ArithmeticType
, the real and the imaginary component
var complex = Complex(real: 12, imaginary: 9)
complex = 12 + 9.i
You can apply operation on it (+, -, *, /, ++, --, -)
result = complex + 8 // Complex(real: 20, imaginary: 9)
Complex(real: 12, imaginary: 9) + Complex(real: 8, imaginary: 1)
// Complex(real: 20, imaginary: 10)
The power of this simple arithmetic protocols are released when using operators
If we implement a box object containing a generic T
value
class Box<T> {
var value: T
}
we can define some operators on it, in a generic way, like we can do with Equatable
or Comparable
func +=<T where T:Addable> (inout box: Box<T>, addend: T) {
box.value = box.value + addend
}
func -=<T where T:Substractable> (inout box: Box<T>, addend: T) {
box.value = box.value - addend
}
how to use this operator:
var myInt: Box<Int>(5)
myInt += 37
For a full example, see Prephirence file from Prephirences framework, or sample Box.swift
For optional attribute you can use Initializable
or any protocol which define a way to get a value
class Box<T> {
var value: T?
}
func +=<T where T:Addable, T:Initializable> (inout box: Box<T>, addend: T) {
box.value = (box.value ?? T()) + addend
}
LogicalOperationsType
is a missing protocol for Bool
inspired from BitwiseOperationsType
(or IntegerArithmeticType
)
The purpose is the same, implement functions without knowing the base type
You can for instance implement your own Boolean
enum and implement the protocol
enum Boolean: LogicalOperationsType {case True, False}
func && (left: Boolean, @autoclosure right: () -> Boolean) -> Boolean {
switch left {
case .False: return .False
case .True: return right()
}
}
...
then create only one operator on Box
for Bool
, Boolean
and any LogicalOperationsType
func &&=<T:LogicalOperationsType> (inout box: Box<T>, @autoclosure right: () -> TT) {
box.value = box.value && right()
}
Take a look at a more complex enum Optional which implement also LogicalOperationsType
with Arithmos
(number) & Statheros
(constant)
Arithmos
and Statheros
add respectively functions and mathematical constants for Double
, Float
and CGFloat
, allowing to implement generic functions without taking care of type
func distance<T: Arithmos>(#x: T, y: T) -> T {
return x.hypot(y)
}
func radiansFromDegrees<T where T: Multiplicable, Dividable, T: Arithmos, T: Statheros>(degrees: T) -> T {
return degrees * T.PI / T(180.0)
}
Take a look at Geometry.swift for more examples
Using cocoapods
pod 'Arithmosophi'
Not interested in full framework ? install a subset with:
pod 'Arithmosophi/Core' # Arithmosophi.swift
pod 'Arithmosophi/Logical' # LogicalOperationsType.swift
pod 'Arithmosophi/Complex' # Complex.swift
pod 'Arithmosophi/MesosOros' # MesosOros.swift
pod 'Arithmosophi/Arithmos' # Arithmos.swift
pod 'Arithmosophi/Sigma' # Sigma.swift
pod 'Arithmosophi/Statheros' # Statheros.swift
pod 'Arithmosophi/Samples' # Samples/*.swift (not installed by default)
Add use_frameworks!
to the end of the Podfile
.
In podspec file
s.dependency 'Arithmosophi'
or define only wanted targets
s.dependency 'Arithmosophi/Core'
s.dependency 'Arithmosophi/Logical'
Drag files to your projects