package scientific.numbers
import std.math.abs as mabs
import std.math.sqrt as msqrt
import std.math.pow as mpow
import std.math.log as mlog
import std.math.log2 as mlog2
import std.math.log10 as mlog10
import std.math.exp as mexp
import std.math.Number
import std.unittest.*
import std.unittest.testmacro.*
import scientific.utils.AssertionException
import scientific.utils.assertEqual
foreign func malloc(size: UIntNative): CPointer<Unit>
foreign func free(ptr: CPointer<Unit>): Unit
foreign func c_readInt8(ptr: CPointer<Unit>, offset: UIntNative): Int8
foreign func c_readInt16(ptr: CPointer<Unit>, offset: UIntNative): Int16
foreign func c_readInt32(ptr: CPointer<Unit>, offset: UIntNative): Int32
foreign func c_readInt64(ptr: CPointer<Unit>, offset: UIntNative): Int64
foreign func c_readUInt8(ptr: CPointer<Unit>, offset: UIntNative): UInt8
foreign func c_readUInt16(ptr: CPointer<Unit>, offset: UIntNative): UInt16
foreign func c_readUInt32(ptr: CPointer<Unit>, offset: UIntNative): UInt32
foreign func c_readUInt64(ptr: CPointer<Unit>, offset: UIntNative): UInt64
foreign func c_readFloat32(ptr: CPointer<Unit>, offset: UIntNative): Float32
foreign func c_readFloat64(ptr: CPointer<Unit>, offset: UIntNative): Float64
foreign func c_writeInt8(ptr: CPointer<Unit>, val: Int8, offset: UIntNative): Unit
foreign func c_writeInt16(ptr: CPointer<Unit>, val: Int16, offset: UIntNative): Unit
foreign func c_writeInt32(ptr: CPointer<Unit>, val: Int32, offset: UIntNative): Unit
foreign func c_writeInt64(ptr: CPointer<Unit>, val: Int64, offset: UIntNative): Unit
foreign func c_writeUInt8(ptr: CPointer<Unit>, val: UInt8, offset: UIntNative): Unit
foreign func c_writeUInt16(ptr: CPointer<Unit>, val: UInt16, offset: UIntNative): Unit
foreign func c_writeUInt32(ptr: CPointer<Unit>, val: UInt32, offset: UIntNative): Unit
foreign func c_writeUInt64(ptr: CPointer<Unit>, val: UInt64, offset: UIntNative): Unit
foreign func c_writeFloat32(ptr: CPointer<Unit>, val: Float32, offset: UIntNative): Unit
foreign func c_writeFloat64(ptr: CPointer<Unit>, val: Float64, offset: UIntNative): Unit
public interface CNumber<T> <: Number<T> & ToString where T <: Equatable<T> {
static func fromInt(n: Int64): T
static func getSize(): Int64
static func getType(): String
static func read(ptr: CPointer<Unit>, offset: Int64): T
static func write(ptr: CPointer<Unit>, val: T, offset: Int64): Unit
}
public interface Real<T> <: CNumber<T> where T <: Equatable<T> {
static func abs(n: T): T
func toFloat64(): Float64
}
public func abs<T>(a: T): T where T <: Real<T> {
return T.abs(a)
}
public interface CInteger<T> <: Real<T> where T <: Equatable<T> {
}
public interface UnsignedInteger<T> <: CInteger<T> where T <: Equatable<T> {
}
public interface SignedInteger<T> <: CInteger<T> where T <: Equatable<T> {
}
extend Int8 <: SignedInteger<Int8> {
public static func fromInt(n: Int64): Int8 {
return Int8(n)
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func getSize(): Int64 {
return 1
}
public static func getType(): String {
return "Int8"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): Int8 {
return unsafe { c_readInt8(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: Int8, offset: Int64): Unit {
unsafe { c_writeInt8(ptr, val, UIntNative(offset)) }
}
public static func abs(n: Int8): Int8 {
return mabs(n)
}
}
extend Int16 <: SignedInteger<Int16> {
public static func fromInt(n:Int64): Int16 {
return Int16(n)
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func getSize(): Int64 {
return 2
}
public static func getType(): String {
return "Int16"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): Int16 {
return unsafe { c_readInt16(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: Int16, offset: Int64): Unit {
unsafe { c_writeInt16(ptr, val, UIntNative(offset)) }
}
public static func abs(n: Int16): Int16 {
return mabs(n)
}
}
extend Int32 <: SignedInteger<Int32> {
public static func fromInt(n:Int64): Int32 {
return Int32(n)
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func getSize(): Int64 {
return 4
}
public static func getType(): String {
return "Int32"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): Int32 {
return unsafe { c_readInt32(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: Int32, offset: Int64): Unit {
unsafe { c_writeInt32(ptr, val, UIntNative(offset)) }
}
public static func abs(n: Int32): Int32 {
return mabs(n)
}
}
extend Int64 <: SignedInteger<Int64> {
public static func fromInt(n:Int64): Int64 {
return n
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func getSize(): Int64 {
return 8
}
public static func getType(): String {
return "Int64"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): Int64 {
return unsafe { c_readInt64(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: Int64, offset: Int64): Unit {
unsafe { c_writeInt64(ptr, val, UIntNative(offset)) }
}
public static func abs(n: Int64): Int64 {
return mabs(n)
}
}
extend UInt8 <: UnsignedInteger<UInt8> {
public static func fromInt(n: Int64): UInt8 {
return UInt8(n)
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func getSize(): Int64 {
return 1
}
public static func getType(): String {
return "UInt8"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): UInt8 {
return unsafe { c_readUInt8(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: UInt8, offset: Int64): Unit {
unsafe { c_writeUInt8(ptr, val, UIntNative(offset)) }
}
public static func abs(n: UInt8): UInt8 {
return n
}
}
extend UInt16 <: UnsignedInteger<UInt16> {
public static func fromInt(n:Int64): UInt16 {
return UInt16(n)
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func getSize(): Int64 {
return 2
}
public static func getType(): String {
return "UInt16"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): UInt16 {
return unsafe { c_readUInt16(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: UInt16, offset: Int64): Unit {
unsafe { c_writeUInt16(ptr, val, UIntNative(offset)) }
}
public static func abs(n: UInt16): UInt16 {
return n
}
}
extend UInt32 <: UnsignedInteger<UInt32> {
public static func fromInt(n:Int64): UInt32 {
return UInt32(n)
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func getSize(): Int64 {
return 4
}
public static func getType(): String {
return "UInt32"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): UInt32 {
return unsafe { c_readUInt32(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: UInt32, offset: Int64): Unit {
unsafe { c_writeUInt32(ptr, val, UIntNative(offset)) }
}
public static func abs(n: UInt32): UInt32 {
return n
}
}
extend UInt64 <: SignedInteger<UInt64> {
public static func fromInt(n:Int64): UInt64 {
return UInt64(n)
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func getSize(): Int64 {
return 8
}
public static func getType(): String {
return "UInt64"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): UInt64 {
return unsafe { c_readUInt64(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: UInt64, offset: Int64): Unit {
unsafe { c_writeUInt64(ptr, val, UIntNative(offset)) }
}
public static func abs(n: UInt64): UInt64 {
return n
}
}
public interface Float<T> <: Real<T> where T <: Equatable<T> {
static func fromFloat(a: Float64): T
static func sqrt(n: T): T
static func power(base: T, exponent: T): T
static func log(a: T): T
static func log2(a: T): T
static func log10(a: T): T
static func exp(a: T): T
static func eps(): T
operator func /(right: T): T
}
public func sqrt<T>(a: T): T where T <: Float<T> {
return T.sqrt(a)
}
public func power<T>(base: T, exponent: T): T where T <: Float<T> {
return T.power(base, exponent)
}
public func log<T>(a: T): T where T <: Float<T> {
return T.log(a)
}
public func log2<T>(a: T): T where T <: Float<T> {
return T.log2(a)
}
public func log10<T>(a: T): T where T <: Float<T> {
return T.log10(a)
}
public func exp<T>(a: T): T where T <: Float<T> {
return T.exp(a)
}
extend Float32 <: CNumber<Float32> {
public static func fromInt(n: Int64): Float32 {
return Float32(n)
}
public static func getSize(): Int64 {
return 4
}
public static func getType(): String {
return "Float32"
}
public static func read(ptr: CPointer<Unit>, offset: Int64): Float32 {
return unsafe { c_readFloat32(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: Float32, offset: Int64): Unit {
unsafe { c_writeFloat32(ptr, val, UIntNative(offset)) }
}
}
extend Float32 <: Real<Float32> {
public static func abs(a: Float32): Float32 {
return mabs(a)
}
public func toFloat64(): Float64 {
return Float64(this)
}
}
extend Float32 <: Float<Float32> {
public static func fromFloat(a: Float64): Float32 {
return Float32(a)
}
public static func sqrt(a: Float32): Float32 {
return msqrt(a)
}
public static func power(base: Float32, exponent: Float32): Float32 {
return mpow(base, exponent)
}
public static func log(a: Float32): Float32 {
return mlog(a)
}
public static func log2(a: Float32): Float32 {
return mlog2(a)
}
public static func log10(a: Float32): Float32 {
return mlog10(a)
}
public static func exp(a: Float32): Float32 {
return mexp(a)
}
public static func eps(): Float32 {
return Float32(1.1920929e-07)
}
}
extend Float64 <: Float<Float64> {
public static func fromInt(n: Int64): Float64 {
return Float64(n)
}
public func toFloat64(): Float64 {
return Float64(this)
}
public static func fromFloat(a: Float64): Float64 {
return a
}
public static func read(ptr: CPointer<Unit>, offset: Int64): Float64 {
return unsafe { c_readFloat64(ptr, UIntNative(offset)) }
}
public static func write(ptr: CPointer<Unit>, val: Float64, offset: Int64): Unit {
unsafe { c_writeFloat64(ptr, val, UIntNative(offset)) }
}
public static func abs(a: Float64): Float64 {
return mabs(a)
}
public static func sqrt(a: Float64): Float64 {
return msqrt(a)
}
public static func power(base: Float64, exponent: Float64): Float64 {
return mpow(base, exponent)
}
public static func log(a: Float64): Float64 {
return mlog(a)
}
public static func log2(a: Float64): Float64 {
return mlog2(a)
}
public static func log10(a: Float64): Float64 {
return mlog10(a)
}
public static func exp(a: Float64): Float64 {
return mexp(a)
}
public static func eps(): Float64 {
return Float64(2.220446049250313e-16)
}
public static func getSize(): Int64 {
return 8
}
public static func getType(): String {
return "Float64"
}
}
public func approxEqual<T>(a: T, b: T, atol!:Float64 = 1e-7): Bool where T <: Float<T> {
return abs(a - b).toFloat64() < atol
}
public func assertApproxEqual<T>(a: T, b: T, atol!:Float64 = 1e-7): Unit where T <: Float<T> & ToString {
if (approxEqual<T>(a, b, atol:atol)) {
return ()
} else {
print("a = ${a}, b = ${b}\n")
throw AssertionException()
}
}
/* Test function */
func sqrtsum<T>(a: Array<T>): T where T <: Float<T> {
var res = T.fromInt(0)
for (val in a) {
res = res + power(val, T.fromFloat(0.5))
}
return res
}
@Test
public class TestNumbers {
@TestCase
func testNumbers(): Unit {
let a1: Array<Int32> = [1, 2, 3, 4]
let a2: Array<Int64> = [1, 2, 3, 4]
let a3: Array<Float32> = [1.0, 2.0, 3.0, 4.0]
let a4: Array<Float64> = [1.0, 2.0, 3.0, 4.0]
@Assert(approxEqual(sqrtsum(a3), Float32(6.14626), atol:1e-5))
@Assert(approxEqual(sqrtsum(a4), Float64(6.14626), atol:1e-5))
}
}