aboutsummaryrefslogtreecommitdiff
path: root/src/main/kotlin/moe/nea/ledger/NumberUtil.kt
blob: 438f342ad7d7f106c7e691419cbd75346ce7d6a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package moe.nea.ledger

import net.minecraft.event.ClickEvent
import net.minecraft.event.HoverEvent
import net.minecraft.util.ChatComponentText
import net.minecraft.util.ChatStyle
import net.minecraft.util.EnumChatFormatting
import net.minecraft.util.IChatComponent
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeFormatterBuilder
import java.time.temporal.ChronoField
import java.util.regex.Matcher
import java.util.regex.Pattern

// language=regexp
val SHORT_NUMBER_PATTERN = "[0-9]+(?:,[0-9]+)*(?:\\.[0-9]+)?[kKmMbB]?"

// language=regexp
val ROMAN_NUMBER_PATTERN = "[IVXLCDM]+"

val romanNumbers = mapOf(
	'I' to 1,
	'V' to 5,
	'X' to 10,
	'L' to 50,
	'C' to 100,
	'D' to 500,
	'M' to 1000
)

fun parseRomanNumber(string: String): Int {
	var smallestSeenSoFar = Int.MAX_VALUE
	var lastSeenOfSmallest = 0
	var amount = 0
	for (c in string) {
		val cV = romanNumbers[c]!!
		if (cV == smallestSeenSoFar) {
			lastSeenOfSmallest++
			amount += cV
		} else if (cV < smallestSeenSoFar) {
			smallestSeenSoFar = cV
			amount += cV
			lastSeenOfSmallest = 1
		} else {
			amount -= lastSeenOfSmallest * smallestSeenSoFar * 2
			smallestSeenSoFar = cV
			amount += cV
			lastSeenOfSmallest = 1
		}
	}
	return amount
}

val siScalars = mapOf(
	'k' to 1_000.0,
	'K' to 1_000.0,
	'm' to 1_000_000.0,
	'M' to 1_000_000.0,
	'b' to 1_000_000_000.0,
	'B' to 1_000_000_000.0,
)

fun parseShortNumber(string: String): Double {
	var k = string.replace(",", "")
	val scalar = k.last()
	var scalarMultiplier = siScalars[scalar]
	if (scalarMultiplier == null) {
		scalarMultiplier = 1.0
	} else {
		k = k.dropLast(1)
	}
	return k.toDouble() * scalarMultiplier
}

fun Pattern.matches(string: String): Boolean = matcher(string).matches()
inline fun <T> Pattern.useMatcher(string: String, block: Matcher.() -> T): T? =
	matcher(string).takeIf { it.matches() }?.let(block)

fun <T> String.ifDropLast(suffix: String, block: (String) -> T): T? {
	if (endsWith(suffix)) {
		return block(dropLast(suffix.length))
	}
	return null
}

fun String.unformattedString(): String = replace("§.".toRegex(), "")

val timeFormat: DateTimeFormatter = DateTimeFormatterBuilder()
	.appendValue(ChronoField.DAY_OF_MONTH, 2)
	.appendLiteral(".")
	.appendValue(ChronoField.MONTH_OF_YEAR, 2)
	.appendLiteral(".")
	.appendValue(ChronoField.YEAR, 4)
	.appendLiteral(" ")
	.appendValue(ChronoField.HOUR_OF_DAY, 2)
	.appendLiteral(":")
	.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
	.appendLiteral(":")
	.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
	.toFormatter()

fun Instant.formatChat(): IChatComponent {
	val text = ChatComponentText(
		LocalDateTime.ofInstant(this, ZoneId.systemDefault()).format(timeFormat)
	)
	text.setChatStyle(
		ChatStyle()
			.setChatClickEvent(
				ClickEvent(ClickEvent.Action.OPEN_URL, "https://time.is/${this.epochSecond}"))
			.setChatHoverEvent(
				HoverEvent(HoverEvent.Action.SHOW_TEXT, ChatComponentText("Click to show on time.is")))
			.setColor(EnumChatFormatting.AQUA))
	return text
}