REPRESENT: A Complete Solution Revision 3.3 2014-03-17 The following specification builds upon an earlier paper 'REPRESENT - A Report and ANS FORTH Proposal' which details deficiencies in the Forth-94 float-to-string conversion primitive REPRESENT. --------------------------------------------------------------------- 1. MAX-FLOAT-DIGITS 2. REPRESENT-CHARS 3. REPRESENT 4. Sample Applications 5. History --------------------------------------------------------------------- 1. MAX-FLOAT-DIGITS 1.1 Rationale There is no easy way for applications to know the number of usable digits a system can output when printing a floating-point number. System designers know and use this value in functions such as REPRESENT and SET-PRECISION. Portable applications need this value in order to determine: - maximum precision a given Forth system can usefully display - upper value to use with SET-PRECISION This proposal adds the environment query string MAX-FLOAT-DIGITS. MAX-FLOAT-DIGITS should not be confused with the intrinsic precision of a floating-point number. The Forth Vendors Group Floating Point Standard has an equivalent function named F#PLACES. 1.2 Proposal Add to Table 12.2 - Environmental query strings String Value Constant? Meaning data type ------ --------- --------- ------- MAX-FLOAT-DIGITS u yes largest number of usable digits available from REPRESENT 1.3 Implementation The value of MAX-FLOAT-DIGITS is implementation dependent. If the constant it represents cannot be found in the system documentation or source code, the following tests may assist in determining it empirically. : TEST1 ( -- ) \ scan forward 80 chars 80 SET-PRECISION 1E0 3E0 F/ PAD 80 REPRESENT 0= ABORT" failed" DROP DROP PAD 0 80 0 DO OVER I CHARS + C@ [CHAR] 3 - IF LEAVE THEN 1+ LOOP SWAP DROP CR ." Max usable digits = " . ; : TEST2 ( -- ) \ scan forward PRECISION chars 80 SET-PRECISION 1E0 3E0 F/ PAD PRECISION REPRESENT 0= ABORT" failed" DROP DROP PAD 0 PRECISION 0 DO OVER I CHARS + C@ [CHAR] 3 - IF LEAVE THEN 1+ LOOP SWAP DROP CR ." Max usable digits = " . ; : TEST3 ( -- ) \ trim trailing '0's 80 SET-PRECISION 1E0 3E0 F/ PAD 80 REPRESENT 0= ABORT" failed" DROP DROP PAD 80 BEGIN DUP WHILE 1- 2DUP CHARS + C@ [CHAR] 0 - UNTIL 1+ THEN SWAP DROP CR ." Max usable digits = " . ; --------------------------------------------------------------------- 2. REPRESENT-CHARS 2.1 Rationale To support non-number representations with lengths in excess of MAX-FLOAT-DIGITS (e.g. 32-bit IEEE NANs with payload information) this proposal adds the environment query string REPRESENT-CHARS. REPRESENT-CHARS is a system constant which returns the length of the largest character representation available from REPRESENT. This value is used by applications when allocating a buffer for REPRESENT or retrieving data from it. 2.2 Proposal Add to Table 12.2 - Environmental query strings String Value Constant? Meaning data type ------ --------- --------- ------- REPRESENT-CHARS u yes number of characters in the largest representation available from REPRESENT 2.3 Implementation REPRESENT-CHARS shall equal the number of characters in the largest representation available from REPRESENT when flag2 is false, or MAX-FLOAT-DIGITS, whichever is the greater. Implementation of REPRESENT-CHARS is optional for systems where the value REPRESENT-CHARS equals MAX-FLOAT-DIGITS. If REPRESENT-CHARS is not present in a system, applications shall substitute the value returned by MAX-FLOAT-DIGITS. 2.4 Usage See the sample applications provided in this paper. --------------------------------------------------------------------- 3. REPRESENT 3.1 Rationale The deficiencies of Forth-94 REPRESENT are discussed in the paper: ftp://ftp.taygeta.com/pub/Forth/Applications/ANS/Represent_11.txt A proposal for a revised specification was included. While it corrected several problems, the issue of string truncation remained. The specification that follows resolves all known issues while providing the portability and functionality necessary for a universal float-to-string primitive. 3.2 Proposal 12.6.1.xxxx REPRESENT FLOATING ( c-addr n1 -- n2 flag1 flag2 ) (F: r -- ) or ( r c-addr n1 -- n2 flag1 flag2 ) Place the external character representation of the significand at c-addr filling the remainder of the buffer with '0' characters. Return the decimal-base exponent as n2, the sign as flag1 and true as flag2. Flag1 is true if r was negative. If n1 is greater than zero, the significand is rounded to n1 digits with an implied decimal point to the left of the first digit. The first digit is zero only if all digits are zero. If n1 is zero, the significand is rounded to the nearest integral value, one or zero, and represented as above. If n1 is less than zero, the significand is zero. Rounding follows the implementation-defined rounding rule. n2 is adjusted, if necessary, to correspond to the rounded magnitude of the significand. If the significand is zero then n2 is 1 and the sign flag1 is implementation-defined. An ambiguous condition exists if the value of BASE is not decimal ten. Flag2 is false if r is not in the implementation-defined range of floating-point numbers. n2 and flag1 are implementation-defined, as are the contents of c-addr. The string at c-addr shall consist of graphic characters with the remainder of the buffer filled with space characters. The size of the string returned at c-addr is the greater of REPRESENT-CHARS and n1 characters. If REPRESENT-CHARS is not present in a system the value returned by MAX-FLOAT-DIGITS shall be substituted. 3.3 Notes The rounding rule is specified more openly than in Forth-94. Whichever rounding method is employed it should be applied consistently across the system i.e. REPRESENT and FROUND should round the same way. When flag2 is false, the character string at c-addr contains the complete represention of r including sign, if any. 3.4 Compatibility Existing applications using REPRESENT should be unaffected by this proposal provided a buffer not less than REPRESENT-CHARS has been allocated. New applications need only ensure a minimum of REPRESENT-CHARS characters is allocated for the REPRESENT buffer. 3.5 Sample REPRESENT implementation \ Sample REPRESENT implementation. Assumes flag2 \ is always true. Negative-zero not implemented. \ This code is PUBLIC DOMAIN. S" MAX-FLOAT-DIGITS" ENVIRONMENT? 0= [IF] CR .( MAX-FLOAT-DIGITS not found ) ABORT [THEN] CONSTANT maxdigits \ maximum usable digits S" REPRESENT-CHARS" ENVIRONMENT? 0= [IF] maxdigits [THEN] CONSTANT maxchars \ maximum float chars 2VARIABLE expsgn \ exponent, sign : REPRESENT ( r c-addr n1 -- n2 flag1 flag2 ) \ 2>R FDUP nan? IF ( not a real number) \ FDROP 2R> maxchars MAX \ OVER SWAP BLANK \ S" NAN" ROT SWAP CMOVE \ 0 FALSE FALSE EXIT \ THEN 2R> 2DUP maxchars MAX [CHAR] 0 FILL maxdigits MIN 2>R FDUP F0< 0 expsgn 2! FABS FDUP F0= 0= IF ( normalize) BEGIN FDUP 1.0E F< 0= WHILE 10.0E F/ 1 expsgn +! REPEAT BEGIN FDUP 0.1E F< WHILE 10.0E F* -1 expsgn +! REPEAT THEN R@ 0 MAX 0 ?DO 10.0E F* LOOP ( scale to integer) FROUND FDUP <# BEGIN ( build decimal string) 10.0E F/ FDUP FLOOR FSWAP FOVER F- 10.0E F* FROUND F>D D>S [CHAR] 0 + HOLD FDUP F0= UNTIL FDROP 0 0 #> DUP R@ - expsgn +! ( adjust exponent) 2R> ROT MIN 1 MAX CMOVE F0= IF ( 0.0E fix-up) 1 0 ELSE expsgn 2@ SWAP THEN TRUE ; --------------------------------------------------------------------- 4. Sample applications 4.1 Example 1 --------- Create a floating-point output function to display fixed point notation and handle all conditions including 'not-a-number'. The following code assumes PRECISION is available and its value does not exceed REPRESENT-CHARS. If PRECISION is unavailable then replace with maxchars. DECIMAL S" MAX-FLOAT-DIGITS" ENVIRONMENT? 0= [IF] CR .( MAX-FLOAT-DIGITS not found ) ABORT [THEN] CONSTANT maxdigits S" REPRESENT-CHARS" ENVIRONMENT? 0= [IF] maxdigits [THEN] CONSTANT maxchars CREATE buf maxchars CHARS ALLOT : .mant ( u -- ) buf OVER TYPE [CHAR] . EMIT buf PRECISION ROT /STRING TYPE ; : F. ( r -- ) buf PRECISION REPRESENT IF IF [CHAR] - EMIT THEN DUP >R 0 PRECISION 1+ WITHIN IF R> .mant ELSE 1 .mant [CHAR] E EMIT R> 1- . THEN ELSE 2DROP buf maxchars -TRAILING TYPE SPACE THEN ; \ Test the function : TEST1 ( -- ) CR 1.23456E20 F. CR 1.23456E9 F. CR 1.23456E8 F. CR 1.23456E7 F. CR 1.23456E6 F. CR 1.23456E5 F. CR 1.23456E4 F. CR 1.23456E3 F. CR 1.23456E2 F. CR 1.23456E1 F. CR 1.23456E0 F. CR 0.0E F. CR 1.23456E-1 F. CR 1.23456E-2 F. \ CR 1.0E 0.0E F/ F. ." {+Infinity on 80x87}" \ CR 0.0E 0.0E F/ F. ." {-NaN() on 80x87}" ; TEST1 4.2 Example 2 --------- Implement the FORTH-94 floating-point display function FE. Check that it performs correctly when PRECISION is less than the number of digits to be displayed in the significand. DECIMAL S" MAX-FLOAT-DIGITS" ENVIRONMENT? 0= [IF] CR .( MAX-FLOAT-DIGITS not found ) ABORT [THEN] CONSTANT maxdigits S" REPRESENT-CHARS" ENVIRONMENT? 0= [IF] maxdigits [THEN] CONSTANT maxchars CREATE buf maxchars CHARS ALLOT : .mant ( u -- ) buf OVER TYPE [CHAR] . EMIT buf PRECISION ROT OVER MIN /STRING TYPE ; : FE. ( r -- ) buf PRECISION REPRESENT IF IF [CHAR] - EMIT THEN 1- S>D 3 FM/MOD 3 * >R 1+ .mant [CHAR] E EMIT R> . ELSE 2DROP buf maxchars -TRAILING TYPE SPACE THEN ; : TEST2 ( -- ) PRECISION >R 2 SET-PRECISION 467.8E CR FE. ." {should be 470.E0}" R> SET-PRECISION ; TEST2 4.3 Example 3 --------- Implements a suite of floating-point output functions comparable with those found in Fortran and C. Note: FPOUT is subject to change. Please use lastest version. \ \ FPOUT.F version 3.9 \ \ A Forth floating-point output words package \ \ Main words: \ \ Compact Formatted String \ ------- --------- ------ \ FS. FS.R (FS.) Scientific \ FE. FE.R (FE.) Engineering \ F. F.R (F.) Floating-point \ G. G.R (G.) General \ \ FDP ( -- a-addr ) \ \ A variable controlling decimal point display. If the \ contents are zero then trailing decimal points are \ not shown. If non-zero (default) the decimal point is \ displayed. \ \ FECHAR ( -- c-addr ) \ \ A character variable containing the output character \ used to indicate the exponent. Default is 'E'. \ \ FEDIGITS ( -- a-addr ) \ \ A variable containing the minimum number of digits to \ output for the exponent. Minimum value is 1. Default \ is 2. Does not affect compact output modes. \ \ MAX-PRECISION ( -- u ) \ \ A constant returning the implementation-defined \ maximum PRECISION. Equivalent to the value returned \ by the environment-query string MAX-FLOAT-DIGITS. \ \ Notes: \ \ Output words which specify the number of places after \ the decimal point may use the value -1 to force compact \ mode. \ \ In compact mode non-essential zeros and signs are \ removed and the number of significant digits output is \ limited to PRECISION digits. FS. FE. F. G. operate in \ compact mode. \ \ In formatted mode the number of decimal places output \ is fixed and PRECISION has no effect. \ \ The character string returned by (FS.) (FE.) (F.) (G.) \ resides in the pictured-numeric output area. \ \ An ambiguous condition exists if: BASE is not decimal; \ character string exceeds pictured-numeric output area; \ PRECISION returns a value less than one or greater \ than MAX-FLOAT-DIGITS. \ \ For use with separate or common stack floating-point \ Forth models. \ \ This code is PUBLIC DOMAIN. Use at your own risk. \ \ ***************************************************** \ This version of FPOUT requires REPRESENT conform to \ the specification proposed here: \ \ ftp://ftp.taygeta.com/pub/Forth/Applications/ANS/ \ Represent_30.txt (2010-12-05) \ \ If your Forth does not have a compliant REPRESENT \ then use FPOUT v2.2 instead. \ ***************************************************** \ \ History: \ \ v3.1 2006-11-13 es Demo for REPRESENT proposal. \ v3.2 2007-06-05 es Changed default to trailing \ decimal point on. \ v3.3 2007-11-19 es Add FECHAR FEDIGITS. Fix zero \ sign in (F.) F.R \ v3.4 2008-01-23 es Updated to REPRESENT spec 2.1 \ v3.5 2010-12-05 es Updated to REPRESENT spec 3.0 \ v3.6 2011-02-06 es Changed FECHAR storage from \ cell to character. \ v3.7 2011-02-16 es Renamed mp# to MAX-PRECISION. \ Removed effect of PRECISION in \ formatted mode. \ v3.8 2011-05-25 es Fixed log(0) in (f1) \ v3.9 2012-05-20 es Changed FEDIGITS minimum to 1. \ CR .( Loading FPOUT 3.9 2012-05-20 ... ) CR DECIMAL \ Compile application CREATE FDP 2 CELLS ALLOT CREATE FECHAR 1 CHARS ALLOT VARIABLE FEDIGITS \ ****************** USER OPTIONS ******************* 1 FDP ! \ trailing decimal point 2 FEDIGITS ! \ minimum exponent digits CHAR E FECHAR C! \ output character for exponent \ ***************************************************** S" MAX-FLOAT-DIGITS" ENVIRONMENT? 0= [IF] CR .( MAX-FLOAT-DIGITS not found ) ABORT [THEN] ( u) \ Maximum PRECISION ( u) CONSTANT MAX-PRECISION ( -- u ) \ Define SET-PRECISION PRECISION if not present [UNDEFINED] SET-PRECISION [IF] \ Return the number of significant digits currently used \ by F. FE. FS. G. MAX-PRECISION VALUE PRECISION ( -- u ) \ Set the number of significant digits currently used by \ F. FE. FS. G. : SET-PRECISION ( u -- ) 1 MAX MAX-PRECISION MIN TO PRECISION ; [THEN] MAX-PRECISION SET-PRECISION \ set to maximum S" REPRESENT-CHARS" ENVIRONMENT? 0= [IF] MAX-PRECISION [THEN] ( u ) ( u ) CONSTANT mc# \ chars output from REPRESENT CREATE fbuf mc# CHARS ALLOT 0 VALUE ex# \ exponent 0 VALUE sn# \ sign 0 VALUE ef# \ exponent factor 1=FS. 3=FE. 0 VALUE pl# \ +n places right of decimal point \ -1 compact display \ get exponent : (f1) ( F: r -- r ) ( -- exp ) FDUP [UNDEFINED] FLOG [IF] fbuf MAX-PRECISION REPRESENT NIP AND [ELSE] F0= IF 1 ELSE FDUP FABS FLOG FLOOR F>D D>S 1+ THEN [THEN] ; \ apply exponent factor : (f2) ( exp -- offset exp2 ) S>D ef# FM/MOD ef# * ; \ float to character string : (f3) ( F: r -- ) ( places -- c-addr u flag ) DUP TO pl# 0< IF PRECISION ELSE (f1) ef# 0> IF 1- (f2) DROP 1+ THEN pl# + MAX-PRECISION MIN THEN fbuf SWAP REPRESENT >R TO sn# TO ex# fbuf mc# -TRAILING R> <# ; \ insert exponent : (f4) ( exp -- ) DUP ABS S>D pl# 0< 0= DUP >R IF FEDIGITS @ 1 MAX 1 ?DO # LOOP THEN #S 2DROP DUP SIGN 0< 0= R> AND IF [CHAR] + HOLD THEN FECHAR C@ HOLD ; \ insert digit and update flag : (f5) ( char -- ) HOLD 1 FDP CELL+ ! ; \ insert string : (f6) ( c-addr u -- ) 0 MAX BEGIN DUP WHILE 1- 2DUP CHARS + C@ (f5) REPEAT 2DROP ; \ insert '0's : (f7) ( n -- ) 0 MAX 0 ?DO [CHAR] 0 (f5) LOOP ; \ insert sign : (f8) ( -- ) sn# SIGN 0 0 #> ; \ trim trailing '0's : (f9) ( c-addr u1 -- c-addr u2 ) pl# 0< IF BEGIN DUP WHILE 1- 2DUP CHARS + C@ [CHAR] 0 - UNTIL 1+ THEN THEN ; : (fa) ( n -- n n|pl# ) pl# 0< IF DUP ELSE pl# THEN ; \ insert fraction string n places right of dec. point : (fb) ( c-addr u n -- ) 0 FDP CELL+ ! >R (f9) R@ + (fa) OVER - (f7) \ trailing 0's (fa) MIN R@ - (f6) \ fraction R> (fa) MIN (f7) \ leading 0's FDP 2@ OR IF [CHAR] . HOLD THEN ; \ split string into integer/fraction parts at n and insert : (fc) ( c-addr u n -- ) >R 2DUP R@ MIN 2SWAP R> /STRING 0 (fb) (f6) ; \ exponent form : (fd) ( F: r -- ) ( n factor -- c-addr u ) TO ef# (f3) IF ex# 1- (f2) (f4) 1+ (fc) (f8) THEN ; \ display c-addr u right-justified in field width u2 : (fe) ( c-addr u u2 -- ) OVER - SPACES TYPE ; \ Main words \ Convert real number r to a string c-addr u in scientific \ notation with n places right of the decimal point. : (FS.) ( F: r -- ) ( n -- c-addr u ) 1 (fd) ; \ Display real number r in scientific notation right- \ justified in a field width u with n places right of the \ decimal point. : FS.R ( F: r -- ) ( n u -- ) >R (FS.) R> (fe) ; \ Display real number r in scientific notation followed by \ a space. Non-essential zeros and signs are removed. : FS. ( F: r -- ) -1 0 FS.R SPACE ; \ Convert real number r to a string c-addr u in engineering \ notation with n places right of the decimal point. : (FE.) ( F: r -- ) ( n -- c-addr u ) 3 (fd) ; \ Display real number r in engineering notation right- \ justified in a field width u with n places right of the \ decimal point. : FE.R ( F: r -- ) ( n u -- ) >R (FE.) R> (fe) ; \ Display real number r in engineering notation followed \ by a space. Non-essential zeros and signs are removed. : FE. ( F: r -- ) -1 0 FE.R SPACE ; \ Convert real number r to string c-addr u in fixed-point \ notation with n places right of the decimal point. : (F.) ( F: r -- ) ( n -- c-addr u ) 0 TO ef# (f3) IF ex# DUP mc# > IF fbuf 0 ( dummy ) 0 (fb) mc# - (f7) (f6) ELSE DUP 0> IF (fc) ELSE ABS (fb) 1 (f7) THEN THEN (f8) THEN ; \ Display real number r in fixed-point notation right- \ justified in a field width u with n places right of the \ decimal point. : F.R ( F: r -- ) ( n u -- ) >R (F.) R> (fe) ; \ Display real number r in floating-point notation followed \ by a space. Non-essential zeros and signs are removed. : F. ( F: r -- ) -1 0 F.R SPACE ; \ Convert real number r to string c-addr u with n places \ right of the decimal point. Fixed-point is used if the \ exponent is in the range -4 to 5 otherwise use scientific \ notation. : (G.) ( F: r -- ) ( n -- c-addr u ) >R (f1) [ -4 1+ ] LITERAL [ 5 2 + ] LITERAL WITHIN R> SWAP IF (F.) ELSE (FS.) THEN ; \ Display real number r right-justified in a field width u \ with n places right of the decimal point. Fixed-point is \ used if the exponent is in the range -4 to 5 otherwise \ use scientific notation. : G.R ( F: r -- ) ( n u -- ) >R (G.) R> (fe) ; \ Display real number r followed by a space. Floating-point \ is used if the exponent is in the range -4 to 5 otherwise \ use scientific notation. Non-essential zeros and signs are \ removed. : G. ( F: r -- ) -1 0 G.R SPACE ; CR FDP @ [IF] CR .( Decimal point always displayed. Use 0 FDP ! ) CR .( or FDP OFF to disable trailing decimal point. ) [ELSE] CR .( Trailing decimal point not displayed. Use ) CR .( 1 FDP ! or FDP ON for FORTH-94 compliance. ) [THEN] CR [DEFINED] DXFORTH [IF] BEHEAD mc# (fe) [THEN] \ ****************** DEMONSTRATION ****************** 0 [IF] CR .( Loading demo words... ) CR CR .( TEST1 formatted, n decimal places ) CR .( TEST2 compact & right-justified ) CR .( TEST3 display FS. ) CR .( TEST4 display F. ) CR .( TEST5 display G. ) CR .( TEST6 display 8087 non-numbers ) CR CR .( 'n PLACES' sets decimal places for TEST1. ) CR .( SET-PRECISION sets maximum significant ) CR .( digits displayable. ) CR CR [UNDEFINED] F, [IF] : F, ( r -- ) FALIGN HERE 1 FLOATS ALLOT F! ; [THEN] CREATE f-array \ floating-point numbers array FALIGN HERE 1.23456E-16 F, 1.23456E-11 F, 1.23456E-7 F, 1.23456E-6 F, 1.23456E-5 F, 1.23456E-4 F, 1.23456E-3 F, 1.23456E-2 F, 1.23456E-1 F, 0.E0 F, 1.23456E+0 F, 1.23456E+1 F, 1.23456E+2 F, 1.23456E+3 F, 1.23456E+4 F, 1.23456E+5 F, 1.23456E+6 F, 1.23456E+7 F, 1.23456E+11 F, 1.23456E+16 F, HERE SWAP - 1 FLOATS / CONSTANT #numbers : do-it ( xt -- ) #numbers 0 DO f-array FALIGNED I FLOATS + OVER >R F@ CR R> EXECUTE LOOP DROP ; 2VARIABLE (dw) : d.w ( -- dec.places width ) (dw) 2@ ; : PLACES ( places -- ) d.w SWAP DROP (dw) 2! ; : WIDTH ( width -- ) d.w DROP SWAP (dw) 2! ; 5 PLACES 19 WIDTH : (t1) ( r -- ) FDUP d.w FS.R FDUP d.w F.R FDUP d.w G.R d.w FE.R ; : TEST1 ( -- ) CR ." TEST1 right-justified, formatted (" d.w DROP 0 .R ." decimal places)" CR ['] (t1) do-it CR ; : (t2) ( r -- ) FDUP -1 d.w NIP FS.R FDUP -1 d.w NIP F.R FDUP -1 d.w NIP G.R -1 d.w NIP FE.R ; : TEST2 ( -- ) CR ." TEST2 right-justified, compact" CR ['] (t2) do-it CR ; : TEST3 ( -- ) CR ." TEST3 FS." CR ['] FS. do-it CR ; : TEST4 ( -- ) CR ." TEST4 F." CR ['] F. do-it CR ; : TEST5 ( -- ) CR ." TEST5 G." CR ['] G. do-it CR ; : TEST6 ( -- ) PRECISION >R 1 SET-PRECISION CR ." TEST6 8087 non-numbers PRECISION = 1" CR CR 1.E0 0.E0 F/ FDUP G. CR FNEGATE G. CR 0.E0 0.E0 F/ FDUP G. CR FNEGATE G. CR R> SET-PRECISION ; [ELSE] CR .( To compile demonstration words TEST1..TEST6 ) CR .( enable conditional in FPOUT source. ) CR [THEN] \ end --------------------------------------------------------------------- 5. History ------- 2006-10-01 Revision of REPRESENT proposal (version 1.1 to 2.0) 2006-10-30 Add addendum and examples 2006-10-31 Add FPOUT example 2006-11-27 Example corrected 2008-01-23 Revised proposal (version 2.0 to 2.1) Specification extended to include negative values of rounding specifier n1. 2010-12-05 Revised proposal (version 2.1 to 3.0) Specification extended to include environment query string REPRESENT-CHARS. 2012-06-04 Sample REPRESENT corrected to remove potential lock-up. Updated FPOUT example to latest version. 2013-10-28 Sample REPRESENT re-coded to work with floats of any precision. 2014-03-17 Specification re-worded (no functional change). Minor update to sample REPRESENT implementation.