Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | +---------------------------------------------------------------------------+ |
| 2 | | wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. | |
| 3 | | | |
| 4 | | Copyright (C) 1992,1993,1994,1995,1996,1997,1999 | |
| 5 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, | |
| 6 | | Australia. E-mail billm@melbpc.org.au | |
| 7 | | | |
| 8 | | This program is free software; you can redistribute it and/or modify | |
| 9 | | it under the terms of the GNU General Public License version 2 as | |
| 10 | | published by the Free Software Foundation. | |
| 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, write to the Free Software | |
| 19 | | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
| 20 | | | |
| 21 | +---------------------------------------------------------------------------+ |
| 22 | |
| 23 | |
| 24 | |
| 25 | wm-FPU-emu is an FPU emulator for Linux. It is derived from wm-emu387 |
| 26 | which was my 80387 emulator for early versions of djgpp (gcc under |
| 27 | msdos); wm-emu387 was in turn based upon emu387 which was written by |
| 28 | DJ Delorie for djgpp. The interface to the Linux kernel is based upon |
| 29 | the original Linux math emulator by Linus Torvalds. |
| 30 | |
| 31 | My target FPU for wm-FPU-emu is that described in the Intel486 |
| 32 | Programmer's Reference Manual (1992 edition). Unfortunately, numerous |
| 33 | facets of the functioning of the FPU are not well covered in the |
| 34 | Reference Manual. The information in the manual has been supplemented |
| 35 | with measurements on real 80486's. Unfortunately, it is simply not |
| 36 | possible to be sure that all of the peculiarities of the 80486 have |
| 37 | been discovered, so there is always likely to be obscure differences |
| 38 | in the detailed behaviour of the emulator and a real 80486. |
| 39 | |
| 40 | wm-FPU-emu does not implement all of the behaviour of the 80486 FPU, |
| 41 | but is very close. See "Limitations" later in this file for a list of |
| 42 | some differences. |
| 43 | |
| 44 | Please report bugs, etc to me at: |
| 45 | billm@melbpc.org.au |
| 46 | or b.metzenthen@medoto.unimelb.edu.au |
| 47 | |
| 48 | For more information on the emulator and on floating point topics, see |
| 49 | my web pages, currently at http://www.suburbia.net/~billm/ |
| 50 | |
| 51 | |
| 52 | --Bill Metzenthen |
| 53 | December 1999 |
| 54 | |
| 55 | |
| 56 | ----------------------- Internals of wm-FPU-emu ----------------------- |
| 57 | |
| 58 | Numeric algorithms: |
| 59 | (1) Add, subtract, and multiply. Nothing remarkable in these. |
| 60 | (2) Divide has been tuned to get reasonable performance. The algorithm |
| 61 | is not the obvious one which most people seem to use, but is designed |
| 62 | to take advantage of the characteristics of the 80386. I expect that |
| 63 | it has been invented many times before I discovered it, but I have not |
| 64 | seen it. It is based upon one of those ideas which one carries around |
| 65 | for years without ever bothering to check it out. |
| 66 | (3) The sqrt function has been tuned to get good performance. It is based |
| 67 | upon Newton's classic method. Performance was improved by capitalizing |
| 68 | upon the properties of Newton's method, and the code is once again |
| 69 | structured taking account of the 80386 characteristics. |
| 70 | (4) The trig, log, and exp functions are based in each case upon quasi- |
| 71 | "optimal" polynomial approximations. My definition of "optimal" was |
| 72 | based upon getting good accuracy with reasonable speed. |
| 73 | (5) The argument reducing code for the trig function effectively uses |
| 74 | a value of pi which is accurate to more than 128 bits. As a consequence, |
| 75 | the reduced argument is accurate to more than 64 bits for arguments up |
| 76 | to a few pi, and accurate to more than 64 bits for most arguments, |
| 77 | even for arguments approaching 2^63. This is far superior to an |
| 78 | 80486, which uses a value of pi which is accurate to 66 bits. |
| 79 | |
| 80 | The code of the emulator is complicated slightly by the need to |
| 81 | account for a limited form of re-entrancy. Normally, the emulator will |
| 82 | emulate each FPU instruction to completion without interruption. |
| 83 | However, it may happen that when the emulator is accessing the user |
| 84 | memory space, swapping may be needed. In this case the emulator may be |
| 85 | temporarily suspended while disk i/o takes place. During this time |
| 86 | another process may use the emulator, thereby perhaps changing static |
| 87 | variables. The code which accesses user memory is confined to five |
| 88 | files: |
| 89 | fpu_entry.c |
| 90 | reg_ld_str.c |
| 91 | load_store.c |
| 92 | get_address.c |
| 93 | errors.c |
| 94 | As from version 1.12 of the emulator, no static variables are used |
| 95 | (apart from those in the kernel's per-process tables). The emulator is |
| 96 | therefore now fully re-entrant, rather than having just the restricted |
| 97 | form of re-entrancy which is required by the Linux kernel. |
| 98 | |
| 99 | ----------------------- Limitations of wm-FPU-emu ----------------------- |
| 100 | |
| 101 | There are a number of differences between the current wm-FPU-emu |
| 102 | (version 2.01) and the 80486 FPU (apart from bugs). The differences |
| 103 | are fewer than those which applied to the 1.xx series of the emulator. |
| 104 | Some of the more important differences are listed below: |
| 105 | |
| 106 | The Roundup flag does not have much meaning for the transcendental |
| 107 | functions and its 80486 value with these functions is likely to differ |
| 108 | from its emulator value. |
| 109 | |
| 110 | In a few rare cases the Underflow flag obtained with the emulator will |
| 111 | be different from that obtained with an 80486. This occurs when the |
| 112 | following conditions apply simultaneously: |
| 113 | (a) the operands have a higher precision than the current setting of the |
| 114 | precision control (PC) flags. |
| 115 | (b) the underflow exception is masked. |
| 116 | (c) the magnitude of the exact result (before rounding) is less than 2^-16382. |
| 117 | (d) the magnitude of the final result (after rounding) is exactly 2^-16382. |
| 118 | (e) the magnitude of the exact result would be exactly 2^-16382 if the |
| 119 | operands were rounded to the current precision before the arithmetic |
| 120 | operation was performed. |
| 121 | If all of these apply, the emulator will set the Underflow flag but a real |
| 122 | 80486 will not. |
| 123 | |
| 124 | NOTE: Certain formats of Extended Real are UNSUPPORTED. They are |
| 125 | unsupported by the 80486. They are the Pseudo-NaNs, Pseudoinfinities, |
| 126 | and Unnormals. None of these will be generated by an 80486 or by the |
| 127 | emulator. Do not use them. The emulator treats them differently in |
| 128 | detail from the way an 80486 does. |
| 129 | |
| 130 | Self modifying code can cause the emulator to fail. An example of such |
| 131 | code is: |
| 132 | movl %esp,[%ebx] |
| 133 | fld1 |
| 134 | The FPU instruction may be (usually will be) loaded into the pre-fetch |
| 135 | queue of the CPU before the mov instruction is executed. If the |
| 136 | destination of the 'movl' overlaps the FPU instruction then the bytes |
| 137 | in the prefetch queue and memory will be inconsistent when the FPU |
| 138 | instruction is executed. The emulator will be invoked but will not be |
| 139 | able to find the instruction which caused the device-not-present |
| 140 | exception. For this case, the emulator cannot emulate the behaviour of |
| 141 | an 80486DX. |
| 142 | |
| 143 | Handling of the address size override prefix byte (0x67) has not been |
| 144 | extensively tested yet. A major problem exists because using it in |
| 145 | vm86 mode can cause a general protection fault. Address offsets |
| 146 | greater than 0xffff appear to be illegal in vm86 mode but are quite |
| 147 | acceptable (and work) in real mode. A small test program developed to |
| 148 | check the addressing, and which runs successfully in real mode, |
| 149 | crashes dosemu under Linux and also brings Windows down with a general |
| 150 | protection fault message when run under the MS-DOS prompt of Windows |
| 151 | 3.1. (The program simply reads data from a valid address). |
| 152 | |
| 153 | The emulator supports 16-bit protected mode, with one difference from |
| 154 | an 80486DX. A 80486DX will allow some floating point instructions to |
| 155 | write a few bytes below the lowest address of the stack. The emulator |
| 156 | will not allow this in 16-bit protected mode: no instructions are |
| 157 | allowed to write outside the bounds set by the protection. |
| 158 | |
| 159 | ----------------------- Performance of wm-FPU-emu ----------------------- |
| 160 | |
| 161 | Speed. |
| 162 | ----- |
| 163 | |
| 164 | The speed of floating point computation with the emulator will depend |
| 165 | upon instruction mix. Relative performance is best for the instructions |
| 166 | which require most computation. The simple instructions are adversely |
| 167 | affected by the FPU instruction trap overhead. |
| 168 | |
| 169 | |
| 170 | Timing: Some simple timing tests have been made on the emulator functions. |
| 171 | The times include load/store instructions. All times are in microseconds |
| 172 | measured on a 33MHz 386 with 64k cache. The Turbo C tests were under |
| 173 | ms-dos, the next two columns are for emulators running with the djgpp |
| 174 | ms-dos extender. The final column is for wm-FPU-emu in Linux 0.97, |
| 175 | using libm4.0 (hard). |
| 176 | |
| 177 | function Turbo C djgpp 1.06 WM-emu387 wm-FPU-emu |
| 178 | |
| 179 | + 60.5 154.8 76.5 139.4 |
| 180 | - 61.1-65.5 157.3-160.8 76.2-79.5 142.9-144.7 |
| 181 | * 71.0 190.8 79.6 146.6 |
| 182 | / 61.2-75.0 261.4-266.9 75.3-91.6 142.2-158.1 |
| 183 | |
| 184 | sin() 310.8 4692.0 319.0 398.5 |
| 185 | cos() 284.4 4855.2 308.0 388.7 |
| 186 | tan() 495.0 8807.1 394.9 504.7 |
| 187 | atan() 328.9 4866.4 601.1 419.5-491.9 |
| 188 | |
| 189 | sqrt() 128.7 crashed 145.2 227.0 |
| 190 | log() 413.1-419.1 5103.4-5354.21 254.7-282.2 409.4-437.1 |
| 191 | exp() 479.1 6619.2 469.1 850.8 |
| 192 | |
| 193 | |
| 194 | The performance under Linux is improved by the use of look-ahead code. |
| 195 | The following results show the improvement which is obtained under |
| 196 | Linux due to the look-ahead code. Also given are the times for the |
| 197 | original Linux emulator with the 4.1 'soft' lib. |
| 198 | |
| 199 | [ Linus' note: I changed look-ahead to be the default under linux, as |
| 200 | there was no reason not to use it after I had edited it to be |
| 201 | disabled during tracing ] |
| 202 | |
| 203 | wm-FPU-emu w original w |
| 204 | look-ahead 'soft' lib |
| 205 | + 106.4 190.2 |
| 206 | - 108.6-111.6 192.4-216.2 |
| 207 | * 113.4 193.1 |
| 208 | / 108.8-124.4 700.1-706.2 |
| 209 | |
| 210 | sin() 390.5 2642.0 |
| 211 | cos() 381.5 2767.4 |
| 212 | tan() 496.5 3153.3 |
| 213 | atan() 367.2-435.5 2439.4-3396.8 |
| 214 | |
| 215 | sqrt() 195.1 4732.5 |
| 216 | log() 358.0-387.5 3359.2-3390.3 |
| 217 | exp() 619.3 4046.4 |
| 218 | |
| 219 | |
| 220 | These figures are now somewhat out-of-date. The emulator has become |
| 221 | progressively slower for most functions as more of the 80486 features |
| 222 | have been implemented. |
| 223 | |
| 224 | |
| 225 | ----------------------- Accuracy of wm-FPU-emu ----------------------- |
| 226 | |
| 227 | |
| 228 | The accuracy of the emulator is in almost all cases equal to or better |
| 229 | than that of an Intel 80486 FPU. |
| 230 | |
| 231 | The results of the basic arithmetic functions (+,-,*,/), and fsqrt |
| 232 | match those of an 80486 FPU. They are the best possible; the error for |
| 233 | these never exceeds 1/2 an lsb. The fprem and fprem1 instructions |
| 234 | return exact results; they have no error. |
| 235 | |
| 236 | |
| 237 | The following table compares the emulator accuracy for the sqrt(), |
| 238 | trig and log functions against the Turbo C "emulator". For this table, |
| 239 | each function was tested at about 400 points. Ideal worst-case results |
| 240 | would be 64 bits. The reduced Turbo C accuracy of cos() and tan() for |
| 241 | arguments greater than pi/4 can be thought of as being related to the |
| 242 | precision of the argument x; e.g. an argument of pi/2-(1e-10) which is |
| 243 | accurate to 64 bits can result in a relative accuracy in cos() of |
| 244 | about 64 + log2(cos(x)) = 31 bits. |
| 245 | |
| 246 | |
| 247 | Function Tested x range Worst result Turbo C |
| 248 | (relative bits) |
| 249 | |
| 250 | sqrt(x) 1 .. 2 64.1 63.2 |
| 251 | atan(x) 1e-10 .. 200 64.2 62.8 |
| 252 | cos(x) 0 .. pi/2-(1e-10) 64.4 (x <= pi/4) 62.4 |
| 253 | 64.1 (x = pi/2-(1e-10)) 31.9 |
| 254 | sin(x) 1e-10 .. pi/2 64.0 62.8 |
| 255 | tan(x) 1e-10 .. pi/2-(1e-10) 64.0 (x <= pi/4) 62.1 |
| 256 | 64.1 (x = pi/2-(1e-10)) 31.9 |
| 257 | exp(x) 0 .. 1 63.1 ** 62.9 |
| 258 | log(x) 1+1e-6 .. 2 63.8 ** 62.1 |
| 259 | |
| 260 | ** The accuracy for exp() and log() is low because the FPU (emulator) |
| 261 | does not compute them directly; two operations are required. |
| 262 | |
| 263 | |
| 264 | The emulator passes the "paranoia" tests (compiled with gcc 2.3.3 or |
| 265 | later) for 'float' variables (24 bit precision numbers) when precision |
| 266 | control is set to 24, 53 or 64 bits, and for 'double' variables (53 |
| 267 | bit precision numbers) when precision control is set to 53 bits (a |
| 268 | properly performing FPU cannot pass the 'paranoia' tests for 'double' |
| 269 | variables when precision control is set to 64 bits). |
| 270 | |
| 271 | The code for reducing the argument for the trig functions (fsin, fcos, |
| 272 | fptan and fsincos) has been improved and now effectively uses a value |
| 273 | for pi which is accurate to more than 128 bits precision. As a |
| 274 | consequence, the accuracy of these functions for large arguments has |
| 275 | been dramatically improved (and is now very much better than an 80486 |
| 276 | FPU). There is also now no degradation of accuracy for fcos and fptan |
| 277 | for operands close to pi/2. Measured results are (note that the |
| 278 | definition of accuracy has changed slightly from that used for the |
| 279 | above table): |
| 280 | |
| 281 | Function Tested x range Worst result |
| 282 | (absolute bits) |
| 283 | |
| 284 | cos(x) 0 .. 9.22e+18 62.0 |
| 285 | sin(x) 1e-16 .. 9.22e+18 62.1 |
| 286 | tan(x) 1e-16 .. 9.22e+18 61.8 |
| 287 | |
| 288 | It is possible with some effort to find very large arguments which |
| 289 | give much degraded precision. For example, the integer number |
| 290 | 8227740058411162616.0 |
| 291 | is within about 10e-7 of a multiple of pi. To find the tan (for |
| 292 | example) of this number to 64 bits precision it would be necessary to |
| 293 | have a value of pi which had about 150 bits precision. The FPU |
| 294 | emulator computes the result to about 42.6 bits precision (the correct |
| 295 | result is about -9.739715e-8). On the other hand, an 80486 FPU returns |
| 296 | 0.01059, which in relative terms is hopelessly inaccurate. |
| 297 | |
| 298 | For arguments close to critical angles (which occur at multiples of |
| 299 | pi/2) the emulator is more accurate than an 80486 FPU. For very large |
| 300 | arguments, the emulator is far more accurate. |
| 301 | |
| 302 | |
| 303 | Prior to version 1.20 of the emulator, the accuracy of the results for |
| 304 | the transcendental functions (in their principal range) was not as |
| 305 | good as the results from an 80486 FPU. From version 1.20, the accuracy |
| 306 | has been considerably improved and these functions now give measured |
| 307 | worst-case results which are better than the worst-case results given |
| 308 | by an 80486 FPU. |
| 309 | |
| 310 | The following table gives the measured results for the emulator. The |
| 311 | number of randomly selected arguments in each case is about half a |
| 312 | million. The group of three columns gives the frequency of the given |
| 313 | accuracy in number of times per million, thus the second of these |
| 314 | columns shows that an accuracy of between 63.80 and 63.89 bits was |
| 315 | found at a rate of 133 times per one million measurements for fsin. |
| 316 | The results show that the fsin, fcos and fptan instructions return |
| 317 | results which are in error (i.e. less accurate than the best possible |
| 318 | result (which is 64 bits)) for about one per cent of all arguments |
| 319 | between -pi/2 and +pi/2. The other instructions have a lower |
| 320 | frequency of results which are in error. The last two columns give |
| 321 | the worst accuracy which was found (in bits) and the approximate value |
| 322 | of the argument which produced it. |
| 323 | |
| 324 | frequency (per M) |
| 325 | ------------------- --------------- |
| 326 | instr arg range # tests 63.7 63.8 63.9 worst at arg |
| 327 | bits bits bits bits |
| 328 | ----- ------------ ------- ---- ---- ----- ----- -------- |
| 329 | fsin (0,pi/2) 547756 0 133 10673 63.89 0.451317 |
| 330 | fcos (0,pi/2) 547563 0 126 10532 63.85 0.700801 |
| 331 | fptan (0,pi/2) 536274 11 267 10059 63.74 0.784876 |
| 332 | fpatan 4 quadrants 517087 0 8 1855 63.88 0.435121 (4q) |
| 333 | fyl2x (0,20) 541861 0 0 1323 63.94 1.40923 (x) |
| 334 | fyl2xp1 (-.293,.414) 520256 0 0 5678 63.93 0.408542 (x) |
| 335 | f2xm1 (-1,1) 538847 4 481 6488 63.79 0.167709 |
| 336 | |
| 337 | |
| 338 | Tests performed on an 80486 FPU showed results of lower accuracy. The |
| 339 | following table gives the results which were obtained with an AMD |
| 340 | 486DX2/66 (other tests indicate that an Intel 486DX produces |
| 341 | identical results). The tests were basically the same as those used |
| 342 | to measure the emulator (the values, being random, were in general not |
| 343 | the same). The total number of tests for each instruction are given |
| 344 | at the end of the table, in case each about 100k tests were performed. |
| 345 | Another line of figures at the end of the table shows that most of the |
| 346 | instructions return results which are in error for more than 10 |
| 347 | percent of the arguments tested. |
| 348 | |
| 349 | The numbers in the body of the table give the approx number of times a |
| 350 | result of the given accuracy in bits (given in the left-most column) |
| 351 | was obtained per one million arguments. For three of the instructions, |
| 352 | two columns of results are given: * The second column for f2xm1 gives |
| 353 | the number cases where the results of the first column were for a |
| 354 | positive argument, this shows that this instruction gives better |
| 355 | results for positive arguments than it does for negative. * In the |
| 356 | cases of fcos and fptan, the first column gives the results when all |
| 357 | cases where arguments greater than 1.5 were removed from the results |
| 358 | given in the second column. Unlike the emulator, an 80486 FPU returns |
| 359 | results of relatively poor accuracy for these instructions when the |
| 360 | argument approaches pi/2. The table does not show those cases when the |
| 361 | accuracy of the results were less than 62 bits, which occurs quite |
| 362 | often for fsin and fptan when the argument approaches pi/2. This poor |
| 363 | accuracy is discussed above in relation to the Turbo C "emulator", and |
| 364 | the accuracy of the value of pi. |
| 365 | |
| 366 | |
| 367 | bits f2xm1 f2xm1 fpatan fcos fcos fyl2x fyl2xp1 fsin fptan fptan |
| 368 | 62.0 0 0 0 0 437 0 0 0 0 925 |
| 369 | 62.1 0 0 10 0 894 0 0 0 0 1023 |
| 370 | 62.2 14 0 0 0 1033 0 0 0 0 945 |
| 371 | 62.3 57 0 0 0 1202 0 0 0 0 1023 |
| 372 | 62.4 385 0 0 10 1292 0 23 0 0 1178 |
| 373 | 62.5 1140 0 0 119 1649 0 39 0 0 1149 |
| 374 | 62.6 2037 0 0 189 1620 0 16 0 0 1169 |
| 375 | 62.7 5086 14 0 646 2315 10 101 35 39 1402 |
| 376 | 62.8 8818 86 0 984 3050 59 287 131 224 2036 |
| 377 | 62.9 11340 1355 0 2126 4153 79 605 357 321 1948 |
| 378 | 63.0 15557 4750 0 3319 5376 246 1281 862 808 2688 |
| 379 | 63.1 20016 8288 0 4620 6628 511 2569 1723 1510 3302 |
| 380 | 63.2 24945 11127 10 6588 8098 1120 4470 2968 2990 4724 |
| 381 | 63.3 25686 12382 69 8774 10682 1906 6775 4482 5474 7236 |
| 382 | 63.4 29219 14722 79 11109 12311 3094 9414 7259 8912 10587 |
| 383 | 63.5 30458 14936 393 13802 15014 5874 12666 9609 13762 15262 |
| 384 | 63.6 32439 16448 1277 17945 19028 10226 15537 14657 19158 20346 |
| 385 | 63.7 35031 16805 4067 23003 23947 18910 20116 21333 25001 26209 |
| 386 | 63.8 33251 15820 7673 24781 25675 24617 25354 24440 29433 30329 |
| 387 | 63.9 33293 16833 18529 28318 29233 31267 31470 27748 29676 30601 |
| 388 | |
| 389 | Per cent with error: |
| 390 | 30.9 3.2 18.5 9.8 13.1 11.6 17.4 |
| 391 | Total arguments tested: |
| 392 | 70194 70099 101784 100641 100641 101799 128853 114893 102675 102675 |
| 393 | |
| 394 | |
| 395 | ------------------------- Contributors ------------------------------- |
| 396 | |
| 397 | A number of people have contributed to the development of the |
| 398 | emulator, often by just reporting bugs, sometimes with suggested |
| 399 | fixes, and a few kind people have provided me with access in one way |
| 400 | or another to an 80486 machine. Contributors include (to those people |
| 401 | who I may have forgotten, please forgive me): |
| 402 | |
| 403 | Linus Torvalds |
| 404 | Tommy.Thorn@daimi.aau.dk |
| 405 | Andrew.Tridgell@anu.edu.au |
| 406 | Nick Holloway, alfie@dcs.warwick.ac.uk |
| 407 | Hermano Moura, moura@dcs.gla.ac.uk |
| 408 | Jon Jagger, J.Jagger@scp.ac.uk |
| 409 | Lennart Benschop |
| 410 | Brian Gallew, geek+@CMU.EDU |
| 411 | Thomas Staniszewski, ts3v+@andrew.cmu.edu |
| 412 | Martin Howell, mph@plasma.apana.org.au |
| 413 | M Saggaf, alsaggaf@athena.mit.edu |
| 414 | Peter Barker, PETER@socpsy.sci.fau.edu |
| 415 | tom@vlsivie.tuwien.ac.at |
| 416 | Dan Russel, russed@rpi.edu |
| 417 | Daniel Carosone, danielce@ee.mu.oz.au |
| 418 | cae@jpmorgan.com |
| 419 | Hamish Coleman, t933093@minyos.xx.rmit.oz.au |
| 420 | Bruce Evans, bde@kralizec.zeta.org.au |
| 421 | Timo Korvola, Timo.Korvola@hut.fi |
| 422 | Rick Lyons, rick@razorback.brisnet.org.au |
| 423 | Rick, jrs@world.std.com |
| 424 | |
| 425 | ...and numerous others who responded to my request for help with |
| 426 | a real 80486. |
| 427 | |