1#[cfg(test)]
2mod test;
3
4use crate::{
5 fem::block::element::{
6 ElementNodalReferenceCoordinates, FiniteElement, GradientVectors, ParametricCoordinate,
7 ParametricCoordinates, ParametricReference, ShapeFunctions,
8 ShapeFunctionsAtIntegrationPoints, ShapeFunctionsGradients, StandardGradientOperators,
9 StandardGradientOperatorsTransposed,
10 composite::{
11 CompositeElement, NormalizedProjectionMatrix, ParametricGradientOperators,
12 ProjectionMatrix, ShapeFunctionIntegrals, ShapeFunctionIntegralsProducts,
13 },
14 linear::Tetrahedron as LinearTetrahedron,
15 quadratic::Tetrahedron as QuadraticTetrahedron,
16 },
17 math::{Scalar, ScalarList, Tensor, TensorRank1},
18};
19
20const G: usize = 4;
21const M: usize = 3;
22const N: usize = 10;
23const P: usize = 4;
24const Q: usize = 12;
25
26pub type Tetrahedron = CompositeElement<G, N>;
27
28impl From<ElementNodalReferenceCoordinates<N>> for Tetrahedron {
29 fn from(reference_nodal_coordinates: ElementNodalReferenceCoordinates<N>) -> Self {
30 let gradient_vectors = Self::projected_gradient_vectors(&reference_nodal_coordinates);
31 let integration_weights =
32 Self::reference_jacobians(&reference_nodal_coordinates) * Self::integration_weight();
33 Self {
34 gradient_vectors,
35 integration_weights,
36 }
37 }
38}
39
40impl FiniteElement<G, M, N, P> for Tetrahedron {
41 fn integration_points() -> ParametricCoordinates<G, M> {
42 QuadraticTetrahedron::integration_points() }
44 fn integration_weights(&self) -> &ScalarList<G> {
45 &self.integration_weights
46 }
47 fn parametric_reference() -> ParametricReference<M, N> {
48 [
49 [0.0, 0.0, 0.0],
50 [1.0, 0.0, 0.0],
51 [0.0, 1.0, 0.0],
52 [0.0, 0.0, 1.0],
53 [0.5, 0.0, 0.0],
54 [0.5, 0.5, 0.0],
55 [0.0, 0.5, 0.0],
56 [0.0, 0.0, 0.5],
57 [0.5, 0.0, 0.5],
58 [0.0, 0.5, 0.5],
59 ]
60 .into()
61 }
62 fn parametric_weights() -> ScalarList<G> {
63 [1.0 / 24.0; G].into()
64 }
65 fn shape_functions(parametric_coordinate: ParametricCoordinate<M>) -> ShapeFunctions<P> {
66 LinearTetrahedron::shape_functions(parametric_coordinate) }
68 fn shape_functions_gradients(
69 parametric_coordinate: ParametricCoordinate<M>,
70 ) -> ShapeFunctionsGradients<M, P> {
71 LinearTetrahedron::shape_functions_gradients(parametric_coordinate) }
73}
74
75impl Tetrahedron {
76 const fn integration_weight() -> Scalar {
77 1.0 / 24.0
78 }
79 fn inverse_normalized_projection_matrix() -> NormalizedProjectionMatrix<P> {
80 const DIAG: Scalar = 4.0 / 640.0;
81 const OFF: Scalar = -1.0 / 640.0;
82 [
83 [DIAG, OFF, OFF, OFF],
84 [OFF, DIAG, OFF, OFF],
85 [OFF, OFF, DIAG, OFF],
86 [OFF, OFF, OFF, DIAG],
87 ]
88 .into()
89 }
90 fn inverse_projection_matrix(
91 reference_jacobians_subelements: &ScalarList<Q>,
92 ) -> NormalizedProjectionMatrix<P> {
93 Self::shape_function_integrals_products()
94 .iter()
95 .zip(reference_jacobians_subelements)
96 .map(
97 |(shape_function_integrals_products, reference_jacobian_subelement)| {
98 shape_function_integrals_products * reference_jacobian_subelement
99 },
100 )
101 .sum::<ProjectionMatrix<P>>()
102 .inverse()
103 }
104 fn projected_gradient_vectors(
105 reference_nodal_coordinates: &ElementNodalReferenceCoordinates<N>,
106 ) -> GradientVectors<G, N> {
107 let parametric_gradient_operators = Self::shape_functions_gradients_at_integration_points()
108 .iter()
109 .map(|standard_gradient_operator| {
110 reference_nodal_coordinates * standard_gradient_operator
111 })
112 .collect::<ParametricGradientOperators<Q>>();
113 let reference_jacobians_subelements =
114 Self::reference_jacobians_subelements(reference_nodal_coordinates);
115 let inverse_projection_matrix =
116 Self::inverse_projection_matrix(&reference_jacobians_subelements);
117 Self::shape_functions_at_integration_points()
118 .iter()
119 .map(|shape_functions_at_integration_point| {
120 Self::standard_gradient_operators_transposed()
121 .iter()
122 .map(|standard_gradient_operators_a| {
123 Self::shape_function_integrals()
124 .iter()
125 .zip(
126 standard_gradient_operators_a.iter().zip(
127 parametric_gradient_operators
128 .iter()
129 .zip(reference_jacobians_subelements.iter()),
130 ),
131 )
132 .map(
133 |(
134 shape_function_integral,
135 (
136 standard_gradient_operator,
137 (
138 parametric_gradient_operator,
139 reference_jacobian_subelement,
140 ),
141 ),
142 )| {
143 (parametric_gradient_operator.inverse_transpose()
144 * standard_gradient_operator)
145 * reference_jacobian_subelement
146 * (shape_functions_at_integration_point
147 * (&inverse_projection_matrix
148 * shape_function_integral))
149 },
150 )
151 .sum()
152 })
153 .collect()
154 })
155 .collect()
156 }
157 fn reference_jacobians(
158 reference_nodal_coordinates: &ElementNodalReferenceCoordinates<N>,
159 ) -> ScalarList<G> {
160 let vector = Self::inverse_normalized_projection_matrix()
161 * Self::shape_function_integrals()
162 .iter()
163 .zip(Self::reference_jacobians_subelements(
164 reference_nodal_coordinates,
165 ))
166 .map(|(shape_function_integral, reference_jacobian_subelement)| {
167 shape_function_integral * reference_jacobian_subelement
168 })
169 .sum::<TensorRank1<P, 9>>();
170 Self::shape_functions_at_integration_points()
171 .iter()
172 .map(|shape_functions_at_integration_point| {
173 shape_functions_at_integration_point * &vector
174 })
175 .collect()
176 }
177 fn reference_jacobians_subelements(
178 reference_nodal_coordinates: &ElementNodalReferenceCoordinates<N>,
179 ) -> ScalarList<Q> {
180 Self::shape_functions_gradients_at_integration_points()
181 .iter()
182 .map(|standard_gradient_operator| {
183 reference_nodal_coordinates * standard_gradient_operator
184 })
185 .collect::<ParametricGradientOperators<Q>>()
186 .iter()
187 .map(|parametric_gradient_operator| parametric_gradient_operator.determinant())
188 .collect()
189 }
190 fn shape_functions_at_integration_points() -> ShapeFunctionsAtIntegrationPoints<G, P> {
191 const DIAG: Scalar = 0.585_410_196_624_968_5;
192 const OFF: Scalar = 0.138_196_601_125_010_5;
193 [
194 [DIAG, OFF, OFF, OFF],
195 [OFF, DIAG, OFF, OFF],
196 [OFF, OFF, DIAG, OFF],
197 [OFF, OFF, OFF, DIAG],
198 ]
199 .into()
200 }
201 fn shape_function_integrals() -> ShapeFunctionIntegrals<Q, P> {
202 [
203 [200.0, 40.0, 40.0, 40.0],
204 [40.0, 200.0, 40.0, 40.0],
205 [40.0, 40.0, 200.0, 40.0],
206 [40.0, 40.0, 40.0, 200.0],
207 [30.0, 70.0, 30.0, 30.0],
208 [10.0, 50.0, 50.0, 50.0],
209 [30.0, 30.0, 30.0, 70.0],
210 [50.0, 50.0, 10.0, 50.0],
211 [50.0, 50.0, 50.0, 10.0],
212 [30.0, 30.0, 70.0, 30.0],
213 [50.0, 10.0, 50.0, 50.0],
214 [70.0, 30.0, 30.0, 30.0],
215 ]
216 .into()
217 }
218 fn shape_function_integrals_products() -> ShapeFunctionIntegralsProducts<Q, P> {
219 [
220 [
221 [128.0, 24.0, 24.0, 24.0],
222 [24.0, 8.0, 4.0, 4.0],
223 [24.0, 4.0, 8.0, 4.0],
224 [24.0, 4.0, 4.0, 8.0],
225 ],
226 [
227 [8.0, 24.0, 4.0, 4.0],
228 [24.0, 128.0, 24.0, 24.0],
229 [4.0, 24.0, 8.0, 4.0],
230 [4.0, 24.0, 4.0, 8.0],
231 ],
232 [
233 [8.0, 4.0, 24.0, 4.0],
234 [4.0, 8.0, 24.0, 4.0],
235 [24.0, 24.0, 128.0, 24.0],
236 [4.0, 4.0, 24.0, 8.0],
237 ],
238 [
239 [8.0, 4.0, 4.0, 24.0],
240 [4.0, 8.0, 4.0, 24.0],
241 [4.0, 4.0, 8.0, 24.0],
242 [24.0, 24.0, 24.0, 128.0],
243 ],
244 [
245 [7.0, 13.0, 5.0, 5.0],
246 [13.0, 31.0, 13.0, 13.0],
247 [5.0, 13.0, 7.0, 5.0],
248 [5.0, 13.0, 5.0, 7.0],
249 ],
250 [
251 [1.0, 3.0, 3.0, 3.0],
252 [3.0, 17.0, 15.0, 15.0],
253 [3.0, 15.0, 17.0, 15.0],
254 [3.0, 15.0, 15.0, 17.0],
255 ],
256 [
257 [7.0, 5.0, 5.0, 13.0],
258 [5.0, 7.0, 5.0, 13.0],
259 [5.0, 5.0, 7.0, 13.0],
260 [13.0, 13.0, 13.0, 31.0],
261 ],
262 [
263 [17.0, 15.0, 3.0, 15.0],
264 [15.0, 17.0, 3.0, 15.0],
265 [3.0, 3.0, 1.0, 3.0],
266 [15.0, 15.0, 3.0, 17.0],
267 ],
268 [
269 [17.0, 15.0, 15.0, 3.0],
270 [15.0, 17.0, 15.0, 3.0],
271 [15.0, 15.0, 17.0, 3.0],
272 [3.0, 3.0, 3.0, 1.0],
273 ],
274 [
275 [7.0, 5.0, 13.0, 5.0],
276 [5.0, 7.0, 13.0, 5.0],
277 [13.0, 13.0, 31.0, 13.0],
278 [5.0, 5.0, 13.0, 7.0],
279 ],
280 [
281 [17.0, 3.0, 15.0, 15.0],
282 [3.0, 1.0, 3.0, 3.0],
283 [15.0, 3.0, 17.0, 15.0],
284 [15.0, 3.0, 15.0, 17.0],
285 ],
286 [
287 [31.0, 13.0, 13.0, 13.0],
288 [13.0, 7.0, 5.0, 5.0],
289 [13.0, 5.0, 7.0, 5.0],
290 [13.0, 5.0, 5.0, 7.0],
291 ],
292 ]
293 .into()
294 }
295 fn shape_functions_gradients_at_integration_points() -> StandardGradientOperators<M, N, Q> {
296 [
297 [
298 [-2.0, -2.0, -2.0],
299 [0.0, 0.0, 0.0],
300 [0.0, 0.0, 0.0],
301 [0.0, 0.0, 0.0],
302 [2.0, 0.0, 0.0],
303 [0.0, 0.0, 0.0],
304 [0.0, 2.0, 0.0],
305 [0.0, 0.0, 2.0],
306 [0.0, 0.0, 0.0],
307 [0.0, 0.0, 0.0],
308 ],
309 [
310 [0.0, 0.0, 0.0],
311 [2.0, 0.0, 0.0],
312 [0.0, 0.0, 0.0],
313 [0.0, 0.0, 0.0],
314 [-2.0, -2.0, -2.0],
315 [0.0, 2.0, 0.0],
316 [0.0, 0.0, 0.0],
317 [0.0, 0.0, 0.0],
318 [0.0, 0.0, 2.0],
319 [0.0, 0.0, 0.0],
320 ],
321 [
322 [0.0, 0.0, 0.0],
323 [0.0, 0.0, 0.0],
324 [0.0, 2.0, 0.0],
325 [0.0, 0.0, 0.0],
326 [0.0, 0.0, 0.0],
327 [2.0, 0.0, 0.0],
328 [-2.0, -2.0, -2.0],
329 [0.0, 0.0, 0.0],
330 [0.0, 0.0, 0.0],
331 [0.0, 0.0, 2.0],
332 ],
333 [
334 [0.0, 0.0, 0.0],
335 [0.0, 0.0, 0.0],
336 [0.0, 0.0, 0.0],
337 [0.0, 0.0, 2.0],
338 [0.0, 0.0, 0.0],
339 [0.0, 0.0, 0.0],
340 [0.0, 0.0, 0.0],
341 [-2.0, -2.0, -2.0],
342 [2.0, 0.0, 0.0],
343 [0.0, 2.0, 0.0],
344 ],
345 [
346 [0.0, 0.0, 0.0],
347 [0.0, 0.0, 0.0],
348 [0.0, 0.0, 0.0],
349 [0.0, 0.0, 0.0],
350 [-2.0 / 3.0, -2.0, -2.0],
351 [4.0 / 3.0, 2.0, 0.0],
352 [-2.0 / 3.0, 0.0, 0.0],
353 [-2.0 / 3.0, 0.0, 0.0],
354 [4.0 / 3.0, 0.0, 2.0],
355 [-2.0 / 3.0, 0.0, 0.0],
356 ],
357 [
358 [0.0, 0.0, 0.0],
359 [0.0, 0.0, 0.0],
360 [0.0, 0.0, 0.0],
361 [0.0, 0.0, 0.0],
362 [-2.0 / 3.0, -2.0 / 3.0, -2.0 / 3.0],
363 [4.0 / 3.0, 4.0 / 3.0, -2.0 / 3.0],
364 [-2.0 / 3.0, -2.0 / 3.0, -2.0 / 3.0],
365 [-2.0 / 3.0, -2.0 / 3.0, -2.0 / 3.0],
366 [4.0 / 3.0, -2.0 / 3.0, 4.0 / 3.0],
367 [-2.0 / 3.0, 4.0 / 3.0, 4.0 / 3.0],
368 ],
369 [
370 [0.0, 0.0, 0.0],
371 [0.0, 0.0, 0.0],
372 [0.0, 0.0, 0.0],
373 [0.0, 0.0, 0.0],
374 [0.0, 0.0, -2.0 / 3.0],
375 [0.0, 0.0, -2.0 / 3.0],
376 [0.0, 0.0, -2.0 / 3.0],
377 [-2.0, -2.0, -2.0 / 3.0],
378 [2.0, 0.0, 4.0 / 3.0],
379 [0.0, 2.0, 4.0 / 3.0],
380 ],
381 [
382 [0.0, 0.0, 0.0],
383 [0.0, 0.0, 0.0],
384 [0.0, 0.0, 0.0],
385 [0.0, 0.0, 0.0],
386 [0.0, -4.0 / 3.0, -2.0],
387 [0.0, 2.0 / 3.0, 0.0],
388 [0.0, 2.0 / 3.0, 0.0],
389 [-2.0, -4.0 / 3.0, 0.0],
390 [2.0, 2.0 / 3.0, 2.0],
391 [0.0, 2.0 / 3.0, 0.0],
392 ],
393 [
394 [0.0, 0.0, 0.0],
395 [0.0, 0.0, 0.0],
396 [0.0, 0.0, 0.0],
397 [0.0, 0.0, 0.0],
398 [0.0, -2.0, -4.0 / 3.0],
399 [2.0, 2.0, 2.0 / 3.0],
400 [-2.0, 0.0, -4.0 / 3.0],
401 [0.0, 0.0, 2.0 / 3.0],
402 [0.0, 0.0, 2.0 / 3.0],
403 [0.0, 0.0, 2.0 / 3.0],
404 ],
405 [
406 [0.0, 0.0, 0.0],
407 [0.0, 0.0, 0.0],
408 [0.0, 0.0, 0.0],
409 [0.0, 0.0, 0.0],
410 [0.0, -2.0 / 3.0, 0.0],
411 [2.0, 4.0 / 3.0, 0.0],
412 [-2.0, -2.0 / 3.0, -2.0],
413 [0.0, -2.0 / 3.0, 0.0],
414 [0.0, -2.0 / 3.0, 0.0],
415 [0.0, 4.0 / 3.0, 2.0],
416 ],
417 [
418 [0.0, 0.0, 0.0],
419 [0.0, 0.0, 0.0],
420 [0.0, 0.0, 0.0],
421 [0.0, 0.0, 0.0],
422 [2.0 / 3.0, 0.0, 0.0],
423 [2.0 / 3.0, 0.0, 0.0],
424 [-4.0 / 3.0, 0.0, -2.0],
425 [-4.0 / 3.0, -2.0, 0.0],
426 [2.0 / 3.0, 0.0, 0.0],
427 [2.0 / 3.0, 2.0, 2.0],
428 ],
429 [
430 [0.0, 0.0, 0.0],
431 [0.0, 0.0, 0.0],
432 [0.0, 0.0, 0.0],
433 [0.0, 0.0, 0.0],
434 [2.0 / 3.0, -4.0 / 3.0, -4.0 / 3.0],
435 [2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0],
436 [-4.0 / 3.0, 2.0 / 3.0, -4.0 / 3.0],
437 [-4.0 / 3.0, -4.0 / 3.0, 2.0 / 3.0],
438 [2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0],
439 [2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0],
440 ],
441 ]
442 .into()
443 }
444 fn standard_gradient_operators_transposed() -> StandardGradientOperatorsTransposed<M, N, Q> {
445 [
446 [
447 [-2.0, -2.0, -2.0],
448 [0.0, 0.0, 0.0],
449 [0.0, 0.0, 0.0],
450 [0.0, 0.0, 0.0],
451 [0.0, 0.0, 0.0],
452 [0.0, 0.0, 0.0],
453 [0.0, 0.0, 0.0],
454 [0.0, 0.0, 0.0],
455 [0.0, 0.0, 0.0],
456 [0.0, 0.0, 0.0],
457 [0.0, 0.0, 0.0],
458 [0.0, 0.0, 0.0],
459 ],
460 [
461 [0.0, 0.0, 0.0],
462 [2.0, 0.0, 0.0],
463 [0.0, 0.0, 0.0],
464 [0.0, 0.0, 0.0],
465 [0.0, 0.0, 0.0],
466 [0.0, 0.0, 0.0],
467 [0.0, 0.0, 0.0],
468 [0.0, 0.0, 0.0],
469 [0.0, 0.0, 0.0],
470 [0.0, 0.0, 0.0],
471 [0.0, 0.0, 0.0],
472 [0.0, 0.0, 0.0],
473 ],
474 [
475 [0.0, 0.0, 0.0],
476 [0.0, 0.0, 0.0],
477 [0.0, 2.0, 0.0],
478 [0.0, 0.0, 0.0],
479 [0.0, 0.0, 0.0],
480 [0.0, 0.0, 0.0],
481 [0.0, 0.0, 0.0],
482 [0.0, 0.0, 0.0],
483 [0.0, 0.0, 0.0],
484 [0.0, 0.0, 0.0],
485 [0.0, 0.0, 0.0],
486 [0.0, 0.0, 0.0],
487 ],
488 [
489 [0.0, 0.0, 0.0],
490 [0.0, 0.0, 0.0],
491 [0.0, 0.0, 0.0],
492 [0.0, 0.0, 2.0],
493 [0.0, 0.0, 0.0],
494 [0.0, 0.0, 0.0],
495 [0.0, 0.0, 0.0],
496 [0.0, 0.0, 0.0],
497 [0.0, 0.0, 0.0],
498 [0.0, 0.0, 0.0],
499 [0.0, 0.0, 0.0],
500 [0.0, 0.0, 0.0],
501 ],
502 [
503 [2.0, 0.0, 0.0],
504 [-2.0, -2.0, -2.0],
505 [0.0, 0.0, 0.0],
506 [0.0, 0.0, 0.0],
507 [-2.0 / 3.0, -2.0, -2.0],
508 [-2.0 / 3.0, -2.0 / 3.0, -2.0 / 3.0],
509 [0.0, 0.0, -2.0 / 3.0],
510 [0.0, -4.0 / 3.0, -2.0],
511 [0.0, -2.0, -4.0 / 3.0],
512 [0.0, -2.0 / 3.0, 0.0],
513 [2.0 / 3.0, 0.0, 0.0],
514 [2.0 / 3.0, -4.0 / 3.0, -4.0 / 3.0],
515 ],
516 [
517 [0.0, 0.0, 0.0],
518 [0.0, 2.0, 0.0],
519 [2.0, 0.0, 0.0],
520 [0.0, 0.0, 0.0],
521 [4.0 / 3.0, 2.0, 0.0],
522 [4.0 / 3.0, 4.0 / 3.0, -2.0 / 3.0],
523 [0.0, 0.0, -2.0 / 3.0],
524 [0.0, 2.0 / 3.0, 0.0],
525 [2.0, 2.0, 2.0 / 3.0],
526 [2.0, 4.0 / 3.0, 0.0],
527 [2.0 / 3.0, 0.0, 0.0],
528 [2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0],
529 ],
530 [
531 [0.0, 2.0, 0.0],
532 [0.0, 0.0, 0.0],
533 [-2.0, -2.0, -2.0],
534 [0.0, 0.0, 0.0],
535 [-2.0 / 3.0, 0.0, 0.0],
536 [-2.0 / 3.0, -2.0 / 3.0, -2.0 / 3.0],
537 [0.0, 0.0, -2.0 / 3.0],
538 [0.0, 2.0 / 3.0, 0.0],
539 [-2.0, 0.0, -4.0 / 3.0],
540 [-2.0, -2.0 / 3.0, -2.0],
541 [-4.0 / 3.0, 0.0, -2.0],
542 [-4.0 / 3.0, 2.0 / 3.0, -4.0 / 3.0],
543 ],
544 [
545 [0.0, 0.0, 2.0],
546 [0.0, 0.0, 0.0],
547 [0.0, 0.0, 0.0],
548 [-2.0, -2.0, -2.0],
549 [-2.0 / 3.0, 0.0, 0.0],
550 [-2.0 / 3.0, -2.0 / 3.0, -2.0 / 3.0],
551 [-2.0, -2.0, -2.0 / 3.0],
552 [-2.0, -4.0 / 3.0, 0.0],
553 [0.0, 0.0, 2.0 / 3.0],
554 [0.0, -2.0 / 3.0, 0.0],
555 [-4.0 / 3.0, -2.0, 0.0],
556 [-4.0 / 3.0, -4.0 / 3.0, 2.0 / 3.0],
557 ],
558 [
559 [0.0, 0.0, 0.0],
560 [0.0, 0.0, 2.0],
561 [0.0, 0.0, 0.0],
562 [2.0, 0.0, 0.0],
563 [4.0 / 3.0, 0.0, 2.0],
564 [4.0 / 3.0, -2.0 / 3.0, 4.0 / 3.0],
565 [2.0, 0.0, 4.0 / 3.0],
566 [2.0, 2.0 / 3.0, 2.0],
567 [0.0, 0.0, 2.0 / 3.0],
568 [0.0, -2.0 / 3.0, 0.0],
569 [2.0 / 3.0, 0.0, 0.0],
570 [2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0],
571 ],
572 [
573 [0.0, 0.0, 0.0],
574 [0.0, 0.0, 0.0],
575 [0.0, 0.0, 2.0],
576 [0.0, 2.0, 0.0],
577 [-2.0 / 3.0, 0.0, 0.0],
578 [-2.0 / 3.0, 4.0 / 3.0, 4.0 / 3.0],
579 [0.0, 2.0, 4.0 / 3.0],
580 [0.0, 2.0 / 3.0, 0.0],
581 [0.0, 0.0, 2.0 / 3.0],
582 [0.0, 4.0 / 3.0, 2.0],
583 [2.0 / 3.0, 2.0, 2.0],
584 [2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0],
585 ],
586 ]
587 .into()
588 }
589}