Skip to content

Getting started in C

Kt-Math's Kotlin/Native build is also a C library: every GitHub release ships it, prebuilt, for the desktop platforms. No Kotlin or Gradle is needed to use it.

Download

Pick the zip of your platform among the assets of the latest release:

Platform Asset Library Header
Linux x64 kt-math-VERSION-linuxX64.zip libktmath.so libktmath_api.h
Linux ARM64 kt-math-VERSION-linuxArm64.zip libktmath.so libktmath_api.h
macOS (Apple Silicon) kt-math-VERSION-macosArm64.zip libktmath.dylib libktmath_api.h
Windows x64 kt-math-VERSION-mingwX64.zip ktmath.dll (plus ktmath.def) ktmath_api.h

Each library is self-contained: the Kotlin runtime is linked into it. On Windows, every libktmath_ prefix below is ktmath_ instead (ktmath_symbols(), ktmath_kref_...), as in the header's name: include ktmath_api.h.

curl -LO https://github.com/gciatto/kt-math/releases/download/VERSION/kt-math-VERSION-linuxX64.zip
unzip kt-math-VERSION-linuxX64.zip -d ktmath

Write a program

The header declares a single function, libktmath_symbols(), returning a structure that mirrors Kotlin's packages and classes: lib->kotlin.root.org.gciatto.kt.math.BigInteger.times(x, y) is Kotlin's x * y. Kotlin objects are handles, to release when no longer needed:

// Every Kotlin object is a handle (a struct holding a pointer) to dispose of with DisposeStablePointer, every returned
// string is to dispose of with DisposeString; overloads get trailing underscores, in declaration order. Kotlin
// exceptions abort the process: the parse*OrNull functions return null handles instead.
#include <stdio.h>
#include <string.h>

#include "libktmath_api.h"

static libktmath_ExportedSymbols *lib;
static int failures = 0;

#define KT_MATH (lib->kotlin.root.org.gciatto.kt.math)
#define DISPOSE(handle) lib->DisposeStablePointer((handle).pinned)

// Checks that a string returned by Kotlin is the expected one, then frees it
static void expect(const char *what, const char *actual, const char *expected) {
    if (strcmp(actual, expected) != 0) {
        printf("FAILED %s: expected %s, got %s\n", what, expected, actual);
        failures++;
    } else {
        printf("ok %s = %s\n", what, actual);
    }
    lib->DisposeString(actual);
}

static void big_integers(libktmath_kref_org_gciatto_kt_math_BigFactory factory) {
    libktmath_kref_org_gciatto_kt_math_BigInteger x =
        KT_MATH.BigFactory.parseBigInteger(factory, "123456789012345678901234567890", 10);
    libktmath_kref_org_gciatto_kt_math_BigInteger y = KT_MATH.BigFactory.bigIntegerOf(factory, 42);
    libktmath_kref_org_gciatto_kt_math_BigInteger product = KT_MATH.BigInteger.times(x, y);
    expect("x * 42", KT_MATH.BigInteger.toString_(product), "5185185138518518513851851851380");
    libktmath_kref_org_gciatto_kt_math_BigInteger quotient = KT_MATH.BigInteger.div(x, y);
    expect("x / 42", KT_MATH.BigInteger.toString_(quotient), "2939447357436801878600823045");
    expect("x in hex", KT_MATH.BigInteger.toString(x, 16), "18ee90ff6c373e0ee4e3f0ad2");
    if (KT_MATH.BigInteger.compare(x, y) <= 0) {
        printf("FAILED compare(x, 42) > 0\n");
        failures++;
    }
    libktmath_kref_org_gciatto_kt_math_BigInteger invalid = KT_MATH.BigFactory.parseBigIntegerOrNull(factory, "12x", 10);
    if (invalid.pinned != NULL) {
        printf("FAILED parseBigIntegerOrNull(\"12x\") is null\n");
        failures++;
    }
    DISPOSE(quotient);
    DISPOSE(product);
    DISPOSE(y);
    DISPOSE(x);
}

static void big_decimals(libktmath_kref_org_gciatto_kt_math_BigFactory factory) {
    libktmath_kref_org_gciatto_kt_math_MathContext_Companion contexts = KT_MATH.MathContext.Companion._instance();
    libktmath_kref_org_gciatto_kt_math_MathContext unlimited = KT_MATH.MathContext.Companion.get_UNLIMITED(contexts);
    libktmath_kref_org_gciatto_kt_math_BigDecimal a = KT_MATH.BigFactory.parseBigDecimal(factory, "1.10", unlimited);
    libktmath_kref_org_gciatto_kt_math_BigDecimal b = KT_MATH.BigFactory.bigDecimalOf(factory, 3, unlimited);
    libktmath_kref_org_gciatto_kt_math_RoundingMode half_even = KT_MATH.RoundingMode.HALF_EVEN.get();
    libktmath_kref_org_gciatto_kt_math_MathContext ten_digits = KT_MATH.MathContext.MathContext(10, half_even);
    libktmath_kref_org_gciatto_kt_math_BigDecimal quotient = KT_MATH.BigDecimal.div_(a, b, ten_digits);
    expect("1.10 / 3", KT_MATH.BigDecimal.toPlainString(quotient), "0.3666666667");
    libktmath_kref_org_gciatto_kt_math_BigDecimal root = KT_MATH.BigDecimal.sqrt(b, ten_digits);
    expect("sqrt(3)", KT_MATH.BigDecimal.toPlainString(root), "1.732050808");
    libktmath_kref_org_gciatto_kt_math_BigDecimal log = KT_MATH.big.log(b, ten_digits); // big-math's functions
    expect("log(3)", KT_MATH.BigDecimal.toPlainString(log), "1.098612289");
    DISPOSE(log);
    DISPOSE(root);
    DISPOSE(quotient);
    DISPOSE(ten_digits);
    DISPOSE(half_even);
    DISPOSE(b);
    DISPOSE(a);
    DISPOSE(unlimited);
    DISPOSE(contexts);
}

static void big_rationals(libktmath_kref_org_gciatto_kt_math_BigFactory factory) {
    libktmath_kref_org_gciatto_kt_math_BigRational third = KT_MATH.BigFactory.parseBigRational(factory, "1/3");
    libktmath_kref_org_gciatto_kt_math_BigRational sixth = KT_MATH.BigFactory.bigRationalOf_____(factory, 1, 6);
    libktmath_kref_org_gciatto_kt_math_BigRational sum = KT_MATH.BigRational.plus(third, sixth);
    libktmath_kref_org_gciatto_kt_math_BigRational reduced = KT_MATH.BigRational.reduce(sum);
    expect("1/3 + 1/6", KT_MATH.BigRational.toRationalString(reduced), "1/2");
    libktmath_kref_org_gciatto_kt_math_BigRational invalid = KT_MATH.BigFactory.parseBigRationalOrNull(factory, "1/0");
    if (invalid.pinned != NULL) {
        printf("FAILED parseBigRationalOrNull(\"1/0\") is null\n");
        failures++;
    }
    DISPOSE(reduced);
    DISPOSE(sum);
    DISPOSE(sixth);
    DISPOSE(third);
}

int main(void) {
    lib = libktmath_symbols();
    libktmath_kref_org_gciatto_kt_math_BigFactory_Companion companion = KT_MATH.BigFactory.Companion._instance();
    libktmath_kref_org_gciatto_kt_math_BigFactory factory = KT_MATH.BigFactory.Companion.get_default(companion);
    big_integers(factory);
    big_decimals(factory);
    big_rationals(factory);
    return failures == 0 ? 0 : 1;
}

This is the beginning of Kt-Math's own C usage test, compiled and run by CI: src/nativeUsageTest/c/usage.c goes on to call every other function of the C API, a reference for each of them.

Compile and run

cc usage.c -I ktmath -L ktmath -lktmath -Wl,-rpath,"$PWD/ktmath" -o usage
./usage
cc usage.c -I ktmath -L ktmath -lktmath -Wl,-rpath,"$PWD/ktmath" -o usage
./usage

A library downloaded with a browser is quarantined by macOS: xattr -d com.apple.quarantine ktmath/libktmath.dylib.

gcc usage.c -I ktmath -L ktmath -lktmath -o usage.exe
cp ktmath/ktmath.dll .
./usage.exe

With MSVC, create an import library from the definitions file first: lib /def:ktmath.def /out:ktmath.lib /machine:x64.

Going further

  • Call Kt-Math from C: the conventions of the C API (handles, strings, overloads, exceptions), and how to build the library yourself, e.g. for other Kotlin/Native targets.
  • Getting started in Kotlin: the same API from Kotlin/Native code, through Gradle.
  • API reference: every class and function, under the same names in C.