diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26c4834 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.idea +out/* +*.iml +target + +settings.xml diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f488518 --- /dev/null +++ b/pom.xml @@ -0,0 +1,216 @@ + + 4.0.0 + + com.github.insanusmokrassar + PsychomatrixBase + 0.1 + jar + + ${project.artifactId} + ${project.artifactId} + https://insanusmokrassar.github.io/${project.artifactId}/ + + + Ovsyannikov Alexey + ovsyannikov.alexey95@gmail.com + + + + + MIT + https://github.com/insanusmokrassar/${project.artifactId}/blob/master/LICENSE + repo + + + + https://github.com/insanusmokrassar/${project.artifactId} + HEAD + + scm:git:[fetch=]https://github.com/insanusmokrassar/${project.artifactId}.git[push=]ssh:git@github.com:insanusmokrassar/${project.artifactId}.git + + + + + UTF-8 + 1.2.61 + 0.24.0 + 0.9.16 + 1.6 + 2.6 + 3.0.1 + 3.0.2 + bintray-insanusmokrassar-StandardRepository + ${project.groupId}.${project.artifactId}.ExampleKt + 1.7 + 1.7 + + + + + jcenter + JCenter + https://jcenter.bintray.com/ + + + + + + + ${project.basedir}/src/main/res/ + + + ${project.basedir}/src/main/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + + + compile + process-sources + + compile + + + + test-compile + test-compile + + test-compile + + + + + + -Xcoroutines=enable + + + + + org.apache.maven.plugins + maven-jar-plugin + ${jar.version} + + + + true + ${main.class} + + + + + + org.apache.maven.plugins + maven-source-plugin + ${source.version} + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-assembly-plugin + ${assembly.version} + + + make-assembly + package + + single + + + + + ${main.class} + + + + jar-with-dependencies + + + + + + + org.jetbrains.dokka + dokka-maven-plugin + ${dokka.version} + + + package + + dokka + javadocJar + + + + + + ${project.build.sourceDirectory} + + default + + + + org.apache.maven.plugins + maven-gpg-plugin + ${gpg.version} + + + sign-artifacts + package + + sign + + + + + + + + + + bintray-insanusmokrassar-StandardRepository + insanusmokrassar-StandardRepository + https://api.bintray.com/maven/insanusmokrassar/StandardRepository/${project.artifactId}/;publish=1 + + + + + + junit + junit + 4.12 + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + org.jetbrains.kotlinx + kotlinx-coroutines-core + ${kotlin.coroutines.version} + + + org.jetbrains.kotlin + kotlin-test-junit + ${kotlin.version} + test + + + + joda-time + joda-time + 2.10 + + + diff --git a/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/Example.kt b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/Example.kt new file mode 100644 index 0000000..6105437 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/Example.kt @@ -0,0 +1,4 @@ +package com.github.insanusmokrassar.PsychomatrixBase + +fun main(args: Array) { +} diff --git a/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/domain/UseCases/UserCanCalculatePsychomatrixByDate.kt b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/domain/UseCases/UserCanCalculatePsychomatrixByDate.kt new file mode 100644 index 0000000..3de23a4 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/domain/UseCases/UserCanCalculatePsychomatrixByDate.kt @@ -0,0 +1,15 @@ +package com.github.insanusmokrassar.PsychomatrixBase.domain.UseCases + +import com.github.insanusmokrassar.PsychomatrixBase.domain.entities.Psychomatrix +import kotlinx.coroutines.experimental.Deferred +import kotlinx.coroutines.experimental.channels.ReceiveChannel +import org.joda.time.DateTime +import java.util.* + +interface UserCanCalculatePsychomatrixByDate { + suspend fun calculate(date: Long): Deferred + suspend fun calculate(date: DateTime): Deferred + suspend fun calculate(date: Date): Deferred + + suspend fun openPsychomatrixCreatedSubscription(): ReceiveChannel +} \ No newline at end of file diff --git a/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/domain/entities/Psychomatrix.kt b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/domain/entities/Psychomatrix.kt new file mode 100644 index 0000000..2a1eb88 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/domain/entities/Psychomatrix.kt @@ -0,0 +1,140 @@ +package com.github.insanusmokrassar.PsychomatrixBase.domain.entities + +import org.joda.time.DateTime +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* +import kotlin.math.sqrt + +private val dateFormat: DateFormat = SimpleDateFormat("dMyyyy", Locale.ROOT) +private fun Int.toDigits(): List { + return "$this".map { "$it".toByte() } +} + +class Psychomatrix(val date: DateTime) { + private val numbers: ByteArray = ByteArray(10) + + /** + * Always array 4*4 of values. In rows was put columns ( + */ + val asMatrix: Array> + get() = arrayOf( + arrayOf(-1, -1, -1, getUpperDiagSum()), + arrayOf(numbers[1], numbers[4], numbers[7], getRowSum(0)), + arrayOf(numbers[2], numbers[5], numbers[8], getRowSum(1)), + arrayOf(numbers[3], numbers[6], numbers[9], getRowSum(2)), + arrayOf(getColumnSum(0), getColumnSum(1), getColumnSum(2), getDownDiagSum()) + ) + + init { + val dateDigits = dateFormat.format(date).map { "$it".toByte() }.toMutableList() + + val firstNumber = dateDigits.sum() + val firstNumberDigits = firstNumber.toDigits() + + val secondNumber = firstNumberDigits.sum() + val secondNumberDigits = secondNumber.toDigits() + + val thirdNumber = firstNumber - dateDigits[0] * 2 + val thirdNumberDigits = thirdNumber.toDigits() + + val fourthNumber = thirdNumberDigits.sum() + val fourthNumberDigits = fourthNumber.toDigits() + + dateDigits.addAll(firstNumberDigits) + dateDigits.addAll(secondNumberDigits) + dateDigits.addAll(thirdNumberDigits) + dateDigits.addAll(fourthNumberDigits) + + (0 until numbers.size).forEach { + index -> + numbers[index] = dateDigits.count { + it == index.toByte() + }.toByte() + } + } + + /** + * @return count of numbers of `i` + */ + operator fun get(i: Int): Byte { + return numbers[i] + } + + /** + * 1 4 7 - zero row + * 2 5 8 - first row + * 3 6 9 - second row + */ + fun getRowSum(i: Int): Byte { + return (i + 1 until numbers.size step 3).map { numbers[it] }.sum().toByte() + } + + /** + * 1 4 7 + * 2 5 8 + * 3 6 9 + * | | | + * z f s + * e i e + * r r c + * o s o + * t n + * d + */ + fun getColumnSum(i: Int): Byte { + val side = sqrt(numbers.size.toDouble() - 1).toInt() + val first = side * i + 1 + return (first until first + side).map { numbers[it] }.sum().toByte() + } + + /** + * upper + * / + * 1 4 7 + * 2 5 8 + * 3 6 9 + */ + fun getUpperDiagSum(): Byte { + val side = sqrt(numbers.size.toDouble() - 1).toInt() + return (side .. numbers.size - side step side - 1).map { numbers[it] }.sum().toByte() + } + + /** + * 1 4 7 + * 2 5 8 + * 3 6 9 + * \ + * down + */ + fun getDownDiagSum(): Byte { + val side = sqrt(numbers.size.toDouble() - 1).toInt() + return (1 until numbers.size step side + 1).map { numbers[it] }.sum().toByte() + } + + override fun toString(): String { + return (" %2d\n" + + "%2d %2d %2d %2d\n" + + "%2d %2d %2d %2d\n" + + "%2d %2d %2d %2d\n" + + "%2d %2d %2d %2d").format( + getUpperDiagSum(), + numbers[1], + numbers[4], + numbers[7], + getRowSum(0), + numbers[2], + numbers[5], + numbers[8], + getRowSum(1), + numbers[3], + numbers[6], + numbers[9], + getRowSum(2), + getColumnSum(0), + getColumnSum(1), + getColumnSum(2), + getDownDiagSum() + ) + } +} diff --git a/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/presentation/presenters/DatePickerPresenter.kt b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/presentation/presenters/DatePickerPresenter.kt new file mode 100644 index 0000000..9ff49e6 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/presentation/presenters/DatePickerPresenter.kt @@ -0,0 +1,14 @@ +package com.github.insanusmokrassar.PsychomatrixBase.presentation.presenters + +import com.github.insanusmokrassar.PsychomatrixBase.domain.entities.Psychomatrix +import kotlinx.coroutines.experimental.channels.ReceiveChannel +import org.joda.time.DateTime + +interface DatePickerPresenter { + + suspend fun openPsychomatrixCreatedSubscription(): ReceiveChannel + + fun userPickDate(date: Long) + fun userPickDate(date: DateTime) + +} diff --git a/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/utils/extensions/Calendar.kt b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/utils/extensions/Calendar.kt new file mode 100644 index 0000000..08d0051 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/utils/extensions/Calendar.kt @@ -0,0 +1,7 @@ +package com.github.insanusmokrassar.PsychomatrixBase.utils.extensions + +import java.util.* + +fun Calendar.getYear(): Int = get(Calendar.YEAR) +fun Calendar.getMonth(): Int = get(Calendar.MONTH) +fun Calendar.getDayOfMonth(): Int = get(Calendar.DAY_OF_MONTH) diff --git a/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/utils/extensions/ReceiveChannel.kt b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/utils/extensions/ReceiveChannel.kt new file mode 100644 index 0000000..8be02b5 --- /dev/null +++ b/src/main/kotlin/com/github/insanusmokrassar/PsychomatrixBase/utils/extensions/ReceiveChannel.kt @@ -0,0 +1,85 @@ +package com.github.insanusmokrassar.PsychomatrixBase.utils.extensions + +import kotlinx.coroutines.experimental.* +import kotlinx.coroutines.experimental.channels.BroadcastChannel +import kotlinx.coroutines.experimental.channels.ReceiveChannel +import java.util.concurrent.TimeUnit + +fun ReceiveChannel.subscribeChecking( + throwableHandler: (Throwable) -> Boolean = { + it.printStackTrace() + true + }, + by: suspend (T) -> Boolean +): Job { + return launch { + while (isActive && !isClosedForReceive) { + try { + val received = receive() + + launch { + try { + if (!by(received)) { + cancel() + } + } catch (e: Throwable) { + if (!throwableHandler(e)) { + cancel() + } + } + } + } catch (e: CancellationException) { + break + } + } + cancel() + } +} + +fun ReceiveChannel.subscribe( + throwableHandler: (Throwable) -> Boolean = { + it.printStackTrace() + true + }, + by: suspend (T) -> Unit +): Job { + return subscribeChecking(throwableHandler) { + by(it) + true + } +} + +fun ReceiveChannel.debounce(delayMs: Long, awaitedSubscriptions: Int = 256): BroadcastChannel { + val channel = BroadcastChannel(awaitedSubscriptions) + var lastReceived: Pair? = null + var job: Job? = null + launch { + while (isActive && !isClosedForReceive) { + val received = receive() + + lastReceived = Pair(System.currentTimeMillis() + delayMs, received) + + job ?:let { + job = launch { + try { + var now = System.currentTimeMillis() + while (isActive && lastReceived?.first ?: now >= now) { + delay((lastReceived ?.first ?: now) - now, TimeUnit.MILLISECONDS) + now = System.currentTimeMillis() + } + + lastReceived?.second?.also { + channel.send(it) + } + } catch (e: Exception) { + e.printStackTrace() + } finally { + job = null + } + } + } + } + cancel() + } + return channel +}