File size: 27,736 Bytes
985c397
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
# SPDX-License-Identifier: BSD-3-Clause

# Copyright (c) 2011-2012, Thomas Paviot (tpaviot@gmail.com)
# All rights reserved.

# This file is part of the StepClassLibrary (SCL).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#   Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
#
#   Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
#
#   Neither the name of the <ORGANIZATION> nor the names of its contributors may
#   be used to endorse or promote products derived from this software without
#   specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

__doc__ = "This module defines EXPRESS built in constants and functions"
import math

from SimpleDataTypes import *
from BaseType import Aggregate
from AggregationDataTypes import *

SCL_float_epsilon = 1e-7
# Builtin constants

# EXPRESS definition:
# ===================
# 14.1 CONST_E is a REAL constant representing the mathematical value e, the base of the natural
# logarithm function (ln).
CONST_E = REAL(math.pi)

# EXPRESS definition:
# ===================
# 14.2 Indeterminate
# The indeterminate symbol (?) stands for an ambiguous value. It is compatible with all data
# types.
# NOTE - The most common use of indeterminate (?) is as the upper bound specification of a bag,
# list or set. This usage represents the notion that the size of the aggregate value defined by the
# aggregation data type is unbounded.
# python note: indeterminate value is mapped to None in aggregate bounds

# EXPRESS definition:
# ===================
# 14.3 False
# false is a logical constant representing the logical notion of falsehood. It is compatible with
# the boolean and logical data types.
FALSE = False

# EXPRESS definition:
# ===================
# 14.4 Pi
# PI is a REAL constant representing the mathematical value π, the ratio of a circle's circumference
# to its diameter.
PI = REAL(math.pi)

# EXPRESS definition:
# ===================
# 14.5 Self
# SELF refers to the current entity instance or type value. self may appear within an entity
# declaration, a type declaration or an entity constructor.
# NOTE - SELF is not a constant, but behaves as one in every context in which it can appear.
# python note: SELF is not mapped to any constant, but is mapper to self

# EXPRESS definition:
# ===================
# 14.6 True
# true is a logical constant representing the logical notion of truth. It is compatible with the
# boolean and logical data types.
TRUE = True

# EXPRESS definition:
# ===================
# 14.7 Unknown
# unknown is a logical constant representing that there is insufficient information available to
# be able to evaluate a logical condition. It is compatible with the logical data type, but not
# with the boolean data type.
# @TODO: define UNKNOWN in python

#
# Builtin Functions
# 15 Built-in functions
# All functions (and mathematical operations in general) are assumed to evaluate to exact results.
# The prototype for each of the built-in functions is given to show the type of the formal parameters
# and the result.
#

# EXPRESS definition:
# ===================
# 15.1 Abs - arithmetic function
# FUNCTION ABS ( V:NUMBER ) : NUMBER;
# The abs function returns the absolute value of a number.
# Parameters : V is a number.
# Result : The absolute value of V. The returned data type is identical to the data type of V.
# EXAMPLE 125 { ABS ( -10 ) --> 10
# Python definition:
# ==================
# ABS is mapped to python abs builtin function
def ABS(V):
    if not isinstance(V, NUMBER):
        raise TypeError("ABS function takes a NUMBER parameter")
    return type(V)(abs(V))


# EXPRESS definition:
# ===================
# 15.2 ACos - arithmetic function
# FUNCTION ACOS ( V:NUMBER ) : REAL;
# The acos function returns the angle given a cosine value.
# Parameters : V is a number which is the cosine of an angle.
# Result : The angle in radians (0 <= result <= pi) whose cosine is V.
# Conditions : -1.0=<V<=1.0
# EXAMPLE 126 { ACOS ( 0.3 ) --> 1.266103...
# Python definition:
# ==================
# ACOS is mapped to python math.acos builtin function
def ACOS(V):
    if not isinstance(V, NUMBER):
        raise TypeError("ACOS function takes a NUMBER parameter")
    return REAL(math.acos(V))


# it's the same for ASIN and ATAN
def ASIN(V):
    if not isinstance(V, NUMBER):
        raise TypeError("ASIN function takes a NUMBER parameter")
    return REAL(math.asin(V))


# EXPRESS definition:
# ===================
# 15.3 ATan - arithmetic function
# FUNCTION ATAN ( V1:NUMBER; V2:NUMBER ) : REAL;
# The atan function returns the angle given a tangent value of V , where V is given by the
# expression V = V1/V2.
# Parameters :
# a) V1 is a number.
# b) V2 is a number.
# Result : The angle in radians (-pi/2<=result<=pi/2) whose tangent is V. If V2 is zero, the result
# is pi/2 or -pi/2 depending on the sign of V1.
# Conditions : Both V1 and V2 shall not be zero.
# EXAMPLE 128 { ATAN ( -5.5, 3.0 ) --> -1.071449...
def ATAN(V1, V2):
    if not isinstance(V1, NUMBER) and not isinstance(V2, NUMBER):
        raise TypeError("ATAN function takes 2 NUMBER parameters")
    if V2 == 0:
        if V1 > 0:
            return REAL(math.pi / 2)
        elif V1 < 0:
            return REAL(-math.pi / 2)
        else:
            raise ValueError("ATAN parameters can be both equal to zero")
    else:
        return REAL(math.atan(float(V1) / float(V2)))


# EXPRESS definition:
# ===================
# 15.5 BLength - binary function
# FUNCTION BLENGTH ( V:BINARY ) : INTEGER;
# The blength function returns the number of bits in a binary.
# Parameters : V is a binary value.
# Result : The returned value is the actual number of bits in the binary value passed.
# EXAMPLE 129
# LOCAL
# n : NUMBER;
# x : BINARY := %01010010 ;
# END_LOCAL;
# ...
# n := BLENGTH ( x ); -- n is assigned the value 8
def BLENGTH(V):
    if not isinstance(V, BINARY):
        raise TypeError("BLENGTH function takes one BINARY parameter")
    return INTEGER(len(V))


# EXPRESS definition:
# ===================
# 15.6 Cos - arithmetic function
# FUNCTION COS ( V:NUMBER ) : REAL;
# The cos function returns the cosine of an angle.
# Parameters : V is a number which is an angle in radians.
# Result : The cosine of V (-1.0<=result<=1.0).
# EXAMPLE 130 { COS ( 0.5 ) --> 8.77582...E-1
#
# 15.21 Sin - arithmetic function
# FUNCTION SIN ( V:NUMBER ) : REAL;
# The sin function returns the sine of an angle.
# Parameters : V is a number representing an angle expressed in radians.
# Result : The sine of V (-1.0 <= result <= 1.0).
# EXAMPLE 144 { SIN ( PI ) --> 0.0
#
def COS(V):
    if not isinstance(V, NUMBER):
        raise TypeError("COS function takes a NUMBER parameter")
    return REAL(math.cos(V))


def SIN(V):
    if not isinstance(V, NUMBER):
        raise TypeError("SIN function takes a NUMBER parameter")
    return REAL(math.sin(V))


# EXPRESS definition:
# ===================
# 15.7 Exists - general function
# FUNCTION EXISTS ( V:GENERIC ) : BOOLEAN;
# The exists function returns true if a value exists for the input parameter, or false if no value
# exists for it. The exists function is useful for checking if values have been given to optional
# attributes, or if variables have been initialized.
# Parameters : V is an expression which results in any type.
# Result : true or false depending on whether V has an actual or indeterminate (?) value.
# EXAMPLE 131 { IF EXISTS ( a ) THEN ...
def EXISTS(V):
    if V == None:
        return False
    else:
        return True


# EXPRESS definition:
# ===================
# 15.8 Exp - arithmetic function
# FUNCTION EXP ( V:NUMBER ) : REAL;
# The exp function returns e (the base of the natural logarithm system) raised to the power V.
# Parameters : V is a number.
# Result : The value eV .
# EXAMPLE 132 { EXP ( 10 ) --> 2.202646...E+4
def EXP(V):
    if not isinstance(V, NUMBER):
        raise TypeError("EXP function takes a NUMBER parameter")
    return REAL(math.exp(V))


# EXPRESS definition:
# ===================
# 15.9 Format - general function
# FUNCTION FORMAT(N:NUMBER; F:STRING):STRING;
# The format returns a formatted string representation of a number.
# Parameters :
# a) N is a number (integer or real).
# b) F is a string containing formatting commands.
# Result : A string representation of N formatted according to F. Rounding is applied to the
# string representation if necessary.
# The formatting string contains special characters to indicate the appearance of the result. The
# formatting string can be written in three ways:
# a) The formatting string can give a symbolic description of the output representation.
# b) The formatting string can give a picture description of the output representation.
# c) When the formatting string is empty, a standard output representation is produced.
# Table 20:
# Number Format Display Comment
# 10 +7I ' +10' Zero suppression
# 10 +07I '+000010' Zeros not suppressed
# 10 10.3E ' 1.000E+01'
# 123.456789 8.2F ' 123.46'
# 123.456789 8.2E '1.23E+02'
# 123.456789 08.2E '0.12E+02' Preceding zero forced
# 9.876E123 8.2E '9.88E+123' Exponent part is 3 characters
# and width ignored
# 32.777 6I ' 33' Rounded
# Python definition
# =================
# python string formatting is obtained from the val function
# @TODO: implement a safe eval or provide another implementation
# that avoids unsafe eval python builtin function.
def FORMAT(N, F):
    if not isinstance(N, NUMBER):
        raise TypeError("FORMAT function takes a NUMBER parameter")
    if not isinstance(F, STRING):
        raise TypeError("FORMAT function takes a NUMBER parameter")
    py_formatting = F.lower()
    string_to_evaluate = "'%"
    string_to_evaluate += "%s'" % py_formatting
    string_to_evaluate += "%"
    string_to_evaluate += "%s" % N
    result = eval(string_to_evaluate).upper()
    return STRING(result)


# EXPRESS definition:
# ===================
# 15.10 HiBound - arithmetic function
# FUNCTION HIBOUND ( V:AGGREGATE OF GENERIC ) : INTEGER;
# The hibound function returns the declared upper index of an array or the declared upper
# bound of a bag, list or set.
# Parameters : V is an aggregate value.
# Result :
# a) When V is an array the returned value is the declared upper index.
# b) When V is a bag, list or set the returned value is the declared upper bound; if there
# are no bounds declared or the upper bound is declared to be indeterminate (?) indeterminate
# (?) is returned.
# EXAMPLE 133 { Usage of hibound function on nested aggregate values.
# LOCAL
# a : ARRAY[-3:19] OF SET[2:4] OF LIST[0:?] OF INTEGER;
# h1, h2, h3 : INTEGER;
# END_LOCAL;
# ...
# a[-3][1][1] := 2; -- places a value in the list
# ...
# h1 := HIBOUND(a); -- =19 (upper bound of array)
# h2 := HIBOUND(a[-3]); -- = 4 (upper bound of set)
# h3 := HIBOUND(a[-3][1]); -- = ? (upper bound of list (unbounded))
def HIBOUND(V):
    if not isinstance(V, Aggregate):
        raise TypeError("HIBOUND takes an aggregate of generic")
    return V.get_hibound()


# EXPRESS definition:
# ===================
# 15.11 HiIndex - arithmetic function
# FUNCTION HIINDEX ( V:AGGREGATE OF GENERIC ) : INTEGER;
# The hiindex function returns the upper index of an array or the number of elements in a bag,
# list or set
# Parameters : V is an aggregate value.
# Result :
# a) When V is an array, the returned value is the declared upper index.
# b) When V is a bag, list or set, the returned value is the actual number of elements in
# the aggregate value.
# EXAMPLE 134 { Usage of hiindex function on nested aggregate values.
# LOCAL
# a : ARRAY[-3:19] OF SET[2:4] OF LIST[0:?] OF INTEGER;
# h1, h2, h3 : INTEGER;
# END_LOCAL;
# a[-3][1][1] := 2; -- places a value in the list
# h1 := HIINDEX(a); -- = 19 (upper bound of array)
# h2 := HIINDEX(a[-3]); -- = 1 (size of set) -- this is invalid with respect
# -- to the bounds on the SET
# h3 := HIINDEX(a[-3][1]); -- = 1 (size of list)
def HIINDEX(V):
    if not isinstance(V, Aggregate):
        raise TypeError("HIINDEX takes an aggregate of generic")
    return V.get_hiindex()


# EXPRESS definition:
# ===================
# 15.12 Length - string function
# FUNCTION LENGTH ( V:STRING ) : INTEGER;
# The length function returns the number of characters in a string.
# Parameters : V is a string value.
# Result : The returned value is the number of characters in the string and shall be greater than
# or equal to zero.
# EXAMPLE 135 - Usage of the length function.
# LOCAL
# n : NUMBER;
# x1 : STRING := 'abc';
# x2 : STRING := "000025FF000101B5;
# END_LOCAL;
# ...
# n := LENGTH ( x1 ); -- n is assigned the value 3
# n := LENGTH ( x2 ); -- n is assigned the value 2
def LENGTH(V):
    if not isinstance(V, STRING):
        raise TypeError("LENGTH take a STRING parameter")
    return INTEGER(len(V))


# EXPRESS definition:
# ===================
# 15.13 LoBound - arithmetic function
# FUNCTION LOBOUND ( V:AGGREGATE OF GENERIC ) : INTEGER;
# The lobound function returns the declared lower index of an array, or the declared lower
# bound of a bag, list or set.
# Parameters : V is an aggregate value.
# Result :
# a) When V is an array the returned value is the declared lower index.
# b) When V is a bag, list or set the returned value is the declared lower bound; if no
# lower bound is declared, zero (0) is returned.
# EXAMPLE 136 { Usage of lobound function on nested aggregate values.
# LOCAL
# a : ARRAY[-3:19] OF SET[2:4] OF LIST[0:?] OF INTEGER;
# h1, h2, h3 : INTEGER;
# END_LOCAL;
# ...
# h1 := LOBOUND(a); -- =-3 (lower index of array)
# h2 := LOBOUND(a[-3]); -- = 2 (lower bound of set)
# h3 := LOBOUND(a[-3][1]); -- = 0 (lower bound of list)
def LOBOUND(V):
    if not isinstance(V, Aggregate):
        raise TypeError("HIBOUND takes an aggregate of generic")
    return V.get_lobound()


# EXPRESS definition:
# ===================
# 15.14 Log - arithmetic function
# FUNCTION LOG ( V:NUMBER ) : REAL;
# The log function returns the natural logarithm of a number.
# Parameters : V is a number.
# Result : A real number which is the natural logarithm of V.
# Conditions : V > 0:0
# EXAMPLE 137 { LOG ( 4.5 ) --> 1.504077...E0
# 15.15 Log2 - arithmetic function
# FUNCTION LOG2 ( V:NUMBER ) : REAL;
# The log2 function returns the base two logarithm of a number.
# Parameters : V is a number.
# Result : A real number which is the base two logarithm of V.
# Conditions : V > 0:0
# EXAMPLE 138 { LOG2 ( 8 ) --> 3.00...E0
# 15.16 Log10 - arithmetic function
# FUNCTION LOG10 ( V:NUMBER ) : REAL;
# The log10 function returns the base ten logarithm of a number.
# Parameters : V is a number.
# Result : A real number which is the base ten logarithm of V.
# Conditions : V > 0:0
# EXAMPLE 139 { LOG10 ( 10 ) --> 1.00...E0
def LOG(V):
    if not isinstance(V, NUMBER):
        raise TypeError("LOG function takes a NUMBER parameter")
    return REAL(math.log(V))


def LOG2(V):
    if not isinstance(V, NUMBER):
        raise TypeError("LOG2 function takes a NUMBER parameter")
    return REAL(math.log(V, 2))


def LOG10(V):
    if not isinstance(V, NUMBER):
        raise TypeError("LOG10 function takes a NUMBER parameter")
    return REAL(math.log10(V))


# EXPRESS definition:
# ===================
# 15.17 LoIndex - arithmetic function
# FUNCTION LOINDEX ( V:AGGREGATE OF GENERIC ) : INTEGER;
# The loindex function returns the lower index of an aggregate value.
# Parameters : V is an aggregate value.
# Result :
# a) When V is an array the returned value is the declared lower index.
# b) When V is a bag, list or set, the returned value is 1 (one).
# EXAMPLE 140 { Usage of loindex function on nested aggregate values.
# LOCAL
# a : ARRAY[-3:19] OF SET[2:4] OF LIST[0:?] OF INTEGER;
# h1, h2, h3 : INTEGER;
# END_LOCAL;
# ...
# h1 := LOINDEX(a); -- =-3 (lower bound of array)
# h2 := LOINDEX(a[-3]); -- = 1 (for set)
# h3 := LOINDEX(a[-3][1]); -- = 1 (for list)
def LOINDEX(V):
    if not isinstance(V, Aggregate):
        raise TypeError("LOINDEX takes an aggregate of generic")
    return V.get_loindex()


# EXPRESS definition:
# ===================
# 15.18 NVL - null value function
# FUNCTION NVL(V:GENERIC:GEN1; SUBSTITUTE:GENERIC:GEN1):GENERIC:GEN1;
# The nvl function returns either the input value or an alternate value in the case where the input
# has a indeterminate (?) value.
# Parameters :
# a) V is an expression which is of any type.
# b) SUBSTITUTE is an expression which shall not evaluate to indeterminate (?).
# Result : When V is not indeterminate (?) that value is returned. Otherwise, SUBSTITUTE is
# returned.
# EXAMPLE 141 { ENTITY unit_vector;
# x, y : REAL;
# z : OPTIONAL REAL;
# WHERE
# x**2 + y**2 + NVL(z, 0.0)**2 = 1.0;
# END_ENTITY;
# The nvl function is used to supply zero (0.0) as the value of Z when Z is indeterminate (?).
def NVL(V, SUBSTITUTE):
    if V is not None:
        return V
    else:
        return SUBSTITUTE


# EXPRESS definition:
# ===================
# 15.19 Odd - arithmetic function
# FUNCTION ODD ( V:INTEGER ) : LOGICAL;
# The odd function returns true or false depending on whether a number is odd or even.
# Parameters : V is an integer number.
# Result : When V MOD 2 = 1 true is returned; otherwise false is returned.
# Conditions : Zero is not odd.
# EXAMPLE 142 { ODD ( 121 ) --> TRUE
def ODD(V):
    if not isinstance(V, INTEGER):
        raise TypeError("ODD takes an INTEGER")
    if V % 2 == 0:
        return False
    else:
        return True


# EXPRESS definition:
# ===================
# 15.20 RolesOf - general function
# FUNCTION ROLESOF ( V:GENERIC ) : SET OF STRING;
# The rolesof function returns a set of strings containing the fully qualified names of the roles
# played by the specified entity instance. A fully qualified name is defined to be the name of the
# attribute qualified by the name of the schema and entity in which this attribute is declared (i.e.
#'SCHEMA.ENTITY.ATTRIBUTE').
# Parameters : V is any instance of an entity data type.
# Result : A set of string values (in upper case) containing the fully qualified names of the
# attributes of the entity instances which use the instance V.
# When a named data type is used or referenced, the schema and the name in that schema,
# if renamed, are also returned. Since use statements may be chained, all the chained schema
# names and the name in each schema are returned.
# EXAMPLE 143 { This example shows that a point might be used as the centre of a circle. The
# rolesof function determines what roles an entity instance actually plays.
# SCHEMA that_schema;
# ENTITY point;
# x, y, z : REAL;
# END_ENTITY;
# ENTITY line;
# start,
# end : point;
# END_ENTITY;
# END_SCHEMA;
# SCHEMA this_schema;
# USE FROM that_schema (point,line);
# CONSTANT
# origin : point := point(0.0, 0.0, 0.0);
# END_CONSTANT;
# ENTITY circle;
# centre : point;
# axis : vector;
# radius : REAL;
# END_ENTITY;
# ...
# LOCAL
# p : point := point(1.0, 0.0, 0.0);
# c : circle := circle(p, vector(1,1,1), 1.0);
# l : line := line(p, origin);
# END_LOCAL;
# ...
# IF 'THIS_SCHEMA.CIRCLE.CENTRE' IN ROLESOF(p) THEN -- true
# ...
# IF 'THIS_SCHEMA.LINE.START' IN ROLESOF(p) THEN -- true
# ...
# IF 'THAT_SCHEMA.LINE.START' IN ROLESOF(p) THEN -- true
# ...
# IF 'THIS_SCHEMA.LINE.END' IN ROLESOF(p) THEN -- false
#
# Python note:
# @TODO: implement the ROLESOF function
def ROLESOF(V):
    raise NotImplemented("Function ROLESOF not implemented")


# EXPRESS definition:
# ===================
# 15.22 SizeOf - aggregate function
# FUNCTION SIZEOF ( V:AGGREGATE OF GENERIC ) : INTEGER;
# The sizeof function returns the number of elements in an aggregate value.
# Parameters : V is an aggregate value.
# Result :
# a) When V is an array the returned value is its declared number of elements in the
# aggregation data type.
# b) When V is a bag, list or set, the returned value is the actual number of elements in
# the aggregate value.
# EXAMPLE 145 { LOCAL
# n : NUMBER;
# y : ARRAY[2:5] OF b;
# END_LOCAL;
# ...
# n := SIZEOF (y); -- n is assigned the value 4
def SIZEOF(V):
    if not isinstance(V, Aggregate):
        raise TypeError("SIZEOF takes an aggregate of generic")
    return V.get_size()


# EXPRESS definition:
# ===================
# 15.23 Sqrt - arithmetic function
# FUNCTION SQRT ( V:NUMBER ) : REAL;
# The sqrt function returns the non-negative square root of a number.
# Parameters : V is any non-negative number.
# Result : The non-negative square root of V.
# Conditions : V >= 0:0
# EXAMPLE 146 - SQRT ( 121 ) --> 11.0
def SQRT(V):
    if not isinstance(V, NUMBER):
        raise TypeError("SQRT function takes a NUMBER parameter")
    if V < 0.0:
        raise ValueError("SQRT takes a non-negative parameter")
    return REAL(math.sqrt(V))


# EXPRESS definition:
# ===================
# 15.24 Tan - arithmetic function
# FUNCTION TAN ( V:NUMBER ) : REAL;
# The tan function returns the tangent of an angle.
# Parameters : V is a number representing an angle expressed in radians.
# Result : The tangent of the angle. If the angle is npi/2, where n is an odd integer, indeterminate
# (?) is returned.
# EXAMPLE 147 - TAN ( 0.0 ) --> 0.0
def TAN(V):
    if not isinstance(V, NUMBER):
        raise TypeError("TAN function takes a NUMBER parameter")
    # check if angle is npi/2 where n is an odd integer
    a = V / (PI / 2)
    if abs(a % 2 - 1.0) < SCL_float_epsilon:
        return None
    else:
        return REAL(math.tan(V))


# EXPRESS definition:
# ===================
# 15.25 TypeOf - general function
# FUNCTION TYPEOF ( V:GENERIC ) : SET OF STRING;
# The typeof function returns a set of strings that contains the names of all the data types
# of which the parameter is a member. Except for the simple data types (binary, boolean,
# integer, logical, number, real, and string) and the aggregation data types (array, bag,
# list, set) these names are qualified by the name of the schema which contains the definition of
# the type.
# NOTE 1 { The primary purpose of this function is to check whether a given value (variable, at-
# tribute value) can be used for a certain purpose, e.g. to ensure assignment compatibility between
# two values. It may also be used if different subtypes or specializations of a given type have to be
# treated differently in some context.
# Parameters : V is a value of any type.
# Result : The contents of the returned set of string values are the names (in upper case) of all
# types the value V is a member of. Such names are qualified by the name of the schema which
# contains the definition of the type ('SCHEMA.TYPE') if it is neither a simple data type nor an
# aggregation data type. It may be derived by the following algorithm (which is given here for
# specification purposes rather than to prescribe any particular type of implementation)
def TYPEOF(V):
    # Create the set to return
    v_types = set()
    # append the type of V to the set
    try:  # it's a class
        to_add = V.__name__.upper()
    except AttributeError:  # it's an instance, first retrieve the type
        to_add = type(V).__name__.upper()
    if not to_add in ["FLOAT", "INT", "AGGREGATE"]:
        v_types.add(to_add)
    # recursively adds the base class names
    for base_type in type(V).__bases__:
        # print base_type
        if not base_type == object:
            v_types = v_types.union(TYPEOF(base_type))
    # finally, converts the v_types set to SET
    return v_types


# EXPRESS definition:
# ===================
# 15.26 UsedIn - general function
# FUNCTION USEDIN ( T:GENERIC; R:STRING) : BAG OF GENERIC;
# The usedin function returns each entity instance that uses a specified entity instance in a
# specified role.
def USEDIN(T, R):
    raise NotImplemented("USEDIN function not yet implemented.")


# EXPRESS definition:
# ===================
# 15.27 Value - arithmetic function
# FUNCTION VALUE ( V:STRING ) : NUMBER;
# The value function returns the numeric representation of a string.
# Parameters : V is a string containing either a real or integer literal.
# Result : A number corresponding to the string representation. If it is not possible to interpret
# the string as either a real or integer literal, indeterminate (?) is returned.
# EXAMPLE 151 { VALUE ( '1.234' ) --> 1.234 (REAL)
# VALUE ( '20' ) --> 20 (INTEGER)
# VALUE ( 'abc' ) --> ? null
def VALUE(V):
    if not isinstance(V, STRING):
        raise TypeError("VALUE function takes a NUMBER parameter")
    # first try to instantiate an INTEGER from the string:
    try:
        return INTEGER(V)
    except Exception:
        pass  # not possible, try to cast to REAL
    try:
        return REAL(V)
    except Exception:
        pass
    # else return None
    return None


# EXPRESS definition:
# ===================
# 15.28 Value in - membership function
# FUNCTION VALUE_IN ( C:AGGREGATE OF GENERIC:GEN; V:GENERIC:GEN ) : LOGICAL;
# The value in function returns a logical value depending on whether or not a particular value
# is a member of an aggregation.
# Parameters :
# a) C is an aggregation of any type.
# b) V is an expression which is assignment compatible with the base type of C.
# Result :
# a) If either V or C is indeterminate (?), unknown is returned.
# b) If any element of C has a value equal to the value of V, true is returned.
# c) If any element of C is indeterminate (?), unknown is returned.
# d) Otherwise false is returned.
# EXAMPLE 152 { The following test ensures that there is at least one point which is positioned at
# the origin.
# LOCAL
# points : SET OF point;
# END_LOCAL;
# ...
# IF VALUE_IN(points, point(0.0, 0.0, 0.0)) THEN ...
def VALUE_IN(C, V):
    if not isinstance(C, Aggregate):
        raise TypeError("VALUE_IN method takes an aggregate as first parameter")
    raise NotImplemented("VALUE_IN function not yet implemented")


# EXPRESS definition:
# ===================
# 15.29 Value unique - uniqueness function
# FUNCTION VALUE UNIQUE ( V:AGGREGATE OF GENERIC) : LOGICAL;
# The value unique function returns a logical value depending on whether or not the elements
# of an aggregation are value unique.
# Parameters : V is an aggregation of any type.
# Result :
# a) If V is indeterminate (?), unknown is returned.
# b) If any any two elements of V are value equal, false is returned.
# c) If any element of V is indeterminate (?), unknown is returned.
# d) Otherwise true is returned.
# EXAMPLE 153 { The following test ensures that each point is placed at a different position, (by
# definition they are distinct, i.e., instance unique).
# IF VALUE_UNIQUE(points) THEN ...
def VALUE_UNIQUE(V):
    if not isinstance(V, Aggregate):
        raise TypeError("VALUE_UNIQUE method takes an aggregate as first parameter")
    return V.get_value_unique()