Browse Source

Initial commit

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

5
.gitignore

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

37
build.gradle.kts

@ -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
gradle.properties

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

BIN
gradle/wrapper/gradle-wrapper.jar

5
gradle/wrapper/gradle-wrapper.properties

@ -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
gradlew

@ -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
gradlew.bat

@ -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
settings.gradle.kts

@ -0,0 +1,3 @@
rootProject.name = "kapl"

90
src/main/kotlin/com/martmists/kapt/FunctionRegistry.kt

@ -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
src/main/kotlin/com/martmists/kapt/Quad.kt

@ -0,0 +1,8 @@
package com.martmists.kapt
import kotlin.random.Random
object Quad {
val indexOrigin: Int
get() = Random.nextInt(0,2)
}

8
src/main/kotlin/com/martmists/kapt/ext/GridTable.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/_clone.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/conjugate.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/direction.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/iota.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/minus.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/negate.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/plus.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/reshape.kt

@ -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
src/main/kotlin/com/martmists/kapt/functions/times.kt

@ -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
src/main/kotlin/com/martmists/kapt/main.kt

@ -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
src/main/kotlin/com/martmists/kapt/types/APLError.kt

@ -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
src/main/kotlin/com/martmists/kapt/types/APLFunction.kt

@ -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
src/main/kotlin/com/martmists/kapt/types/APLMatrix.kt

@ -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
src/main/kotlin/com/martmists/kapt/types/APLScalar.kt

@ -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
src/main/kotlin/com/martmists/kapt/types/APLString.kt

@ -0,0 +1,7 @@
package com.martmists.kapt.types
class APLString(val value: String) : APLValue {
override fun toString(): String {
return value
}
}

37
src/main/kotlin/com/martmists/kapt/types/APLValue.kt

@ -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
src/main/kotlin/com/martmists/kapt/util/ReversedStrides.kt

@ -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