sonolus.py 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/allocate.py +125 -51
- sonolus/backend/blocks.py +756 -756
- sonolus/backend/coalesce.py +85 -0
- sonolus/backend/constant_evaluation.py +374 -0
- sonolus/backend/dead_code.py +80 -0
- sonolus/backend/dominance.py +111 -0
- sonolus/backend/excepthook.py +37 -37
- sonolus/backend/finalize.py +69 -69
- sonolus/backend/flow.py +121 -92
- sonolus/backend/inlining.py +150 -0
- sonolus/backend/ir.py +5 -3
- sonolus/backend/liveness.py +173 -0
- sonolus/backend/mode.py +24 -24
- sonolus/backend/node.py +40 -40
- sonolus/backend/ops.py +197 -197
- sonolus/backend/optimize.py +37 -9
- sonolus/backend/passes.py +52 -6
- sonolus/backend/simplify.py +47 -30
- sonolus/backend/ssa.py +187 -0
- sonolus/backend/utils.py +48 -48
- sonolus/backend/visitor.py +892 -880
- sonolus/build/cli.py +7 -1
- sonolus/build/compile.py +88 -90
- sonolus/build/engine.py +55 -5
- sonolus/build/level.py +24 -23
- sonolus/build/node.py +43 -43
- sonolus/script/archetype.py +23 -6
- sonolus/script/array.py +2 -2
- sonolus/script/bucket.py +191 -191
- sonolus/script/callbacks.py +127 -115
- sonolus/script/comptime.py +1 -1
- sonolus/script/containers.py +23 -0
- sonolus/script/debug.py +19 -3
- sonolus/script/easing.py +323 -0
- sonolus/script/effect.py +131 -131
- sonolus/script/engine.py +37 -1
- sonolus/script/globals.py +269 -269
- sonolus/script/graphics.py +200 -150
- sonolus/script/instruction.py +151 -0
- sonolus/script/internal/__init__.py +5 -5
- sonolus/script/internal/builtin_impls.py +144 -144
- sonolus/script/internal/context.py +12 -4
- sonolus/script/internal/descriptor.py +17 -17
- sonolus/script/internal/introspection.py +14 -14
- sonolus/script/internal/native.py +40 -38
- sonolus/script/internal/value.py +3 -3
- sonolus/script/interval.py +120 -112
- sonolus/script/iterator.py +214 -211
- sonolus/script/math.py +30 -1
- sonolus/script/num.py +1 -1
- sonolus/script/options.py +191 -191
- sonolus/script/particle.py +157 -157
- sonolus/script/pointer.py +30 -30
- sonolus/script/{preview.py → print.py} +81 -81
- sonolus/script/random.py +14 -0
- sonolus/script/range.py +58 -58
- sonolus/script/record.py +3 -3
- sonolus/script/runtime.py +45 -6
- sonolus/script/sprite.py +333 -333
- sonolus/script/text.py +407 -407
- sonolus/script/timing.py +42 -42
- sonolus/script/transform.py +77 -23
- sonolus/script/ui.py +160 -160
- sonolus/script/vec.py +81 -72
- {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/METADATA +1 -2
- sonolus_py-0.1.4.dist-info/RECORD +84 -0
- {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/WHEEL +1 -1
- {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/licenses/LICENSE +21 -21
- sonolus/build/defaults.py +0 -32
- sonolus/script/icon.py +0 -73
- sonolus_py-0.1.2.dist-info/RECORD +0 -76
- {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/entry_points.txt +0 -0
sonolus/script/interval.py
CHANGED
|
@@ -1,112 +1,120 @@
|
|
|
1
|
-
from typing import Self
|
|
2
|
-
|
|
3
|
-
from sonolus.backend.ops import Op
|
|
4
|
-
from sonolus.script.debug import error
|
|
5
|
-
from sonolus.script.internal.native import native_function
|
|
6
|
-
from sonolus.script.record import Record
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Interval(Record):
|
|
10
|
-
"""A closed interval."""
|
|
11
|
-
|
|
12
|
-
start: float
|
|
13
|
-
end: float
|
|
14
|
-
|
|
15
|
-
@property
|
|
16
|
-
def length(self) -> float:
|
|
17
|
-
return self.end - self.start
|
|
18
|
-
|
|
19
|
-
@property
|
|
20
|
-
def is_empty(self) -> bool:
|
|
21
|
-
return self.start > self.end
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def mid(self) -> float:
|
|
25
|
-
return (self.start + self.end) / 2
|
|
26
|
-
|
|
27
|
-
@property
|
|
28
|
-
def tuple(self):
|
|
29
|
-
return self.start, self.end
|
|
30
|
-
|
|
31
|
-
def __contains__(self, item: Self | float | int) -> bool:
|
|
32
|
-
match item:
|
|
33
|
-
case Interval(start, end):
|
|
34
|
-
return self.start <= start and end <= self.end
|
|
35
|
-
case float() | int() as value:
|
|
36
|
-
return self.start <= value <= self.end
|
|
37
|
-
case _:
|
|
38
|
-
error("Invalid type for interval check")
|
|
39
|
-
|
|
40
|
-
def __add__(self, other: float | int) -> Self:
|
|
41
|
-
return Interval(self.start + other, self.end + other)
|
|
42
|
-
|
|
43
|
-
def __sub__(self, other: float | int) -> Self:
|
|
44
|
-
return Interval(self.start - other, self.end - other)
|
|
45
|
-
|
|
46
|
-
def __mul__(self, other: float | int) -> Self:
|
|
47
|
-
return Interval(self.start * other, self.end * other)
|
|
48
|
-
|
|
49
|
-
def __truediv__(self, other: float | int) -> Self:
|
|
50
|
-
return Interval(self.start / other, self.end / other)
|
|
51
|
-
|
|
52
|
-
def __floordiv__(self, other: float | int) -> Self:
|
|
53
|
-
return Interval(self.start // other, self.end // other)
|
|
54
|
-
|
|
55
|
-
def __and__(self, other: Self) -> Self:
|
|
56
|
-
return Interval(max(self.start, other.start), min(self.end, other.end))
|
|
57
|
-
|
|
58
|
-
def shrink(self, value: float | int) -> Self:
|
|
59
|
-
return Interval(self.start + value, self.end - value)
|
|
60
|
-
|
|
61
|
-
def expand(self, value: float | int) -> Self:
|
|
62
|
-
return Interval(self.start - value, self.end + value)
|
|
63
|
-
|
|
64
|
-
def lerp(self, x: float, /) -> float:
|
|
65
|
-
return lerp(self.start, self.end, x)
|
|
66
|
-
|
|
67
|
-
def lerp_clamped(self, x: float, /) -> float:
|
|
68
|
-
return lerp_clamped(self.start, self.end, x)
|
|
69
|
-
|
|
70
|
-
def unlerp(self, x: float, /) -> float:
|
|
71
|
-
return unlerp(self.start, self.end, x)
|
|
72
|
-
|
|
73
|
-
def unlerp_clamped(self, x: float, /) -> float:
|
|
74
|
-
return unlerp_clamped(self.start, self.end, x)
|
|
75
|
-
|
|
76
|
-
def clamp(self, x: float, /) -> float:
|
|
77
|
-
return clamp(x, self.start, self.end)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
@native_function(Op.Lerp)
|
|
81
|
-
def lerp(a, b, x, /):
|
|
82
|
-
return a + (b - a) * x
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@native_function(Op.LerpClamped)
|
|
86
|
-
def lerp_clamped(a, b, x, /):
|
|
87
|
-
return a + (b - a) * max(0, min(1, x))
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
@native_function(Op.Unlerp)
|
|
91
|
-
def unlerp(a, b, x, /):
|
|
92
|
-
return (x - a) / (b - a)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@native_function(Op.UnlerpClamped)
|
|
96
|
-
def unlerp_clamped(a, b, x, /):
|
|
97
|
-
return max(0, min(1, (x - a) / (b - a)))
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
@native_function(Op.Remap)
|
|
101
|
-
def remap(a, b, c, d, x, /):
|
|
102
|
-
return c + (d - c) * (x - a) / (b - a)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
@native_function(Op.RemapClamped)
|
|
106
|
-
def remap_clamped(a, b, c, d, x, /):
|
|
107
|
-
return c + (d - c) * max(0, min(1, (x - a) / (b - a)))
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
@native_function(Op.Clamp)
|
|
111
|
-
def clamp(x, a, b, /):
|
|
112
|
-
return max(a, min(b, x))
|
|
1
|
+
from typing import Self
|
|
2
|
+
|
|
3
|
+
from sonolus.backend.ops import Op
|
|
4
|
+
from sonolus.script.debug import error
|
|
5
|
+
from sonolus.script.internal.native import native_function
|
|
6
|
+
from sonolus.script.record import Record
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Interval(Record):
|
|
10
|
+
"""A closed interval."""
|
|
11
|
+
|
|
12
|
+
start: float
|
|
13
|
+
end: float
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def length(self) -> float:
|
|
17
|
+
return self.end - self.start
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def is_empty(self) -> bool:
|
|
21
|
+
return self.start > self.end
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def mid(self) -> float:
|
|
25
|
+
return (self.start + self.end) / 2
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def tuple(self):
|
|
29
|
+
return self.start, self.end
|
|
30
|
+
|
|
31
|
+
def __contains__(self, item: Self | float | int) -> bool:
|
|
32
|
+
match item:
|
|
33
|
+
case Interval(start, end):
|
|
34
|
+
return self.start <= start and end <= self.end
|
|
35
|
+
case float() | int() as value:
|
|
36
|
+
return self.start <= value <= self.end
|
|
37
|
+
case _:
|
|
38
|
+
error("Invalid type for interval check")
|
|
39
|
+
|
|
40
|
+
def __add__(self, other: float | int) -> Self:
|
|
41
|
+
return Interval(self.start + other, self.end + other)
|
|
42
|
+
|
|
43
|
+
def __sub__(self, other: float | int) -> Self:
|
|
44
|
+
return Interval(self.start - other, self.end - other)
|
|
45
|
+
|
|
46
|
+
def __mul__(self, other: float | int) -> Self:
|
|
47
|
+
return Interval(self.start * other, self.end * other)
|
|
48
|
+
|
|
49
|
+
def __truediv__(self, other: float | int) -> Self:
|
|
50
|
+
return Interval(self.start / other, self.end / other)
|
|
51
|
+
|
|
52
|
+
def __floordiv__(self, other: float | int) -> Self:
|
|
53
|
+
return Interval(self.start // other, self.end // other)
|
|
54
|
+
|
|
55
|
+
def __and__(self, other: Self) -> Self:
|
|
56
|
+
return Interval(max(self.start, other.start), min(self.end, other.end))
|
|
57
|
+
|
|
58
|
+
def shrink(self, value: float | int) -> Self:
|
|
59
|
+
return Interval(self.start + value, self.end - value)
|
|
60
|
+
|
|
61
|
+
def expand(self, value: float | int) -> Self:
|
|
62
|
+
return Interval(self.start - value, self.end + value)
|
|
63
|
+
|
|
64
|
+
def lerp(self, x: float, /) -> float:
|
|
65
|
+
return lerp(self.start, self.end, x)
|
|
66
|
+
|
|
67
|
+
def lerp_clamped(self, x: float, /) -> float:
|
|
68
|
+
return lerp_clamped(self.start, self.end, x)
|
|
69
|
+
|
|
70
|
+
def unlerp(self, x: float, /) -> float:
|
|
71
|
+
return unlerp(self.start, self.end, x)
|
|
72
|
+
|
|
73
|
+
def unlerp_clamped(self, x: float, /) -> float:
|
|
74
|
+
return unlerp_clamped(self.start, self.end, x)
|
|
75
|
+
|
|
76
|
+
def clamp(self, x: float, /) -> float:
|
|
77
|
+
return clamp(x, self.start, self.end)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@native_function(Op.Lerp)
|
|
81
|
+
def lerp(a, b, x, /):
|
|
82
|
+
return a + (b - a) * x
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@native_function(Op.LerpClamped)
|
|
86
|
+
def lerp_clamped(a, b, x, /):
|
|
87
|
+
return a + (b - a) * max(0, min(1, x))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@native_function(Op.Unlerp)
|
|
91
|
+
def unlerp(a, b, x, /):
|
|
92
|
+
return (x - a) / (b - a)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@native_function(Op.UnlerpClamped)
|
|
96
|
+
def unlerp_clamped(a, b, x, /):
|
|
97
|
+
return max(0, min(1, (x - a) / (b - a)))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@native_function(Op.Remap)
|
|
101
|
+
def remap(a, b, c, d, x, /):
|
|
102
|
+
return c + (d - c) * (x - a) / (b - a)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@native_function(Op.RemapClamped)
|
|
106
|
+
def remap_clamped(a, b, c, d, x, /):
|
|
107
|
+
return c + (d - c) * max(0, min(1, (x - a) / (b - a)))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@native_function(Op.Clamp)
|
|
111
|
+
def clamp(x, a, b, /):
|
|
112
|
+
return max(a, min(b, x))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def generic_lerp[T](a: T, b: T, x: float, /) -> T:
|
|
116
|
+
return a + (b - a) * x
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def generic_lerp_clamped[T](a: T, b: T, x: float, /) -> T:
|
|
120
|
+
return a + (b - a) * max(0, min(1, x))
|
sonolus/script/iterator.py
CHANGED
|
@@ -1,211 +1,214 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from abc import abstractmethod
|
|
4
|
-
from collections.abc import Collection, Iterator
|
|
5
|
-
|
|
6
|
-
from sonolus.script.num import Num
|
|
7
|
-
from sonolus.script.record import Record
|
|
8
|
-
from sonolus.script.values import copy
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class SonolusIterator[T](Iterator[T]):
|
|
12
|
-
@abstractmethod
|
|
13
|
-
def has_next(self) -> bool:
|
|
14
|
-
raise NotImplementedError
|
|
15
|
-
|
|
16
|
-
@abstractmethod
|
|
17
|
-
def next(self) -> T:
|
|
18
|
-
raise NotImplementedError
|
|
19
|
-
|
|
20
|
-
def __next__(self) -> T:
|
|
21
|
-
if not self.has_next():
|
|
22
|
-
raise StopIteration
|
|
23
|
-
return self.next()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class ArrayLike[T](Collection):
|
|
27
|
-
@abstractmethod
|
|
28
|
-
def size(self) -> int:
|
|
29
|
-
pass
|
|
30
|
-
|
|
31
|
-
@abstractmethod
|
|
32
|
-
def __getitem__(self, index: Num) -> T:
|
|
33
|
-
pass
|
|
34
|
-
|
|
35
|
-
@abstractmethod
|
|
36
|
-
def __setitem__(self, index: Num, value: T):
|
|
37
|
-
pass
|
|
38
|
-
|
|
39
|
-
def __len__(self) -> int:
|
|
40
|
-
return self.size()
|
|
41
|
-
|
|
42
|
-
def __iter__(self) -> SonolusIterator[T]:
|
|
43
|
-
return ArrayIterator(0, self)
|
|
44
|
-
|
|
45
|
-
def __contains__(self, value: T) -> bool:
|
|
46
|
-
i = 0
|
|
47
|
-
while i < self.size():
|
|
48
|
-
if self[i] == value:
|
|
49
|
-
return True
|
|
50
|
-
i += 1
|
|
51
|
-
return False
|
|
52
|
-
|
|
53
|
-
def reversed(self) -> ArrayLike[T]:
|
|
54
|
-
return ArrayReverser(self)
|
|
55
|
-
|
|
56
|
-
def iter(self) -> SonolusIterator[T]:
|
|
57
|
-
return self.__iter__() # noqa: PLC2801
|
|
58
|
-
|
|
59
|
-
def enumerate(self, start: Num = 0) -> SonolusIterator[T]:
|
|
60
|
-
return ArrayEnumerator(0, start, self)
|
|
61
|
-
|
|
62
|
-
def index_of(self, value: T, start: Num = 0) -> Num:
|
|
63
|
-
i = start
|
|
64
|
-
while i < self.size():
|
|
65
|
-
if self[i] == value:
|
|
66
|
-
return i
|
|
67
|
-
i += 1
|
|
68
|
-
return -1
|
|
69
|
-
|
|
70
|
-
def last_index_of(self, value: T) -> Num:
|
|
71
|
-
i = self.size() - 1
|
|
72
|
-
while i >= 0:
|
|
73
|
-
if self[i] == value:
|
|
74
|
-
return i
|
|
75
|
-
i -= 1
|
|
76
|
-
return -1
|
|
77
|
-
|
|
78
|
-
def index_of_max(self) -> Num:
|
|
79
|
-
if self.size() == 0:
|
|
80
|
-
return -1
|
|
81
|
-
max_index = 0
|
|
82
|
-
i = 1
|
|
83
|
-
while i < self.size():
|
|
84
|
-
if self[i] > self[max_index]:
|
|
85
|
-
max_index = i
|
|
86
|
-
i += 1
|
|
87
|
-
return max_index
|
|
88
|
-
|
|
89
|
-
def index_of_min(self) -> Num:
|
|
90
|
-
if self.size() == 0:
|
|
91
|
-
return -1
|
|
92
|
-
min_index = 0
|
|
93
|
-
i = 1
|
|
94
|
-
while i < self.size():
|
|
95
|
-
if self[i] < self[min_index]:
|
|
96
|
-
min_index = i
|
|
97
|
-
i += 1
|
|
98
|
-
return min_index
|
|
99
|
-
|
|
100
|
-
def max(self) -> T:
|
|
101
|
-
return self[self.index_of_max()]
|
|
102
|
-
|
|
103
|
-
def min(self) -> T:
|
|
104
|
-
return self[self.index_of_min()]
|
|
105
|
-
|
|
106
|
-
def swap(self, i: Num, j: Num):
|
|
107
|
-
temp = copy(self[i])
|
|
108
|
-
self[i] = self[j]
|
|
109
|
-
self[j] = temp
|
|
110
|
-
|
|
111
|
-
def sort(self, *, reverse: bool = False):
|
|
112
|
-
if self.size() < 15:
|
|
113
|
-
_insertion_sort(self, 0, self.size(), reverse)
|
|
114
|
-
else:
|
|
115
|
-
_heap_sort(self, 0, self.size(), reverse)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def _insertion_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
|
|
119
|
-
i = start + 1
|
|
120
|
-
while i < end:
|
|
121
|
-
value = copy(array[i])
|
|
122
|
-
j = i - 1
|
|
123
|
-
while j >= start and (array[j] > value) != reverse:
|
|
124
|
-
array[j + 1] = array[j]
|
|
125
|
-
j -= 1
|
|
126
|
-
array[j + 1] = value
|
|
127
|
-
i += 1
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def _heapify[T](array: ArrayLike[T], end: Num, index: Num, reverse: bool):
|
|
131
|
-
while True:
|
|
132
|
-
left = index * 2 + 1
|
|
133
|
-
right = left + 1
|
|
134
|
-
largest = index
|
|
135
|
-
if left < end and (array[left] > array[largest]) != reverse:
|
|
136
|
-
largest = left
|
|
137
|
-
if right < end and (array[right] > array[largest]) != reverse:
|
|
138
|
-
largest = right
|
|
139
|
-
if largest == index:
|
|
140
|
-
break
|
|
141
|
-
array.swap(index, largest)
|
|
142
|
-
index = largest
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
# Heap sort is simple to implement iteratively without dynamic memory allocation
|
|
146
|
-
def _heap_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
|
|
147
|
-
i = end // 2 - 1
|
|
148
|
-
while i >= start:
|
|
149
|
-
_heapify(array, end, i, reverse)
|
|
150
|
-
i -= 1
|
|
151
|
-
i = end - 1
|
|
152
|
-
while i > start:
|
|
153
|
-
array.swap(start, i)
|
|
154
|
-
_heapify(array, i, start, reverse)
|
|
155
|
-
i -= 1
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
class ArrayIterator[V: ArrayLike](Record, SonolusIterator):
|
|
159
|
-
i: int
|
|
160
|
-
array: V
|
|
161
|
-
|
|
162
|
-
def has_next(self) -> bool:
|
|
163
|
-
return self.i < self.array.size()
|
|
164
|
-
|
|
165
|
-
def next(self) -> V:
|
|
166
|
-
value = self.array[self.i]
|
|
167
|
-
self.i += 1
|
|
168
|
-
return value
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
class ArrayReverser[V: ArrayLike](Record, ArrayLike):
|
|
172
|
-
array: V
|
|
173
|
-
|
|
174
|
-
def size(self) -> int:
|
|
175
|
-
return self.array.size()
|
|
176
|
-
|
|
177
|
-
def __getitem__(self, index: Num) -> V:
|
|
178
|
-
return self.array[self.size() - 1 - index]
|
|
179
|
-
|
|
180
|
-
def __setitem__(self, index: Num, value: V):
|
|
181
|
-
self.array[self.size() - 1 - index] = value
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
def
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
def
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from collections.abc import Collection, Iterator
|
|
5
|
+
|
|
6
|
+
from sonolus.script.num import Num
|
|
7
|
+
from sonolus.script.record import Record
|
|
8
|
+
from sonolus.script.values import copy
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SonolusIterator[T](Iterator[T]):
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def has_next(self) -> bool:
|
|
14
|
+
raise NotImplementedError
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def next(self) -> T:
|
|
18
|
+
raise NotImplementedError
|
|
19
|
+
|
|
20
|
+
def __next__(self) -> T:
|
|
21
|
+
if not self.has_next():
|
|
22
|
+
raise StopIteration
|
|
23
|
+
return self.next()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ArrayLike[T](Collection):
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def size(self) -> int:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def __getitem__(self, index: Num) -> T:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def __setitem__(self, index: Num, value: T):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def __len__(self) -> int:
|
|
40
|
+
return self.size()
|
|
41
|
+
|
|
42
|
+
def __iter__(self) -> SonolusIterator[T]:
|
|
43
|
+
return ArrayIterator(0, self)
|
|
44
|
+
|
|
45
|
+
def __contains__(self, value: T) -> bool:
|
|
46
|
+
i = 0
|
|
47
|
+
while i < self.size():
|
|
48
|
+
if self[i] == value:
|
|
49
|
+
return True
|
|
50
|
+
i += 1
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
def reversed(self) -> ArrayLike[T]:
|
|
54
|
+
return ArrayReverser(self)
|
|
55
|
+
|
|
56
|
+
def iter(self) -> SonolusIterator[T]:
|
|
57
|
+
return self.__iter__() # noqa: PLC2801
|
|
58
|
+
|
|
59
|
+
def enumerate(self, start: Num = 0) -> SonolusIterator[T]:
|
|
60
|
+
return ArrayEnumerator(0, start, self)
|
|
61
|
+
|
|
62
|
+
def index_of(self, value: T, start: Num = 0) -> Num:
|
|
63
|
+
i = start
|
|
64
|
+
while i < self.size():
|
|
65
|
+
if self[i] == value:
|
|
66
|
+
return i
|
|
67
|
+
i += 1
|
|
68
|
+
return -1
|
|
69
|
+
|
|
70
|
+
def last_index_of(self, value: T) -> Num:
|
|
71
|
+
i = self.size() - 1
|
|
72
|
+
while i >= 0:
|
|
73
|
+
if self[i] == value:
|
|
74
|
+
return i
|
|
75
|
+
i -= 1
|
|
76
|
+
return -1
|
|
77
|
+
|
|
78
|
+
def index_of_max(self) -> Num:
|
|
79
|
+
if self.size() == 0:
|
|
80
|
+
return -1
|
|
81
|
+
max_index = 0
|
|
82
|
+
i = 1
|
|
83
|
+
while i < self.size():
|
|
84
|
+
if self[i] > self[max_index]:
|
|
85
|
+
max_index = i
|
|
86
|
+
i += 1
|
|
87
|
+
return max_index
|
|
88
|
+
|
|
89
|
+
def index_of_min(self) -> Num:
|
|
90
|
+
if self.size() == 0:
|
|
91
|
+
return -1
|
|
92
|
+
min_index = 0
|
|
93
|
+
i = 1
|
|
94
|
+
while i < self.size():
|
|
95
|
+
if self[i] < self[min_index]:
|
|
96
|
+
min_index = i
|
|
97
|
+
i += 1
|
|
98
|
+
return min_index
|
|
99
|
+
|
|
100
|
+
def max(self) -> T:
|
|
101
|
+
return self[self.index_of_max()]
|
|
102
|
+
|
|
103
|
+
def min(self) -> T:
|
|
104
|
+
return self[self.index_of_min()]
|
|
105
|
+
|
|
106
|
+
def swap(self, i: Num, j: Num):
|
|
107
|
+
temp = copy(self[i])
|
|
108
|
+
self[i] = self[j]
|
|
109
|
+
self[j] = temp
|
|
110
|
+
|
|
111
|
+
def sort(self, *, reverse: bool = False):
|
|
112
|
+
if self.size() < 15:
|
|
113
|
+
_insertion_sort(self, 0, self.size(), reverse)
|
|
114
|
+
else:
|
|
115
|
+
_heap_sort(self, 0, self.size(), reverse)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _insertion_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
|
|
119
|
+
i = start + 1
|
|
120
|
+
while i < end:
|
|
121
|
+
value = copy(array[i])
|
|
122
|
+
j = i - 1
|
|
123
|
+
while j >= start and (array[j] > value) != reverse:
|
|
124
|
+
array[j + 1] = array[j]
|
|
125
|
+
j -= 1
|
|
126
|
+
array[j + 1] = value
|
|
127
|
+
i += 1
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _heapify[T](array: ArrayLike[T], end: Num, index: Num, reverse: bool):
|
|
131
|
+
while True:
|
|
132
|
+
left = index * 2 + 1
|
|
133
|
+
right = left + 1
|
|
134
|
+
largest = index
|
|
135
|
+
if left < end and (array[left] > array[largest]) != reverse:
|
|
136
|
+
largest = left
|
|
137
|
+
if right < end and (array[right] > array[largest]) != reverse:
|
|
138
|
+
largest = right
|
|
139
|
+
if largest == index:
|
|
140
|
+
break
|
|
141
|
+
array.swap(index, largest)
|
|
142
|
+
index = largest
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# Heap sort is simple to implement iteratively without dynamic memory allocation
|
|
146
|
+
def _heap_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
|
|
147
|
+
i = end // 2 - 1
|
|
148
|
+
while i >= start:
|
|
149
|
+
_heapify(array, end, i, reverse)
|
|
150
|
+
i -= 1
|
|
151
|
+
i = end - 1
|
|
152
|
+
while i > start:
|
|
153
|
+
array.swap(start, i)
|
|
154
|
+
_heapify(array, i, start, reverse)
|
|
155
|
+
i -= 1
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class ArrayIterator[V: ArrayLike](Record, SonolusIterator):
|
|
159
|
+
i: int
|
|
160
|
+
array: V
|
|
161
|
+
|
|
162
|
+
def has_next(self) -> bool:
|
|
163
|
+
return self.i < self.array.size()
|
|
164
|
+
|
|
165
|
+
def next(self) -> V:
|
|
166
|
+
value = self.array[self.i]
|
|
167
|
+
self.i += 1
|
|
168
|
+
return value
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class ArrayReverser[V: ArrayLike](Record, ArrayLike):
|
|
172
|
+
array: V
|
|
173
|
+
|
|
174
|
+
def size(self) -> int:
|
|
175
|
+
return self.array.size()
|
|
176
|
+
|
|
177
|
+
def __getitem__(self, index: Num) -> V:
|
|
178
|
+
return self.array[self.size() - 1 - index]
|
|
179
|
+
|
|
180
|
+
def __setitem__(self, index: Num, value: V):
|
|
181
|
+
self.array[self.size() - 1 - index] = value
|
|
182
|
+
|
|
183
|
+
def reversed(self) -> ArrayLike[V]:
|
|
184
|
+
return self.array
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class Enumerator[V: SonolusIterator](Record, SonolusIterator):
|
|
188
|
+
i: int
|
|
189
|
+
offset: int
|
|
190
|
+
iterator: V
|
|
191
|
+
|
|
192
|
+
def has_next(self) -> bool:
|
|
193
|
+
return self.iterator.has_next()
|
|
194
|
+
|
|
195
|
+
def next(self):
|
|
196
|
+
value = self.iterator.next()
|
|
197
|
+
index = self.i + self.offset
|
|
198
|
+
self.i += 1
|
|
199
|
+
return index, value
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class ArrayEnumerator[V: ArrayLike](Record, SonolusIterator):
|
|
203
|
+
i: int
|
|
204
|
+
offset: int
|
|
205
|
+
array: V
|
|
206
|
+
|
|
207
|
+
def has_next(self) -> bool:
|
|
208
|
+
return self.i < self.array.size()
|
|
209
|
+
|
|
210
|
+
def next(self):
|
|
211
|
+
value = self.array[self.i]
|
|
212
|
+
index = self.i + self.offset
|
|
213
|
+
self.i += 1
|
|
214
|
+
return index, value
|