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
+}