Browse Source

Initial commit

master
Martmists 2 months ago
commit
42912bff35
28 changed files with 1309 additions and 0 deletions
  1. +5
    -0
      .gitignore
  2. +37
    -0
      build.gradle.kts
  3. +1
    -0
      gradle.properties
  4. BIN
     
  5. +5
    -0
      gradle/wrapper/gradle-wrapper.properties
  6. +185
    -0
      gradlew
  7. +89
    -0
      gradlew.bat
  8. +3
    -0
      settings.gradle.kts
  9. +90
    -0
      src/main/kotlin/com/martmists/kapt/FunctionRegistry.kt
  10. +8
    -0
      src/main/kotlin/com/martmists/kapt/Quad.kt
  11. +8
    -0
      src/main/kotlin/com/martmists/kapt/ext/GridTable.kt
  12. +42
    -0
      src/main/kotlin/com/martmists/kapt/functions/_clone.kt
  13. +41
    -0
      src/main/kotlin/com/martmists/kapt/functions/conjugate.kt
  14. +41
    -0
      src/main/kotlin/com/martmists/kapt/functions/direction.kt
  15. +82
    -0
      src/main/kotlin/com/martmists/kapt/functions/iota.kt
  16. +71
    -0
      src/main/kotlin/com/martmists/kapt/functions/minus.kt
  17. +42
    -0
      src/main/kotlin/com/martmists/kapt/functions/negate.kt
  18. +71
    -0
      src/main/kotlin/com/martmists/kapt/functions/plus.kt
  19. +69
    -0
      src/main/kotlin/com/martmists/kapt/functions/reshape.kt
  20. +71
    -0
      src/main/kotlin/com/martmists/kapt/functions/times.kt
  21. +14
    -0
      src/main/kotlin/com/martmists/kapt/main.kt
  22. +10
    -0
      src/main/kotlin/com/martmists/kapt/types/APLError.kt
  23. +28
    -0
      src/main/kotlin/com/martmists/kapt/types/APLFunction.kt
  24. +147
    -0
      src/main/kotlin/com/martmists/kapt/types/APLMatrix.kt
  25. +51
    -0
      src/main/kotlin/com/martmists/kapt/types/APLScalar.kt
  26. +7
    -0
      src/main/kotlin/com/martmists/kapt/types/APLString.kt
  27. +37
    -0
      src/main/kotlin/com/martmists/kapt/types/APLValue.kt
  28. +54
    -0
      src/main/kotlin/com/martmists/kapt/util/ReversedStrides.kt

+ 5
- 0
.gitignore View File

@@ -0,0 +1,5 @@
.idea/
build/
.gradle/
*.iml


+ 37
- 0
build.gradle.kts View File

@@ -0,0 +1,37 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm") version "1.4.21"
application
}

group = "com.martmists.kapt"
version = "1.0-SNAPSHOT"

repositories {
mavenCentral()
repositories {
maven("https://dl.bintray.com/mipt-npm/dev")
maven("https://oss.sonatype.org/content/repositories/snapshots")
}

}


val kmath_version = "0.2.0-dev-5"


dependencies {
implementation("kscience.kmath:kmath-core:$kmath_version")
implementation("kscience.kmath:kmath-dimensions:$kmath_version")
implementation("com.github.inamik.text.tables:inamik-text-tables:1.0-SNAPSHOT")
}


tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "11"
}

application {
mainClassName = "com.martmists.kapt.MainKt"
}

+ 1
- 0
gradle.properties View File

@@ -0,0 +1 @@
kotlin.code.style=official

BIN
View File


+ 5
- 0
gradle/wrapper/gradle-wrapper.properties View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

+ 185
- 0
gradlew View File

@@ -0,0 +1,185 @@
#!/usr/bin/env sh

#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn () {
echo "$*"
}

die () {
echo
echo "$*"
echo
exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar


# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option

if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi

# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"

exec "$JAVACMD" "$@"

+ 89
- 0
gradlew.bat View File

@@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

+ 3
- 0
settings.gradle.kts View File

@@ -0,0 +1,3 @@

rootProject.name = "kapl"


+ 90
- 0
src/main/kotlin/com/martmists/kapt/FunctionRegistry.kt View File

@@ -0,0 +1,90 @@
package com.martmists.kapt

import com.martmists.kapt.functions.*
import com.martmists.kapt.types.*

object FunctionRegistry {
class FunctionBuilder {
var monadic: APLMonadic? = null
var dyadic: APLDyadic? = null
var axisMonadic: APLAxisMonadic? = null
var axisDyadic: APLAxisDyadic? = null

fun monadic(func: APLMonadic) {
monadic = func
}

fun dyadic(func: APLDyadic) {
dyadic = func
}

fun axisMonadic(func: APLAxisMonadic) {
axisMonadic = func
}

fun axisDyadic(func: APLAxisDyadic) {
axisDyadic = func
}
}

val map = mutableMapOf<Char, APLFunction>()

private fun registerInternal(character: Char,
monadic: APLMonadic? = null, dyadic: APLDyadic? = null,
axisMonadic: APLAxisMonadic? = null, axisDyadic: APLAxisDyadic? = null) {
map.putIfAbsent(character, APLFunction(monadic, dyadic, axisMonadic, axisDyadic))
}

fun register(character: Char, block: FunctionBuilder.() -> Unit) {
val builder = FunctionBuilder()
block(builder)
registerInternal(character, builder.monadic, builder.dyadic, builder.axisMonadic, builder.axisDyadic)
}

init {
register('+') {
monadic { omega ->
omega.conjugate()
}

dyadic { alpha, omega ->
alpha + omega
}
}

register('×') {
monadic { omega ->
omega.direction()
}

dyadic { alpha, omega ->
alpha * omega
}
}

register('⍳') {
monadic { omega ->
omega.iota()
}

dyadic { alpha, omega ->
// Indices of omega in alpha
TODO()
}
}

register('⍴') {
monadic { omega ->
TODO()
// omega.shape()
}

dyadic { alpha, omega ->
omega.reshape(alpha)
}

// Imagine using OF

}
}
}

+ 8
- 0
src/main/kotlin/com/martmists/kapt/Quad.kt View File

@@ -0,0 +1,8 @@
package com.martmists.kapt

import kotlin.random.Random

object Quad {
val indexOrigin: Int
get() = Random.nextInt(0,2)
}

+ 8
- 0
src/main/kotlin/com/martmists/kapt/ext/GridTable.kt View File

@@ -0,0 +1,8 @@
package com.martmists.kapt.ext

import com.inamik.text.tables.Cell
import com.inamik.text.tables.GridTable

fun GridTable.cellOf(lines: List<String>, trim: Boolean = true) = Cell.of(*lines.map { if (trim) it.trim() else "${it.trim()} " }.toTypedArray())

fun GridTable.contentToString() = this.apply(Cell.Functions.TOP_ALIGN).apply(Cell.Functions.LEFT_ALIGN).toCell().joinToString("\n")

+ 42
- 0
src/main/kotlin/com/martmists/kapt/functions/_clone.kt View File

@@ -0,0 +1,42 @@
package com.martmists.kapt.functions

import com.martmists.kapt.types.*
import kscience.kmath.operations.Complex
import kscience.kmath.operations.r


fun APLValue._clone(): APLValue {
return when (this) {
is APLFunction -> {
this._clone()
}
is APLMatrix -> {
this._clone()
}
is APLScalar -> {
this._clone()
}
is APLString -> {
this._clone()
}
else -> {
throw APLError.unknownType(this)
}
}
}

fun APLFunction._clone(): APLValue {
throw APLError("Cannot _clone Function")
}

fun APLMatrix._clone(): APLMatrix {
return map { it._clone() }
}

fun APLScalar._clone(): APLScalar {
return APLScalar(Complex(value.re, value.im))
}

fun APLString._clone(): APLValue {
return APLString(value.toCharArray().joinToString())
}

+ 41
- 0
src/main/kotlin/com/martmists/kapt/functions/conjugate.kt View File

@@ -0,0 +1,41 @@
package com.martmists.kapt.functions

import com.martmists.kapt.types.*
import kscience.kmath.operations.conjugate


fun APLValue.conjugate(): APLValue {
return when (this) {
is APLFunction -> {
this.conjugate()
}
is APLMatrix -> {
this.conjugate()
}
is APLScalar -> {
this.conjugate()
}
is APLString -> {
this.conjugate()
}
else -> {
throw APLError.unknownType(this)
}
}
}

fun APLFunction.conjugate(): APLValue {
throw APLError("Cannot conjugate Function")
}

fun APLMatrix.conjugate(): APLMatrix {
return map { it.conjugate() }
}

fun APLScalar.conjugate(): APLScalar {
return APLScalar(value.conjugate)
}

fun APLString.conjugate(): APLValue {
throw APLError("Cannot conjugate String")
}

+ 41
- 0
src/main/kotlin/com/martmists/kapt/functions/direction.kt View File

@@ -0,0 +1,41 @@
package com.martmists.kapt.functions

import com.martmists.kapt.types.*
import kscience.kmath.operations.r


fun APLValue.direction(): APLValue {
return when (this) {
is APLFunction -> {
this.direction()
}
is APLMatrix -> {
this.direction()
}
is APLScalar -> {
this.direction()
}
is APLString -> {
this.direction()
}
else -> {
throw APLError.unknownType(this)
}
}
}

fun APLFunction.direction(): APLValue {
throw APLError("Cannot direction Function")
}

fun APLMatrix.direction(): APLMatrix {
return map { it.direction() }
}

fun APLScalar.direction(): APLScalar {
return APLScalar(value.div(value.r))
}

fun APLString.direction(): APLValue {
throw APLError("Cannot direction String")
}

+ 82
- 0
src/main/kotlin/com/martmists/kapt/functions/iota.kt View File

@@ -0,0 +1,82 @@
package com.martmists.kapt.functions

import com.martmists.kapt.Quad
import com.martmists.kapt.types.*
import com.martmists.kapt.util.ReversedStrides
import kscience.kmath.operations.Complex
import kscience.kmath.operations.r
import kscience.kmath.structures.*


fun APLValue.iota(): APLValue {
return when (this) {
is APLFunction -> {
this.iota()
}
is APLMatrix -> {
this.iota()
}
is APLScalar -> {
this.iota()
}
is APLString -> {
this.iota()
}
else -> {
throw APLError.unknownType(this)
}
}
}

fun APLFunction.iota(): APLValue {
throw APLError("Cannot iota Function")
}

fun APLMatrix.iota(): APLMatrix {
if (size == 1) {
return asScalar().iota()
}

val shape = mutableListOf<Int>()

forEach {
if (it !is APLScalar || !it.natural()) {
throw APLError("Cannot iota non-natural Matrix")
}

shape.add(1 + it.asNatural() - Quad.indexOrigin)
}

val index_shape = listOf(*shape.toTypedArray(), shape.size)
val new_size = index_shape.reduce { acc, i -> acc * i }
val buf = MutableBuffer.boxing(new_size) { APLScalar(0) }

for (index in 0 until new_size) {
val dim_index = index % (index_shape.size-1)
val increase_every = index_shape.subList(dim_index+1, index_shape.size).reduce { acc, i -> acc * i }
val new_val = (((index-dim_index) / increase_every) % index_shape[dim_index]) + Quad.indexOrigin
buf[index] = APLScalar(new_val)
}

return APLMatrix(index_shape.toIntArray(), buf)
}

fun APLScalar.iota(): APLMatrix {
if (!natural()) {
throw APLError("Cannot iota Complex or Float")
}

val elems = mutableListOf<APLScalar>()
for (i in Quad.indexOrigin..asNatural()) {
elems.add(APLScalar(i))
}
if (elems.isEmpty()) {
throw APLError("iota returned empty")
}

return APLMatrix(intArrayOf(elems.size), elems.asBuffer())
}

fun APLString.iota(): APLValue {
throw APLError("Cannot iota String")
}

+ 71
- 0
src/main/kotlin/com/martmists/kapt/functions/minus.kt View File

@@ -0,0 +1,71 @@
package com.martmists.kapt.functions

import com.martmists.kapt.types.*

operator fun APLValue.minus(other: APLValue): APLValue {
return when (this) {
is APLFunction -> {
this - other
}
is APLMatrix -> {
this - other
}
is APLScalar -> {
this - other
}
is APLString -> {
this - other
}
else -> {
throw APLError.unknownType(this)
}
}
}

operator fun APLFunction.minus(other: APLValue): APLValue {
throw APLError("Cannot subtract Function and ${other.javaClass.name}")
}

operator fun APLMatrix.minus(other: APLValue): APLMatrix {
if (other is APLScalar) {
return map { it - other }
}

if (other is APLMatrix) {
return if (sameShape(other)) {
mapIndexed { ints, aplValue ->
aplValue - other[ints.toList()]
}
} else {
when {
other.size == 1 -> {
this - other[0, 0]
}
this.size == 1 -> {
other - this[0, 0]
}
else -> {
throw APLError.matrixShape(this, other)
}
}
}
}

throw APLError("Cannot subtract Matrix and ${other.javaClass.name}")
}

operator fun APLScalar.minus(other: APLValue): APLValue {
if (other is APLScalar) {
return APLScalar(value - other.value)
}

if (other is APLMatrix) {
return other - this
}

throw APLError("Cannot subtract Scalar and ${other.javaClass.name}")
}

operator fun APLString.minus(other: APLValue): APLValue {
throw APLError("Cannot subtract String and ${other.javaClass.name}")
}

+ 42
- 0
src/main/kotlin/com/martmists/kapt/functions/negate.kt View File

@@ -0,0 +1,42 @@
package com.martmists.kapt.functions

import com.martmists.kapt.types.*
import kscience.kmath.operations.Complex
import kscience.kmath.operations.r


fun APLValue.negate(): APLValue {
return when (this) {
is APLFunction -> {
this.negate()
}
is APLMatrix -> {
this.negate()
}
is APLScalar -> {
this.negate()
}
is APLString -> {
this.negate()
}
else -> {
throw APLError.unknownType(this)
}
}
}

fun APLFunction.negate(): APLValue {
throw APLError("Cannot negate Function")
}

fun APLMatrix.negate(): APLMatrix {
return map { it.negate() }
}

fun APLScalar.negate(): APLScalar {
return APLScalar(Complex(-value.re, -value.im))
}

fun APLString.negate(): APLValue {
throw APLError("Cannot negate String")
}

+ 71
- 0
src/main/kotlin/com/martmists/kapt/functions/plus.kt View File

@@ -0,0 +1,71 @@
package com.martmists.kapt.functions

import com.martmists.kapt.types.*

operator fun APLValue.plus(other: APLValue): APLValue {
return when (this) {
is APLFunction -> {
this + other
}
is APLMatrix -> {
this + other
}
is APLScalar -> {
this + other
}
is APLString -> {
this + other
}
else -> {
throw APLError.unknownType(this)
}
}
}

operator fun APLFunction.plus(other: APLValue): APLValue {
throw APLError("Cannot sum Function and ${other.javaClass.name}")
}

operator fun APLMatrix.plus(other: APLValue): APLMatrix {
if (other is APLScalar) {
return map { it + other }
}

if (other is APLMatrix) {
return if (sameShape(other)) {
mapIndexed { ints, aplValue ->
aplValue + other[ints.toList()]
}
} else {
when {
other.size == 1 -> {
this + other[0, 0]
}
this.size == 1 -> {
other + this[0, 0]
}
else -> {
throw APLError.matrixShape(this, other)
}
}
}
}

throw APLError("Cannot sum Matrix and ${other.javaClass.name}")
}

operator fun APLScalar.plus(other: APLValue): APLValue {
if (other is APLScalar) {
return APLScalar(value + other.value)
}

if (other is APLMatrix) {
return other + this
}

throw APLError("Cannot sum Scalar and ${other.javaClass.name}")
}

operator fun APLString.plus(other: APLValue): APLValue {
throw APLError("Cannot sum String and ${other.javaClass.name}")
}

+ 69
- 0
src/main/kotlin/com/martmists/kapt/functions/reshape.kt View File

@@ -0,0 +1,69 @@
package com.martmists.kapt.functions

import com.martmists.kapt.types.*
import kscience.kmath.structures.Buffer
import kscience.kmath.structures.toList

fun APLValue.reshape(other: APLValue): APLValue {
// IntelliJ Ultimate Edition
// at Jetbrains.com

if (other !is APLMatrix) {
throw APLError("Cannot reshape to non-matrix")
}
if (other.shape.size != 1) {
throw APLError("Cannot reshape to non-2D matrix")
}

return when (this) {
is APLFunction -> {
this.reshape(other)
}
is APLMatrix -> {
this.reshape(other)
}
is APLScalar -> {
this.reshape(other)
}
is APLString -> {
this.reshape(other)
}
else -> {
throw APLError.unknownType(this)
}
}
}

fun APLFunction.reshape(other: APLMatrix): APLValue {
throw APLError("Cannot reshape Function")
}

fun APLMatrix.reshape(other: APLMatrix): APLMatrix {
if (other.shape.size != 1) {
throw APLError("Cannot reshape Matrix to 2D shape")
}
if (size == 1) {
if (matrix.buffer[0] is APLString) {
matrix.buffer[0].reshape(other)
}
}

val shape = other.matrix.buffer.toList().map { (it as APLScalar).asNatural() }.toIntArray()
return APLMatrix(shape, Buffer.boxing(other.shape.reduce { acc, i -> acc * i }) { matrix.buffer[it % this.size] })
}

fun APLScalar.reshape(other: APLMatrix): APLValue {
throw APLError("Cannot reshape Scalar")
}

fun APLString.reshape(other: APLMatrix): APLValue {
val shape = other.matrix.buffer.toList().map { (it as APLScalar).asNatural() }.toIntArray()
// return APLMatrix(
// shape,
// Buffer.boxing(other.size) { }
// )
TODO()
}

+ 71
- 0
src/main/kotlin/com/martmists/kapt/functions/times.kt View File

@@ -0,0 +1,71 @@
package com.martmists.kapt.functions

import com.martmists.kapt.types.*

operator fun APLValue.times(other: APLValue): APLValue {
return when (this) {
is APLFunction -> {
this * other
}
is APLMatrix -> {
this * other
}
is APLScalar -> {
this * other
}
is APLString -> {
this * other
}
else -> {
throw APLError.unknownType(this)
}
}
}

operator fun APLFunction.times(other: APLValue): APLValue {
throw APLError("Cannot multiply Function and ${other.javaClass.name}")
}

operator fun APLMatrix.times(other: APLValue): APLMatrix {
if (other is APLScalar) {
return map { it * other }
}

if (other is APLMatrix) {
return if (sameShape(other)) {
mapIndexed { ints, aplValue ->
aplValue * other[ints.toList()]
}
} else {
when {
other.size == 1 -> {
this * other[0, 0]
}
this.size == 1 -> {
other * this[0, 0]
}
else -> {
throw APLError.matrixShape(this, other)
}
}
}
}

throw APLError("Cannot multiply Matrix and ${other.javaClass.name}")
}

operator fun APLScalar.times(other: APLValue): APLValue {
if (other is APLScalar) {
return APLScalar(value * other.value)
}

if (other is APLMatrix) {
return other * this
}

throw APLError("Cannot multiply Scalar and ${other.javaClass.name}")
}

operator fun APLString.times(other: APLValue): APLValue {
throw APLError("Cannot multiply String and ${other.javaClass.name}")
}

+ 14
- 0
src/main/kotlin/com/martmists/kapt/main.kt View File

@@ -0,0 +1,14 @@
package com.martmists.kapt

import com.martmists.kapt.functions.iota
import com.martmists.kapt.types.APLValue

fun main(args: Array<String>) {
// val value = APLValue.of(listOf(-2, 3, "abcdef", listOf(Complex(10.0, -1.7), Complex(0.0, -1.0)), 8.7))
// print(value)
val values = APLValue.of(listOf(100, 100, 100))
val now = System.nanoTime()
val res = values.iota()
val after = System.nanoTime()
println((after - now)/1000000.0)
}

+ 10
- 0
src/main/kotlin/com/martmists/kapt/types/APLError.kt View File

@@ -0,0 +1,10 @@
package com.martmists.kapt.types

class APLError(err: String) : Exception(err) {
companion object {
fun matrixShape(alpha: APLMatrix, omega: APLMatrix) = APLError("Non-matching matrix shapes: ${alpha.shape}, ${omega.shape}")
fun unknownType(other: APLValue) = APLError("ERR: Unknown/Unhandled type: ${other.javaClass.name}")
fun functionType(type: String) = APLError("Function does not implement: $type")
}

}

+ 28
- 0
src/main/kotlin/com/martmists/kapt/types/APLFunction.kt View File

@@ -0,0 +1,28 @@
package com.martmists.kapt.types

typealias APLMonadic = (omega: APLValue) -> APLValue
typealias APLDyadic = (alpha: APLValue, omega: APLValue) -> APLValue
typealias APLAxisMonadic = (axis: IntArray, omega: APLValue) -> APLValue
typealias APLAxisDyadic = (axis: IntArray, alpha: APLValue, omega: APLValue) -> APLValue

open class APLFunction(val monadic: APLMonadic?,
val dyadic: APLDyadic?,
val axisMonadic: APLAxisMonadic?,
val axisDyadic: APLAxisDyadic?
) : APLValue {
fun invoke(p1: APLValue): APLValue {
return monadic?.invoke(p1) ?: throw APLError.functionType("Monadic")
}

fun invoke(p1: APLValue, p2: APLValue): APLValue {
return dyadic?.invoke(p1, p2) ?: throw APLError.functionType("Dyadic")
}

fun invoke(p1: IntArray, p2: APLValue): APLValue {
return axisMonadic?.invoke(p1, p2) ?: throw APLError.functionType("AxisMonadic")
}

fun invoke(p1: IntArray, p2: APLValue, p3: APLValue): APLValue {
return axisDyadic?.invoke(p1, p2, p3) ?: throw APLError.functionType("AxisDyadic")
}
}

+ 147
- 0
src/main/kotlin/com/martmists/kapt/types/APLMatrix.kt View File

@@ -0,0 +1,147 @@
package com.martmists.kapt.types

import com.inamik.text.tables.GridTable
import com.inamik.text.tables.grid.Border
import com.martmists.kapt.util.ReversedStrides
import com.martmists.kapt.ext.*
import kscience.kmath.operations.r
import kscience.kmath.structures.*
import kotlin.math.roundToInt


class APLMatrix(val shape: IntArray,
buffer: Buffer<out APLValue>
) : APLValue {
val matrix = BufferNDStructure(ReversedStrides(shape), buffer)

val size = shape.reduce { x, y -> x * y }

fun sameShape(other: APLMatrix): Boolean {
return shape.contentEquals(other.shape)
}

fun map(callback: (APLValue) -> APLValue): APLMatrix {
return APLMatrix(
shape,
matrix.mapToBuffer { callback(it) }.buffer
)
}

fun mapIndexed(callback: (IntArray, APLValue) -> APLValue): APLMatrix {
return APLMatrix(
shape,
matrix.elements().map { p -> callback(p.first, p.second) }.toList().asBuffer()
)
}

fun forEach(callback: (APLValue) -> Unit) {
matrix.elements().forEach { callback(it.second) }
}

fun forEachIndexed(callback: (IntArray, APLValue) -> Unit) {
matrix.elements().forEach { callback(it.first, it.second) }
}

fun indexAxis(axis: Int, index: Int): APLValue {
val newShape = shape.filterIndexed { index, i -> index != axis }.toIntArray()

if (shape.size == 1) {
return get(index)
}

return APLMatrix(
newShape,
matrix.elements().filter { it.first[axis] == index }.map { it.second }.toList().asBuffer()
)
}

fun asScalar(): APLScalar {
if (size == 1 && get(0) is APLScalar) {
return get(0) as APLScalar
}

throw APLError("Cannot interpret Matrix as Scalar")
}

operator fun get(vararg x: Int): APLValue = matrix[x]
operator fun get(x: List<Int>): APLValue = matrix[x.toIntArray()]
operator fun get(indices: APLValue): APLValue {
if (indices is APLScalar) {
if (indices.natural()) {
return this[indices.value.r.roundToInt(), 0]
} else {
throw APLError("Cannot index Matrix by Complex or Float")
}

}

if (indices is APLMatrix) {
return indices.map {
this[it]
}
}

throw APLError("Cannot index Matrix by ${indices.javaClass.name}")
}

override fun toString(): String {
if (shape.size == 1) {
var grid = GridTable.of(1, shape[0])
val addRules = matrix.elements().any { it.second is APLMatrix }
matrix.elements().map { it.second.toString() }.forEachIndexed { index, s -> grid.put(0, index, grid.cellOf(s.split("\n"), addRules)) }
if (addRules) {
grid = Border.SINGLE_LINE.apply(grid)
}
return grid.contentToString()
}

if (shape.size == 2) {
var grid = GridTable.of(shape[0], shape[1])

val addRules = matrix.elements().any { it.second is APLMatrix }

for (index_1 in 0 until shape[0]) {
val tab = indexAxis(0, index_1)

for (index_2 in 0 until shape[1]) {
val tab_2 = (tab as APLMatrix).indexAxis(0, index_2)
grid.put(index_1, index_2, grid.cellOf(tab_2.toString().split("\n"), addRules))
}
}

if (addRules) {
grid = Border.SINGLE_LINE.apply(grid)
}

return grid.contentToString()
}

if (shape.size == 3) {
var grid = GridTable.of(shape[0], shape[1])

for (index_1 in 0 until shape[0]) {

val tab = indexAxis(0, index_1)

for (index_2 in 0 until shape[1]) {
val tab_2 = (tab as APLMatrix).indexAxis(0, index_2)
grid.put(index_1, index_2, grid.cellOf(tab_2.toString().split("\n")))
}
}

grid = Border.SINGLE_LINE.apply(grid)
return grid.contentToString()
}

if (shape.size > 3) {
var s = ""
for (index in 0 until shape[0]) {
val tab = indexAxis(0, index)
s += tab.toString() + "\n".repeat(shape.size-3)
}
return s.removeSuffix("\n".repeat(shape.size-3))
}

throw APLError("ERR: Negative size matrix shape!")
}
}

+ 51
- 0
src/main/kotlin/com/martmists/kapt/types/APLScalar.kt View File

@@ -0,0 +1,51 @@
package com.martmists.kapt.types

import kscience.kmath.operations.Complex
import kotlin.math.roundToInt

class APLScalar(val value: Complex) : APLValue {
constructor(value: Number) : this(Complex(value, 0.0))

fun natural(): Boolean {
return value.im == 0.0 && value.re.rem(1) == 0.0
}

fun asNatural(): Int {
return value.re.roundToInt()
}

override fun toString(): String {
when {
natural() -> {
// Int
return if (value.re >= 0) {
"${value.re.roundToInt()}"
} else {
"¯${(-value.re).roundToInt()}"
}
}
value.im == 0.0 -> {
// Float
return if (value.re >= 0) {
"${value.re}"
} else {
"¯${-value.re}"
}
}
else -> {
// Complex
val real = if (value.re >= 0) {
"${value.re}"
} else {
"¯${-value.re}"
}
val imag = if (value.im >= 0) {
"${value.im}"
} else {
"¯${-value.im}"
}
return real + "J" + imag
}
}
}
}

+ 7
- 0
src/main/kotlin/com/martmists/kapt/types/APLString.kt View File

@@ -0,0 +1,7 @@
package com.martmists.kapt.types

class APLString(val value: String) : APLValue {
override fun toString(): String {
return value
}
}

+ 37
- 0
src/main/kotlin/com/martmists/kapt/types/APLValue.kt View File

@@ -0,0 +1,37 @@
package com.martmists.kapt.types

import kscience.kmath.operations.Complex
import kscience.kmath.structures.Buffer
import java.lang.IllegalStateException


interface APLValue {
companion object {
@JvmStatic
fun of(value: Any): APLValue {
if (value is Number) {
return APLScalar(value)
} else if (value is Complex) {
return APLScalar(value)
}

if (value is String) {
return APLString(value)
}

if (value is List<*>) {
return APLMatrix(
intArrayOf(value.size),
Buffer.boxing(value.size) { of(value[it] ?: error("Element is null")) }
)
} else if (value is Array<*>) {
return APLMatrix(
intArrayOf(value.size),
Buffer.boxing(value.size) { of(value[it] ?: error("Element is null")) }
)
}

throw IllegalStateException("Unconvertible type: ${value.javaClass.name}")
}
}
}

+ 54
- 0
src/main/kotlin/com/martmists/kapt/util/ReversedStrides.kt View File

@@ -0,0 +1,54 @@
package com.martmists.kapt.util

import kscience.kmath.structures.DefaultStrides
import kscience.kmath.structures.Strides

/**
* Custom strides to iterate like APL does
*/
class ReversedStrides(override val shape: IntArray) : Strides {
override val linearSize: Int
get() = strides[shape.size]

override val strides: List<Int> by lazy {
sequence {
var current = 1
yield(1)

shape.reversedArray().forEach {
current *= it
yield(current)
}
}.toList()
}

override fun offset(index: IntArray): Int = index.mapIndexed { i, value ->
if (value < 0 || value >= this.shape[i])
throw IndexOutOfBoundsException("Index $value out of shape bounds: (0,${this.shape[i]})")

value * strides[strides.size-2-i]
}.sum()

override fun index(offset: Int): IntArray {
val res = IntArray(shape.size)
var current = offset
var strideIndex = strides.size - 2

while (strideIndex >= 0) {
res[strideIndex] = (current / strides[strideIndex])
current %= strides[strideIndex]
strideIndex--
}

return res.reversedArray()
}

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is ReversedStrides) return false
if (!shape.contentEquals(other.shape)) return false
return true
}

override fun hashCode(): Int = shape.contentHashCode()
}

Loading…
Cancel
Save