/Users/alexjokela/projects/lattice/src/array_ops.c
Line | Count | Source |
1 | | #include "array_ops.h" |
2 | | #include <stdlib.h> |
3 | | #include <string.h> |
4 | | |
5 | | /* ── Sort ── */ |
6 | | |
7 | | /* Static error flag for qsort comparator (qsort can't propagate errors) */ |
8 | | static int sort_error = 0; |
9 | | static ValueType sort_type = VAL_UNIT; |
10 | | |
11 | 9 | static int sort_comparator(const void *a, const void *b) { |
12 | 9 | const LatValue *va = (const LatValue *)a; |
13 | 9 | const LatValue *vb = (const LatValue *)b; |
14 | | |
15 | 9 | if (va->type != vb->type) { |
16 | 0 | sort_error = 1; |
17 | 0 | return 0; |
18 | 0 | } |
19 | | |
20 | 9 | switch (va->type) { |
21 | 3 | case VAL_INT: { |
22 | 3 | int64_t ai = va->as.int_val, bi = vb->as.int_val; |
23 | 3 | return (ai > bi) - (ai < bi); |
24 | 0 | } |
25 | 3 | case VAL_FLOAT: { |
26 | 3 | double af = va->as.float_val, bf = vb->as.float_val; |
27 | 3 | return (af > bf) - (af < bf); |
28 | 0 | } |
29 | 3 | case VAL_STR: |
30 | 3 | return strcmp(va->as.str_val, vb->as.str_val); |
31 | 0 | default: |
32 | 0 | sort_error = 1; |
33 | 0 | return 0; |
34 | 9 | } |
35 | 9 | } |
36 | | |
37 | 5 | LatValue array_sort(const LatValue *arr, char **err) { |
38 | 5 | size_t n = arr->as.array.len; |
39 | 5 | if (n == 0) { |
40 | 1 | *err = NULL; |
41 | 1 | LatValue empty; |
42 | 1 | return value_array(&empty, 0); |
43 | 1 | } |
44 | | |
45 | | /* Validate element types - must be homogeneous and comparable */ |
46 | 4 | sort_type = arr->as.array.elems[0].type; |
47 | 4 | if (sort_type != VAL_INT && sort_type != VAL_FLOAT && sort_type != VAL_STR) { |
48 | 0 | *err = strdup(".sort() only supports Int, Float, or String arrays"); |
49 | 0 | return value_unit(); |
50 | 0 | } |
51 | 10 | for (size_t i = 1; i < n; i++) { |
52 | 7 | if (arr->as.array.elems[i].type != sort_type) { |
53 | 1 | *err = strdup(".sort() requires all elements to be the same type"); |
54 | 1 | return value_unit(); |
55 | 1 | } |
56 | 7 | } |
57 | | |
58 | | /* Deep-clone elements into a working buffer */ |
59 | 3 | LatValue *buf = malloc(n * sizeof(LatValue)); |
60 | 12 | for (size_t i = 0; i < n; i++) { |
61 | 9 | buf[i] = value_deep_clone(&arr->as.array.elems[i]); |
62 | 9 | } |
63 | | |
64 | 3 | sort_error = 0; |
65 | 3 | qsort(buf, n, sizeof(LatValue), sort_comparator); |
66 | | |
67 | 3 | if (sort_error) { |
68 | 0 | for (size_t i = 0; i < n; i++) value_free(&buf[i]); |
69 | 0 | free(buf); |
70 | 0 | *err = strdup(".sort() encountered incomparable types"); |
71 | 0 | return value_unit(); |
72 | 0 | } |
73 | | |
74 | 3 | *err = NULL; |
75 | 3 | LatValue result = value_array(buf, n); |
76 | | /* value_array does a shallow memcpy, so it now owns the element data. |
77 | | * Only free the container array, not the individual elements. */ |
78 | 3 | free(buf); |
79 | 3 | return result; |
80 | 3 | } |
81 | | |
82 | | /* ── Flat ── */ |
83 | | |
84 | 8 | LatValue array_flat(const LatValue *arr) { |
85 | 8 | size_t n = arr->as.array.len; |
86 | | |
87 | | /* First pass: count total elements */ |
88 | 8 | size_t total = 0; |
89 | 26 | for (size_t i = 0; i < n; i++) { |
90 | 18 | if (arr->as.array.elems[i].type == VAL_ARRAY) { |
91 | 10 | total += arr->as.array.elems[i].as.array.len; |
92 | 10 | } else { |
93 | 8 | total += 1; |
94 | 8 | } |
95 | 18 | } |
96 | | |
97 | 8 | if (total == 0) { |
98 | 2 | LatValue empty; |
99 | 2 | return value_array(&empty, 0); |
100 | 2 | } |
101 | | |
102 | 6 | LatValue *buf = malloc(total * sizeof(LatValue)); |
103 | 6 | size_t pos = 0; |
104 | 24 | for (size_t i = 0; i < n; i++) { |
105 | 18 | if (arr->as.array.elems[i].type == VAL_ARRAY) { |
106 | 10 | LatValue *inner = arr->as.array.elems[i].as.array.elems; |
107 | 10 | size_t inner_len = arr->as.array.elems[i].as.array.len; |
108 | 26 | for (size_t j = 0; j < inner_len; j++) { |
109 | 16 | buf[pos++] = value_deep_clone(&inner[j]); |
110 | 16 | } |
111 | 10 | } else { |
112 | 8 | buf[pos++] = value_deep_clone(&arr->as.array.elems[i]); |
113 | 8 | } |
114 | 18 | } |
115 | | |
116 | 6 | LatValue result = value_array(buf, pos); |
117 | 6 | free(buf); |
118 | 6 | return result; |
119 | 8 | } |
120 | | |
121 | | /* ── Slice ── */ |
122 | | |
123 | 8 | LatValue array_slice(const LatValue *arr, int64_t start, int64_t end, char **err) { |
124 | 8 | int64_t len = (int64_t)arr->as.array.len; |
125 | | |
126 | | /* Clamp to [0, len] */ |
127 | 8 | if (start < 0) start = 0; |
128 | 8 | if (start > len) start = len; |
129 | 8 | if (end < 0) end = 0; |
130 | 8 | if (end > len) end = len; |
131 | 8 | if (end < start) end = start; |
132 | | |
133 | 8 | size_t count = (size_t)(end - start); |
134 | 8 | if (count == 0) { |
135 | 2 | *err = NULL; |
136 | 2 | LatValue empty; |
137 | 2 | return value_array(&empty, 0); |
138 | 2 | } |
139 | | |
140 | 6 | LatValue *buf = malloc(count * sizeof(LatValue)); |
141 | 22 | for (size_t i = 0; i < count; i++) { |
142 | 16 | buf[i] = value_deep_clone(&arr->as.array.elems[start + (int64_t)i]); |
143 | 16 | } |
144 | | |
145 | | *err = NULL; |
146 | 6 | LatValue result = value_array(buf, count); |
147 | 6 | free(buf); |
148 | 6 | return result; |
149 | 8 | } |