1 /*
2 This file is part of GNU APL, a free implementation of the
3 ISO/IEC Standard 13751, "Programming Language APL, Extended"
4
5 Copyright (C) 2008-2019 Dr. Jürgen Sauermann
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "Bif_OPER1_EACH.hh"
22 #include "Bif_F12_TAKE_DROP.hh"
23 #include "Workspace.hh"
24
25 // primitive function instances
26 //
27 Bif_F12_TAKE Bif_F12_TAKE ::_fun; // ↑
28 Bif_F12_DROP Bif_F12_DROP ::_fun; // ↓
29
30 // primitive function pointers
31 //
32 Bif_F12_TAKE * Bif_F12_TAKE ::fun = &Bif_F12_TAKE ::_fun;
33 Bif_F12_DROP * Bif_F12_DROP ::fun = &Bif_F12_DROP ::_fun;
34
35 //=============================================================================
36 Value_P
first(Value_P B)37 Bif_F12_TAKE::first(Value_P B)
38 {
39 const Cell & first_B = B->get_ravel(0);
40 if (B->element_count() == 0) // empty value: return prototype
41 {
42 if (first_B.is_lval_cell()) // (
43 {
44 Value_P Z(LOC);
45 Z->next_ravel()->init(first_B, B.getref(), LOC);
46 return Z;
47 }
48 Value_P Z = B->prototype(LOC);
49 Z->check_value(LOC);
50 return Z;
51 }
52
53 if (!first_B.is_pointer_cell()) // simple cell
54 {
55 Value_P Z(LOC);
56 Z->get_ravel(0).init(first_B, Z.getref(), LOC);
57 Z->check_value(LOC);
58 return Z;
59 }
60
61 Value_P v1 = first_B.get_pointer_value();
62 Value * v1_owner = v1->get_lval_cellowner();
63 if (v1_owner) // B is a left value
64 {
65 Value_P B1(LOC);
66 new (&B1->get_ravel(0)) PointerCell(v1.get(), B1.getref());
67
68 Value_P Z(LOC);
69 new (&Z->get_ravel(0)) LvalCell(&B1->get_ravel(0), v1_owner);
70
71 Z->check_value(LOC);
72 return Z;
73 }
74 else
75 {
76 const ShapeItem ec = v1->element_count();
77 Value_P Z(v1->get_shape(), LOC);
78 if (ec == 0) Z->get_ravel(0).init(v1->get_ravel(0), Z.getref(), LOC);
79
80 loop(e, ec) Z->next_ravel()->init(v1->get_ravel(e), Z.getref(), LOC);
81
82 Z->check_value(LOC);
83 return Z;
84 }
85 }
86 //-----------------------------------------------------------------------------
87 Token
eval_AXB(Value_P A,Value_P X,Value_P B)88 Bif_F12_TAKE::eval_AXB(Value_P A, Value_P X, Value_P B)
89 {
90 if (X->element_count() == 0) // no axes
91 {
92 Token result(TOK_APL_VALUE1, B->clone(LOC));
93 return result;
94 }
95
96 // A↑[X]B ←→ ⊃[X](⊂A)↑¨⊂[X]B
97 //
98 Value_P cA = Bif_F12_PARTITION::fun->do_eval_B(A); // ⊂A
99 Value_P cB = Bif_F12_PARTITION::fun->do_eval_XB(X, B); // ⊂[X]B
100 Token take(TOK_FUN2, Bif_F12_TAKE::fun);
101 Token cT = Bif_OPER1_EACH::fun->eval_ALB(cA, take, cB); // cA↑¨cB
102
103 Token result = Bif_F12_PICK::fun->eval_XB(X, cT.get_apl_val());
104 return result;
105 }
106 //-----------------------------------------------------------------------------
107 Token
eval_AB(Value_P A,Value_P B)108 Bif_F12_TAKE::eval_AB(Value_P A, Value_P B)
109 {
110 Shape ravel_A1(A.get(), /* ⎕IO */ 0); // checks that 1 ≤ ⍴⍴A and ⍴A ≤ MAX_RANK
111
112 if (B->is_scalar())
113 {
114 Shape shape_B1;
115 loop(a, ravel_A1.get_rank()) shape_B1.add_shape_item(1);
116 Value_P B1 = B->clone(LOC);
117 B1->set_shape(shape_B1);
118 return Token(TOK_APL_VALUE1, do_take(ravel_A1, B1));
119 }
120 else
121 {
122 if (ravel_A1.get_rank() != B->get_rank()) LENGTH_ERROR;
123 return Token(TOK_APL_VALUE1, do_take(ravel_A1, B));
124 }
125 }
126 //-----------------------------------------------------------------------------
127 Value_P
do_take(const Shape & ravel_A1,Value_P B)128 Bif_F12_TAKE::do_take(const Shape & ravel_A1, Value_P B)
129 {
130 // ravel_A1 can have negative items (for take from the end).
131 //
132 Value_P Z(ravel_A1.abs(), LOC);
133
134 if (ravel_A1.is_empty()) Z->set_default(*B.get(), LOC); // empty Z
135 else fill(ravel_A1, &Z->get_ravel(0), Z.getref(), B);
136 Z->check_value(LOC);
137 return Z;
138 }
139 //-----------------------------------------------------------------------------
140 void
fill(const Shape & shape_Zi,Cell * cZ,Value & Z_owner,Value_P B)141 Bif_F12_TAKE::fill(const Shape & shape_Zi, Cell * cZ, Value & Z_owner,
142 Value_P B)
143 {
144 for (TakeDropIterator i(true, shape_Zi, B->get_shape()); i.more(); ++i)
145 {
146 const ShapeItem offset = i();
147 if (offset == -1) cZ++->init_type(B->get_ravel(0), Z_owner, LOC);
148 else cZ++->init(B->get_ravel(offset), Z_owner, LOC);
149 }
150 }
151 //=============================================================================
152 Token
eval_AB(Value_P A,Value_P B)153 Bif_F12_DROP::eval_AB(Value_P A, Value_P B)
154 {
155 Shape ravel_A(A.get(), /* ⎕IO */ 0);
156 if (A->get_rank() > 1) RANK_ERROR;
157
158 if (B->is_scalar())
159 {
160 // if B is a scalar then the result rank shall be the length of A->
161 // the result may be empty (shape 0 0 ... 0) if we drop something
162 // or non-empty (shape 1 1 ... 1) if we drop nothing.
163 //
164 const ShapeItem len_Z = ravel_A.get_volume() ? 0 : 1;
165
166 Shape shape_Z;
167 loop(r, ravel_A.get_rank()) shape_Z.add_shape_item(len_Z);
168
169 Value_P Z(shape_Z, LOC);
170
171 Z->get_ravel(0).init(B->get_ravel(0), Z.getref(), LOC);
172 Z->check_value(LOC);
173 return Token(TOK_APL_VALUE1, Z);
174 }
175
176 if (ravel_A.get_rank() == 0) ravel_A.add_shape_item(1); // A = ,A
177
178 if (ravel_A.get_rank() != B->get_rank()) LENGTH_ERROR;
179
180 Shape sh_Z;
181 loop(r, ravel_A.get_rank())
182 {
183 const ShapeItem sA = ravel_A.get_shape_item(r);
184 const ShapeItem sB = B->get_shape_item(r);
185 const ShapeItem pA = sA < 0 ? -sA : sA;
186 if (pA >= sB) sh_Z.add_shape_item(0); // over-drop
187 else sh_Z.add_shape_item(sB - pA);
188 }
189
190 Value_P Z(sh_Z, LOC);
191 if (sh_Z.is_empty()) // empty Z, e.g. from overdrop
192 {
193 Value_P Z(sh_Z, LOC);
194 Z->set_default(*B.get(), LOC);
195 Z->check_value(LOC);
196 return Token(TOK_APL_VALUE1, Z);
197 }
198
199 for (TakeDropIterator i(false, ravel_A, B->get_shape()); i.more(); ++i)
200 {
201 const ShapeItem offset = i();
202 Z->next_ravel()->init(B->get_ravel(offset), Z.getref(), LOC);
203 }
204
205 Z->check_value(LOC);
206 return Token(TOK_APL_VALUE1, Z);
207 }
208 //-----------------------------------------------------------------------------
209 Token
eval_AXB(Value_P A,Value_P X,Value_P B)210 Bif_F12_DROP::eval_AXB(Value_P A, Value_P X, Value_P B)
211 {
212 if (X->element_count() == 0) // no axes
213 {
214 Token result(TOK_APL_VALUE1, B->clone(LOC));
215 return result;
216 }
217
218 if (X->get_rank() > 1) INDEX_ERROR;
219
220 const uint64_t len_X = X->element_count();
221 if (len_X > MAX_RANK) INDEX_ERROR;
222 if (len_X == 0) INDEX_ERROR;
223
224 if (A->get_rank() > 1) RANK_ERROR;
225
226 uint64_t len_A = A->element_count();
227 if (len_A != len_X) LENGTH_ERROR;
228
229 const APL_Integer qio = Workspace::get_IO();
230
231 // init ravel_A = shape_B and seen.
232 //
233 Shape ravel_A(B->get_shape());
234 bool seen[MAX_RANK];
235 loop(r, B->get_rank()) seen[r] = false;
236
237 loop(r, len_X)
238 {
239 const APL_Integer a = A->get_ravel(r).get_near_int();
240 const APL_Integer x = X->get_ravel(r).get_near_int() - qio;
241
242 if (x >= B->get_rank()) INDEX_ERROR;
243 if (seen[x]) INDEX_ERROR;
244 seen[x] = true;
245
246 const ShapeItem amax = B->get_shape_item(x);
247 if (a >= amax) ravel_A.set_shape_item(x, 0);
248 else if (a >= 0) ravel_A.set_shape_item(x, a - amax);
249 else if (a > -amax) ravel_A.set_shape_item(x, amax + a);
250 else ravel_A.set_shape_item(x, 0);
251 }
252
253 return Token(TOK_APL_VALUE1, Bif_F12_TAKE::do_take(ravel_A, B));
254 }
255 //=============================================================================
256
257