Annotation of freem/mlib/%ulmath.m, revision 1.2
1.2 ! snw 1: %ulmath ;VCL/SNW-MATH LIBRARY; 03/09/25 06:29 PM
! 2: ;0.0;FreeM;****FREEM**;Serena Willis @2025
! 3: ;
! 4: ; $Id$
! 5: ; FreeM Math Library
! 6: ;
! 7: ;
! 8: ; Author: Serena Willis <snw@coherent-logic.com>
! 9: ; Copyright (C) 1998 MUG Deutschland
! 10: ; Copyright (C) 2023, 2025 Coherent Logic Development LLC
! 11: ;
! 12: ;
! 13: ; This file is part of FreeM.
! 14: ;
! 15: ; FreeM is free software: you can redistribute it and/or modify
! 16: ; it under the terms of the GNU Affero Public License as published by
! 17: ; the Free Software Foundation, either version 3 of the License, or
! 18: ; (at your option) any later version.
! 19: ;
! 20: ; FreeM is distributed in the hope that it will be useful,
! 21: ; but WITHOUT ANY WARRANTY; without even the implied warranty of
! 22: ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 23: ; GNU Affero Public License for more details.
! 24: ;
! 25: ; You should have received a copy of the GNU Affero Public License
! 26: ; along with FreeM. If not, see <https://www.gnu.org/licenses/>.
! 27: ;
! 28: ; $Log$
! 29: ;
! 30: ; SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
! 31: ; SPDX-License-Identifier: AGPL-3.0-or-later
! 32: ; MATH library - version 0.5.0.1
1.1 snw 33: ;
34: ; Unless otherwise noted, the code below
35: ; was approved in document X11/95-11
36: ;
37: ; CORRECTION INFORMATION
38: ; Due to the overall routine size this has not been provided
39: ; It is available from Ed de Moel who provided these functions
40: ;
41: ABS(X) Quit $Translate(+X,"-")
42: ;===
43: ;
44: ;
45: %ARCCOS(X) ;
46: ;
47: New A,N,R,SIGN,XX
48: If X<-1 Set $Ecode=",M28,"
49: If X>1 Set $Ecode=",M28,"
50: Set SIGN=1 Set:X<0 X=-X,SIGN=-1
51: Set A(0)=1.5707963050,A(1)=-0.2145988016,A(2)=0.0889789874
52: Set A(3)=-0.0501743046,A(4)=0.0308918810,A(5)=-0.0170881256
53: Set A(6)=0.0066700901,A(7)=-0.0012624911
54: Set R=A(0),XX=1 For N=1:1:7 Set XX=XX*X,R=A(N)*XX+R
55: ;
56: Set R=$$SQRT(1-X,11)*R
57: ;;;
58: ;
59: Quit R*SIGN
60: ;===
61: ;
62: ;
63: ARCCOS(X,PREC) ;
64: I '$D(PREC) Q $$%ARCCOS(X)
65: ;
66: New L,LIM,K,SIG,SIGS,VALUE
67: ;;;
68: ;
69: If X<-1 Set $Ecode=",M28,"
70: If X>1 Set $Ecode=",M28,"
71: Set PREC=$Get(PREC,11)
72: ;
73: If $Translate(X,"-")=1 Quit 0
74: ;;;
75: ;
76: Set SIG=$Select(X<0:-1,1:1),VALUE=1-(X*X)
77: ;
78: Set X=$$SQRT(VALUE,PREC)
79: ;;;
80: ;
81: If $Translate(X,"-")=1 Do Quit VALUE
82: . ;;;
83: . ;
84: . Set VALUE=$$PI()/2*X
85: . Quit
86: ;
87: If X>0.9 Do Quit VALUE
88: . ;;;
89: . ;
90: . Set SIGS=$Select(X<0:-1,1:1)
91: . Set VALUE=1/(1/X/X-1)
92: . ;
93: . Set X=$$SQRT(VALUE,PREC)
94: . ;;;
95: . ;
96: . ;
97: . Set VALUE=$$ARCTAN(X,PREC)*SIGS
98: . ;;;
99: ;
100: . Quit
101: Set (VALUE,L)=X
102: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
103: For K=3:2 Do Quit:($Translate(L,"-")<LIM)
104: . Set L=L*X*X*(K-2)/(K-1)*(K-2)/K,VALUE=VALUE+L
105: . Quit
106: Quit $Select(SIG<0:$$PI()-VALUE,1:VALUE)
107: ;===
108: ;
109: ;
110: ARCCOSH(X,PREC) ;
111: If X<1 Set $Ecode=",M28,"
112: New SQ
113: ;
114: Set PREC=$Get(PREC,11)
115: ;;;
116: ;
117: Set SQ=$$SQRT(X*X-1,PREC)
118: Quit $$LOG(X+SQ,PREC)
119: ;===
120: ;
121: ;
122: ARCCOT(X,PREC) ;
123: Set PREC=$Get(PREC,11)
124: Set X=1/X
125: Quit $$ARCTAN(X,PREC)
126: ;===
127: ;
128: ;
129: ARCCOTH(X,PREC) ;
130: New L1,L2
131: ;
132: Set PREC=$Get(PREC,11)
133: ;;;
134: ;
135: Set L1=$$LOG(X+1,PREC)
136: Set L2=$$LOG(X-1,PREC)
137: Quit L1-L2/2
138: ;===
139: ;
140: ;
141: ARCCSC(X,PREC) ;
142: Set PREC=$Get(PREC,11)
143: Set X=1/X
144: Quit $$ARCSIN(X,PREC)
145: ;===
146: ;
147: ;
148: ARCSEC(X,PREC) ;
149: Set PREC=$Get(PREC,11)
150: Set X=1/X
151: Quit $$ARCCOS(X,PREC)
152: ;===
153: ;
154: ;
155: %ARCSIN(X) ;
156: ;;; ; Number ~~
157: ; Winfried Gerum (8 June 1995)
158: ;
159: New A,N,R,SIGN,XX
160: If X<-1 Set $Ecode=",M28,"
161: If X>1 Set $Ecode=",M28,"
162: Set SIGN=1 Set:X<0 X=-X,SIGN=-1
163: Set A(0)=1.5707963050,A(1)=-0.2145988016,A(2)=0.0889789874
164: Set A(3)=-0.0501743046,A(4)=0.0308918810,A(5)=-0.0170881256
165: Set A(6)=0.0066700901,A(7)=-0.0012624911
166: Set R=A(0),XX=1 For N=1:1:7 Set XX=XX*X,R=A(N)*XX+R
167: ;
168: Set R=$$SQRT(1-X,11)*R
169: ;;;
170: ;
171: Set R=$$PI()/2-R
172: Quit R*SIGN
173: ;===
174: ;
175: ;
176: ARCSIN(X,PREC) ;
177: I '$D(PREC) Q $$%ARCSIN(X)
178: New L,LIM,K,SIGS,VALUE
179: Set PREC=$Get(PREC,11)
180: ;
181: If $Translate(X,"-")=1 Do Quit VALUE
182: . ;;;
183: . ;
184: . Set VALUE=$$PI()/2*X
185: . Quit
186: ;
187: If X>0.99999 Do Quit VALUE
188: . ;;;
189: . ;
190: . Set SIGS=$Select(X<0:-1,1:1)
191: . Set VALUE=1/(1/X/X-1)
192: . ;
193: . ;;;
194: . ;
195: . Set VALUE=$$ARCTAN(X,PREC)*SIGS
196: . ;;;
197: . ;
198: . Quit
199: Set (VALUE,L)=X
200: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
201: For K=3:2 Do Quit:($Translate(L,"-")<LIM)
202: . Set L=L*X*X*(K-2)/(K-1)*(K-2)/K,VALUE=VALUE+L
203: . Quit
204: Quit VALUE
205: ;===
206: ;
207: ;
208: ARCSINH(X,PREC) ;
209: If X<1 Set $Ecode=",M28,"
210: New SQ
211: ;
212: Set PREC=$Get(PREC,11)
213: ;;;
214: ;
215: Set SQ=$$SQRT(X*X+1,PREC)
216: Quit $$LOG(X+SQ,PREC)
217: ;===
218: ;
219: ;
220: ARCTAN(X,PREC) ;
221: New FOLD,HI,L,LIM,LO,K,SIGN,SIGS,SIGT,VALUE
222: Set PREC=$Get(PREC,11)
223: Set LO=0.0000000001,HI=9999999999
224: Set SIGT=$Select(X<0:-1,1:1),X=$Translate(X,"-")
225: Set X=$Select(X<LO:LO,X>HI:HI,1:X)
226: ;
227: Set FOLD=$Select(X'<1:0,1:1)
228: ;;;
229: ;
230: Set X=$Select(FOLD:1/X,1:X)
231: Set L=X,VALUE=$$PI()/2-(1/X),SIGN=1
232: ;
233: If X<1.3 Do Quit VALUE
234: . ;;;
235: . ;
236: . Set X=$Select(FOLD:1/X,1:X),VALUE=1/((1/X/X)+1)
237: . ;
238: . Set X=$$SQRT(VALUE,PREC)
239: . ;;;
240: . ;
241: . If $Translate(X,"-")=1 Do Quit
242: . . Set VALUE=$$PI()/2*X
243: . . Quit
244: . If X>0.9 Do Quit
245: . . Set SIGS=$Select(X<0:-1,1:1)
246: . . Set VALUE=1/(1/X/X-1)
247: . . Set X=$$SQRT(VALUE)
248: . . Set VALUE=$$ARCTAN(X,10)
249: . . Set VALUE=VALUE*SIGS
250: . . Quit
251: . Set (VALUE,L)=X
252: . Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
253: . For K=3:2 Do Quit:($Translate(L,"-")<LIM)
254: . . Set L=L*X*X*(K-2)/(K-1)*(K-2)/K,VALUE=VALUE+L
255: . . Quit
256: . Set VALUE=$Select(SIGT<1:-VALUE,1:VALUE)
257: . Quit
258: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
259: For K=3:2 Do Quit:($Translate(1/L,"-")<LIM)
260: . ;
261: . Set L=L*X*X,VALUE=VALUE+(1/(K*L)*SIGN)
262: . ;;;
263: . ;
264: . Set SIGN=SIGN*-1
265: . Quit
266: Set VALUE=$Select(FOLD:$$PI()/2-VALUE,1:VALUE)
267: Set VALUE=$Select(SIGT<1:-VALUE,1:VALUE)
268: Quit VALUE
269: ;===
270: ;
271: ;
272: ARCTANH(X,PREC) ;
273: If X<-1 Set $Ecode=",M28,"
274: If X>1 Set $Ecode=",M28,"
275: ;
276: Set PREC=$Get(PREC,11)
277: ;;;
278: ;
279: Quit $$LOG(1+X/(1-X),PREC)/2
280: ;===
281: ;
282: ;
283: CABS(Z) ;
284: New ZRE,ZIM
285: Set ZRE=+Z,ZIM=+$Piece(Z,"%",2)
286: Quit $$SQRT(ZRE*ZRE+(ZIM*ZIM))
287: ;===
288: ;
289: ;
290: CADD(X,Y) ;
291: New XRE,XIM,YRE,YIM
292: Set XRE=+X,XIM=+$Piece(X,"%",2)
293: Set YRE=+Y,YIM=+$Piece(Y,"%",2)
294: Quit XRE+YRE_"%"_(XIM+YIM)
295: ;===
296: ;
297: ;
298: CCOS(Z,PREC) ;
299: New E1,E2,IA
300: ;
301: Set PREC=$Get(PREC,11)
302: ;;;
303: ;
304: Set IA=$$CMUL(Z,"0%1")
305: Set E1=$$CEXP(IA,PREC)
306: Set IA=-IA_"%"_(-$Piece(IA,"%",2))
307: Set E2=$$CEXP(IA,PREC)
308: Set IA=$$CADD(E1,E2)
309: Quit $$CMUL(IA,"0.5%0")
310: ;===
311: ;
312: ;
313: CDIV(X,Y) ;
314: New D,IM,RE,XIM,XRE,YIM,YRE
315: Set XRE=+X,XIM=+$Piece(X,"%",2)
316: Set YRE=+Y,YIM=+$Piece(Y,"%",2)
317: Set D=YRE*YRE+(YIM*YIM)
318: Set RE=XRE*YRE+(XIM*YIM)/D
319: Set IM=XIM*YRE-(XRE*YIM)/D
320: Quit RE_"%"_IM
321: ;===
322: ;
323: ;
324: CEXP(Z,PREC) ;
325: New R,ZIM,ZRE
326: ;
327: Set PREC=$Get(PREC,11)
328: ;;;
329: ;
330: Set ZRE=+Z,ZIM=+$Piece(Z,"%",2)
331: Set R=$$EXP(ZRE,PREC)
332: Quit R*$$COS(ZIM,PREC)_"%"_(R*$$SIN(ZIM,PREC))
333: ;===
334: ;
335: ;
336: CLOG(Z,PREC) ;
337: New ABS,ARG,ZIM,ZRE
338: ;
339: Set PREC=$Get(PREC,11)
340: ;;;
341: ;
342: Set ABS=$$CABS(Z)
343: Set ZRE=+Z,ZIM=+$Piece(Z,"%",2)
344: ;
345: Set ARG=$$ARCTAN(ZIM/ZRE,PREC)
346: ;;;
347: ;
348: Quit $$LOG(ABS,PREC)_"%"_ARG
349: ;===
350: ;
351: ;
352: CMUL(X,Y) ;
353: New XIM,XRE,YIM,YRE
354: Set XRE=+X,XIM=+$Piece(X,"%",2)
355: Set YRE=+Y,YIM=+$Piece(Y,"%",2)
356: Quit XRE*YRE-(XIM*YIM)_"%"_(XRE*YIM+(XIM*YRE))
357: ;===
358: ;
359: ;
360: COMPLEX(X) Quit +X_"%0"
361: ;===
362: ;
363: ;
364: CONJUG(Z) ;
365: New ZIM,ZRE
366: Set ZRE=+Z,ZIM=+$Piece(Z,"%",2)
367: Quit ZRE_"%"_(-ZIM)
368: ;===
369: ;
370: ;
371: COS(X,PREC) ;
372: I '$D(PREC) Q $$%COS(X)
373: New L,LIM,K,SIGN,VALUE
374: ;
375: Set:X[":" X=$$DMSDEC(X)
376: ;;;
377: ;
378: Set PREC=$Get(PREC,11)
379: Set X=X#(2*$$PI())
380: Set (VALUE,L)=1,SIGN=-1
381: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
382: For K=2:2 Do Quit:($Translate(L,"-")<LIM) Set SIGN=SIGN*-1
383: . Set L=L*X*X/(K-1*K),VALUE=VALUE+(SIGN*L)
384: . Quit
385: Quit VALUE
386: ;===
387: ;
388: ;
389: %COS(X) ;
390: ;;; ; Number ~~
391: ; Winfried Gerum (8 June 1995)
392: ;
393: New A,N,PI,R,SIGN,XX
394: ;
395: ; This approximation only works for 0 <= x <= pi/2
396: ; so reduce angle to correct quadrant.
397: ;
398: Set PI=$$PI(),X=X#(PI*2),SIGN=1
399: Set:X>PI X=2*PI-X
400: Set:X*2>PI X=PI-X,SIGN=-1
401: ;
402: Set XX=X*X,A(1)=-0.4999999963,A(2)=0.0416666418
403: Set A(3)=-0.0013888397,A(4)=0.0000247609,A(5)=-0.0000002605
404: Set (X,R)=1 For N=1:1:5 Set X=X*XX,R=A(N)*X+R
405: Quit R*SIGN
406: ;===
407: ;
408: ;
409: COSH(X,PREC) ;
410: ;
411: New E,F,I,P,R,T,XX
412: ;;;
413: ;
414: Set PREC=$Get(PREC,11)+1
415: Set @("E=1E-"_PREC)
416: Set XX=X*X,F=1,(P,R,T)=1,I=1
417: For Set T=T*XX,F=I+1*I*F,R=T/F+R,P=P-R/R,I=I+2 If -E<P,P<E Quit
418: Quit R
419: ;===
420: ;
421: ;
422: COT(X,PREC) ;
423: New C,L,LIM,K,SIGN,VALUE
424: Set:X[":" X=$$DMSDEC(X)
425: ;;;
426: ;
427: Set PREC=$Get(PREC,11)
428: Set (VALUE,L)=1,SIGN=-1
429: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
430: For K=2:2 Do Quit:($Translate(L,"-")<LIM) Set SIGN=SIGN*-1
431: . Set L=L*X*X/(K-1*K),VALUE=VALUE+(SIGN*L)
432: . Quit
433: Set C=VALUE
434: Set X=X#(2*$$PI())
435: Set (VALUE,L)=X,SIGN=-1
436: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
437: For K=3:2 Do Quit:($Translate(L,"-")<LIM) Set SIGN=SIGN*-1
438: . Set L=L/(K-1)*X/K*X,VALUE=VALUE+(SIGN*L)
439: . Quit
440: If 'VALUE Quit "INFINITE"
441: Quit VALUE=C/VALUE
442: ;===
443: ;
444: ;
445: COTH(X,PREC) ;
446: New SINH
447: If 'X Quit "INFINITE"
448: ;
449: Set PREC=$Get(PREC,11)
450: ;;;
451: ;
452: Set SINH=$$SINH(X,PREC)
453: If 'SINH Quit "INFINITE"
454: Quit $$COSH(X,PREC)/SINH
455: ;===
456: ;
457: ;
458: CPOWER(Z,N,PREC) ;
459: New AR,NIM,NRE,PHI,PI,R,RHO,TH,ZIM,ZRE
460: ;
461: Set PREC=$Get(PREC,11)
462: ;;;
463: ;
464: Set ZRE=+Z,ZIM=+$Piece(Z,"%",2)
465: Set NRE=+N,NIM=+$Piece(N,"%",2)
466: If 'ZRE,'ZIM,'NRE,'NIM Set $Ecode=",M28,"
467: ;
468: If 'ZRE,'ZIM Quit "0%0"
469: ;;;
470: ;
471: Set PI=$$PI()
472: ;
473: Set R=$$SQRT(ZRE*ZRE+(ZIM*ZIM),PREC)
474: ;;;
475: ;
476: ;
477: If ZRE Set TH=$$ARCTAN(ZIM/ZRE,PREC)
478: ;;;
479: ;
480: Else Set TH=$SELECT(ZIM>0:PI/2,1:-PI/2)
481: ;;;
482: ;
483: Set RHO=$$LOG(R,PREC)
484: Set AR=$$EXP(RHO*NRE-(TH*NIM),PREC)
485: Set PHI=RHO*NIM+(NRE*TH)
486: Quit AR*$$COS(PHI,PREC)_"%"_(AR*$$SIN(PHI,PREC))
487: ;===
488: ;
489: ;
490: CSC(X,PREC) ;
491: New L,LIM,K,SIGN,VALUE
492: ;
493: ; Winfried Gerum (8 June 1995)
494: Set:X[":" X=$$DMSDEC(X)
495: ;;;
496: ;
497: Set PREC=$Get(PREC,11)
498: ;;;
499: ;
500: Set X=X#(2*$$PI())
501: Set (VALUE,L)=X,SIGN=-1
502: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
503: For K=3:2 Do Quit:($Translate(L,"-")<LIM) Set SIGN=SIGN*-1
504: . Set L=L/(K-1)*X/K*X,VALUE=VALUE+(SIGN*L)
505: . Quit
506: If 'VALUE Quit "INFINITE"
507: Quit 1/VALUE
508: ;===
509: ;
510: ;
511: ;
512: CSCH(X,PREC)
513: Quit 1/$$SINH(X,$Get(PREC,11))
514: ;;;
515: ;
516: ;===
517: ;
518: ;
519: CSIN(Z,PREC) ;
520: New IA,E1,E2
521: ;
522: Set PREC=$Get(PREC,11)
523: ;;;
524: ;
525: Set IA=$$CMUL(Z,"0%1")
526: Set E1=$$CEXP(IA,PREC)
527: Set IA=-IA_"%"_(-$Piece(IA,"%",2))
528: Set E2=$$CEXP(IA,PREC)
529: Set IA=$$CSUB(E1,E2)
530: Set IA=$$CMUL(IA,"0.5%0")
531: Quit $$CMUL("0%-1",IA)
532: ;===
533: ;
534: ;
535: CSUB(X,Y) ;
536: New XIM,XRE,YIM,YRE
537: Set XRE=+X,XIM=+$Piece(X,"%",2)
538: Set YRE=+Y,YIM=+$Piece(Y,"%",2)
539: Quit XRE-YRE_"%"_(XIM-YIM)
540: ;===
541: ;
542: ;
543: DECDMS(X,PREC) ;
544: Set PREC=$Get(PREC,5)
545: Set X=X#360*3600
546: Set X=+$Justify(X,0,$Select((PREC-$Length(X\1))'<0:PREC-$Length(X\1),1:0))
547: Quit X\3600_":"_(X\60#60)_":"_(X#60)
548: ;===
549: ;
550: ;
551: DEGRAD(X) Quit X*3.14159265358979/180
552: ;===
553: ;
554: ;
555: DMSDEC(X) ;
556: Quit $Piece(X,":")+($Piece(X,":",2)/60)+($Piece(X,":",3)/3600)
557: ;===
558: ;
559: ;
560: E() Quit 2.71828182845905
561: ;===
562: ;
563: ;
564: EXP(X,PREC) ;
565: New L,LIM,K,VALUE
566: Set PREC=$Get(PREC,11)
567: Set L=X,VALUE=X+1
568: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
569: For K=2:1 Set L=L*X/K,VALUE=VALUE+L Quit:($Translate(L,"-")<LIM)
570: Quit VALUE
571: ;===
572: ;
573: ;
574: LOG(X,PREC) ;
575: New L,LIM,M,N,K,VALUE
576: If X'>0 Set $Ecode=",M28,"
577: Set PREC=$Get(PREC,11)
578: Set M=1
579: ;
580: For N=0:1 Quit:(X/M)<10 Set M=M*10
581: ;;;
582: ;
583: If X<1 For N=0:-1 Quit:(X/M)>0.1 Set M=M*0.1
584: Set X=X/M
585: Set X=(X-1)/(X+1),(VALUE,L)=X
586: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
587: For K=3:2 Set L=L*X*X,M=L/K,VALUE=M+VALUE Set:M<0 M=-M Quit:M<LIM
588: Set VALUE=VALUE*2+(N*2.30258509298749)
589: Quit VALUE
590: ;===
591: ;
592: ;
593: LOG10(X,PREC) ;
594: New L,LIM,M,N,K,VALUE
595: If X'>0 Set $Ecode=",M28,"
596: Set PREC=$Get(PREC,11)
597: Set M=1
598: ;
599: For N=0:1 Quit:(X/M)<10 Set M=M*10
600: ;;;
601: ;
602: If X<1 For N=0:-1 Quit:(X/M)>0.1 Set M=M*0.1
603: Set X=X/M
604: Set X=(X-1)/(X+1),(VALUE,L)=X
605: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
606: For K=3:2 Set L=L*X*X,M=L/K,VALUE=M+VALUE Set:M<0 M=-M Quit:M<LIM
607: Set VALUE=VALUE*2+(N*2.30258509298749)
608: Quit VALUE/2.30258509298749
609: ;===
610: ;
611: ;
612: MTXADD(A,B,R,ROWS,COLS) ;
613: ; Add A[ROWS,COLS] to B[ROWS,COLS],
614: ; result goes to R[ROWS,COLS]
615: IF $DATA(A)<10 QUIT 0
616: IF $DATA(B)<10 QUIT 0
617: IF $GET(ROWS)<1 QUIT 0
618: IF $GET(COLS)<1 QUIT 0
619: ;
620: NEW ROW,COL,ANY
621: FOR ROW=1:1:ROWS FOR COL=1:1:COLS DO
622: . KVALUE R(ROW,COL) SET ANY=0
623: . SET:$DATA(A(ROW,COL))#2 ANY=1
624: . SET:$DATA(B(ROW,COL))#2 ANY=1
625: . SET:ANY R(ROW,COL)=$GET(A(ROW,COL))+$GET(B(ROW,COL))
626: . QUIT
627: QUIT 1
628: ;===
629: ;
630: ;
631: MTXCOF(A,I,K,N) ;
632: ; Compute cofactor for element [i,k]
633: ; in matrix A[N,N]
634: NEW T,R,C,RR,CC
635: SET CC=0 FOR C=1:1:N DO:C'=K
636: . SET CC=CC+1,RR=0
637: . FOR R=1:1:N SET:R'=I RR=RR+1,T(RR,CC)=$GET(A(R,C))
638: . QUIT
639: QUIT $$MTXDET(.T,N-1)
640: ;===
641: ;
642: ;
643: MTXCOPY(A,R,ROWS,COLS) ;
644: ; Copy A[ROWS,COLS] to R[ROWS,COLS]
645: IF $DATA(A)<10 QUIT 0
646: IF $GET(ROWS)<1 QUIT 0
647: IF $GET(COLS)<1 QUIT 0
648: ;
649: NEW ROW,COL
650: FOR ROW=1:1:ROWS FOR COL=1:1:COLS DO
651: . KVALUE R(ROW,COL)
652: . SET:$DATA(A(ROW,COL))#2 R(ROW,COL)=A(ROW,COL)
653: . QUIT
654: QUIT 1
655: ;===
656: ;
657: ;
658: MTXDET(A,N) ;
659: ; Compute determinant of matrix A[N,N]
660: IF $DATA(A)<10 QUIT ""
661: IF N<1 QUIT ""
662: ;
663: ; First the simple cases
664: ;
665: IF N=1 QUIT $GET(A(1,1))
666: IF N=2 QUIT $GET(A(1,1))*$GET(A(2,2))-($GET(A(1,2))*$GET(A(2,1)))
667: ;
668: NEW DET,I,SIGN
669: ;
670: ; Det A = sum (k=1:n) element (i,k) * cofactor [i,k]
671: ;
672: SET DET=0,SIGN=1
673: FOR I=1:1:N DO
674: . SET DET=$GET(A(1,I))*$$MTXCOF(.A,1,I,N)*SIGN+DET
675: . SET SIGN=-SIGN
676: . QUIT
677: QUIT DET
678: ;===
679: ;
680: ;
681: MTXEQU(A,B,R,N,M) ;
682: ; Solve matrix equation A [M,M] * R [M,N] = B [M,N]
683: IF $GET(M)<1 QUIT ""
684: IF $GET(N)<1 QUIT ""
685: IF '$$MTXDET(.A) QUIT 0
686: ;
687: NEW I,I1,J,J1,J2,K,L,T,T1,T2,TEMP,X
688: ;
689: SET X=$$MTXCOPY(.A,.T,N,N)
690: SET X=$$MTXCOPY(.B,.R,N,M)
691: ;
692: ; Reduction of matrix A
693: ; Steps of reduction are counted by index K
694: ;
695: FOR K=1:1:N-1 DO
696: . ;
697: . ; Search for largest coefficient of T
698: . ; (denoted by TEMP)
699: . ; in first column of reduced system
700: . ;
701: . SET TEMP=0,J2=K
702: . FOR J1=K:1:N DO
703: . . QUIT:$TRANSLATE($GET(T(J1,K)),"-")>$TRANSLATE(TEMP,"-")
704: . . SET TEMP=T(J1,K),J2=J1
705: . . QUIT
706: . ;
707: . ; Exchange row number K with row number J2,
708: . ; if necessary
709: . ;
710: . DO:J2'=K
711: . . ;
712: . . FOR J=K:1:N DO
713: . . . SET T1=$GET(T(K,J)),T2=$GET(T(J2,J))
714: . . . KILL T(K,J),T(J2,J)
715: . . . IF T1'="" SET T(J2,J)=T1
716: . . . IF T2'="" SET T(K,J)=T2
717: . . . QUIT
718: . . FOR J=1:1:M DO
719: . . . SET T1=$GET(R(K,J)),T2=$GET(R(J2,J))
720: . . . KILL R(K,J),R(J2,J)
721: . . . IF T1'="" SET R(J2,J)=T1
722: . . . IF T2'="" SET R(K,J)=T2
723: . . . QUIT
724: . . QUIT
725: . ;
726: . ; Actual reduction
727: . ;
728: . FOR I=K+1:1:N DO
729: . . FOR J=K+1:1:N DO
730: . . . QUIT:'$GET(T(K,K))
731: . . . SET T(I,J)=-$GET(T(K,J))*$GET(T(I,K))/T(K,K)+$GET(T(I,J))
732: . . . QUIT
733: . . FOR J=1:1:M DO
734: . . . QUIT:'$GET(T(K,K))
735: . . . SET R(I,J)=-$GET(R(K,J))*$GET(T(I,K))/T(K,K)+$GET(R(I,J))
736: . . . QUIT
737: . . QUIT
738: . QUIT
739: ;
740: ; Backsubstitution
741: ;
742: FOR J=1:1:M DO
743: . IF $GET(T(N,N)) SET R(N,J)=$GET(R(N,J))/T(N,N)
744: . IF N-1>0 FOR I1=1:1:N-1 DO
745: . . SET I=N-I1
746: . . FOR L=I+1:1:N DO
747: . . . SET R(I,J)=-$GET(T(I,L))*$GET(R(L,J))+$GET(R(I,J))
748: . . . QUIT
749: . . IF $GET(T(I,I)) SET R(I,J)=$GET(R(I,J))/$GET(T(I,I))
750: . . QUIT
751: . QUIT
752: QUIT $$MTXDET(.R)
753: ;===
754: ;
755: ;
756: MTXINV(A,R,N) ;
757: ; Invert A[N,N], result goes to R[N,N]
758: IF $DATA(A)<10 QUIT 0
759: IF $GET(N)<1 QUIT 0
760: ;
761: NEW T,X
762: SET X=$$MTXUNIT(.T,N)
763: QUIT $$MTXEQU(.A,.T,.R,N,N)
764: ;===
765: ;
766: ;
767: MTXMUL(A,B,R,M,L,N) ;
768: ; Multiply A[M,L] by B[L,N], result goes to R[M,N]
769: IF $DATA(A)<10 QUIT 0
770: IF $DATA(B)<10 QUIT 0
771: IF $GET(L)<1 QUIT 0
772: IF $GET(M)<1 QUIT 0
773: IF $GET(N)<1 QUIT 0
774: ;
775: NEW I,J,K,SUM,ANY
776: FOR I=1:1:M FOR J=1:1:N DO
777: . SET (SUM,ANY)=0
778: . KVALUE R(I,J)
779: . FOR K=1:1:L DO
780: . . SET:$DATA(A(I,K))#2 ANY=1
781: . . SET:$DATA(B(K,J))#2 ANY=1
782: . . SET SUM=$GET(A(I,K))*$GET(B(K,J))+SUM
783: . . QUIT
784: . SET:ANY R(I,J)=SUM
785: . QUIT
786: QUIT 1
787: ;===
788: ;
789: ;
790: MTXSCA(A,R,ROWS,COLS,S) ;
791: ; Multiply A[ROWS,COLS] with the scalar S,
792: ; result goes to R[ROWS,COLS]
793: IF $DATA(A)<10 QUIT 0
794: IF $GET(ROWS)<1 QUIT 0
795: IF $GET(COLS)<1 QUIT 0
796: IF '($DATA(S)#2) QUIT 0
797: ;
798: NEW ROW,COL
799: FOR ROW=1:1:ROWS FOR COL=1:1:COLS DO
800: . KVALUE R(ROW,COL)
801: . SET:$DATA(A(ROW,COL))#2 R(ROW,COL)=A(ROW,COL)*S
802: . QUIT
803: QUIT 1
804: ;===
805: ;
806: ;
807: MTXSUB(A,B,R,ROWS,COLS) ;
808: ; Subtract B[ROWS,COLS] from A[ROWS,COLS],
809: ; result goes to R[ROWS,COLS]
810: IF $DATA(A)<10 QUIT 0
811: IF $DATA(B)<10 QUIT 0
812: IF $GET(ROWS)<1 QUIT 0
813: IF $GET(COLS)<1 QUIT 0
814: ;
815: NEW ROW,COL,ANY
816: FOR ROW=1:1:ROWS FOR COL=1:1:COLS DO
817: . KVALUE R(ROW,COL) SET ANY=0
818: . SET:$DATA(A(ROW,COL))#2 ANY=1
819: . SET:$DATA(B(ROW,COL))#2 ANY=1
820: . ;
821: . SET:ANY R(ROW,COL)=$GET(A(ROW,COL))-$GET(B(ROW,COL))
822: . ;;;
823: . ;
824: . QUIT
825: QUIT 1
826: ;===
827: ;
828: ;
829: MTXTRP(A,R,M,N) ;
830: ; Transpose A[M,N], result goes to R[N,M]
831: IF $DATA(A)<10 QUIT 0
832: IF $GET(M)<1 QUIT 0
833: IF $GET(N)<1 QUIT 0
834: ;
835: NEW I,J,K,D1,V1,D2,V2
836: FOR I=1:1:M+N-1 FOR J=1:1:I+1\2 DO
837: . SET K=I-J+1
838: . IF K=J DO QUIT
839: . . SET V1=$GET(A(J,J)),D1=$DATA(A(J,J))#2
840: . . IF J'>N,J'>M KVALUE R(J,J) SET:D1 R(J,J)=V1
841: . . QUIT
842: . ;
843: . SET V1=$GET(A(K,J)),D1=$DATA(A(K,J))#2
844: . SET V2=$GET(A(J,K)),D2=$DATA(A(J,K))#2
845: . IF K'>M,J'>N KVALUE R(K,J) SET:D2 R(K,J)=V2
846: . IF J'>M,K'>N KVALUE R(J,K) SET:D1 R(J,K)=V1
847: . QUIT
848: QUIT 1
849: ;===
850: ;
851: ;
852: MTXUNIT(R,N,SPARSE) ;
853: ; Create a unit matrix R[N,N]
854: IF $GET(N)<1 QUIT 0
855: ;
856: NEW ROW,COL
857: FOR ROW=1:1:N FOR COL=1:1:N DO
858: . KVALUE R(ROW,COL)
859: . IF $GET(SPARSE) QUIT:ROW'=COL
860: . SET R(ROW,COL)=$SELECT(ROW=COL:1,1:0)
861: . QUIT
862: QUIT 1
863: ;===
864: ;
865: ;
866: PI() Quit 3.14159265358979
867: ;===
868: ;
869: ;
870: RADDEG(X) Quit X*180/3.14159265358979
871: ;===
872: ;
873: ;
874: SEC(X,PREC) ;
875: New L,LIM,K,SIGN,VALUE
876: ;
877: Set:X[":" X=$$DMSDEC(X)
878: ;;;
879: ;
880: Set PREC=$Get(PREC,11)
881: Set X=X#(2*$$PI())
882: Set (VALUE,L)=1,SIGN=-1
883: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
884: For K=2:2 Do Quit:($Translate(L,"-")<LIM) Set SIGN=SIGN*-1
885: . Set L=L*X*X/(K-1*K),VALUE=VALUE+(SIGN*L)
886: . Quit
887: If 'VALUE Quit "INFINITE"
888: Quit 1/VALUE
889: ;===
890: ;
891: ;
892: SECH(X,PREC)
893: Quit 1/$$COSH(X,$Get(PREC,11))
894: ;;;
895: ;===
896: ;
897: ;
898: SIGN(X) Quit $SELECT(X<0:-1,X>0:1,1:0)
899: ;===
900: ;
901: ;
902: SIN(X,PREC) ;
903: I '$D(PREC) Q $$%SIN(X)
904: New L,LIM,K,SIGN,VALUE
905: ;
906: Set:X[":" X=$$DMSDEC(X)
907: ;;;
908: ;
909: Set PREC=$Get(PREC,11)
910: Set X=X#(2*$$PI())
911: Set (VALUE,L)=X,SIGN=-1
912: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
913: For K=3:2 Do Quit:($Translate(L,"-")<LIM) Set SIGN=SIGN*-1
914: . Set L=L/(K-1)*X/K*X,VALUE=VALUE+(SIGN*L)
915: . Quit
916: Quit VALUE
917: ;===
918: ;
919: ;
920: %SIN(X) ;
921: ;
922: New A,N,PI,R,SIGN,XX
923: ;
924: ; This approximation only works for 0 <= x <= pi/2
925: ; so reduce angle to correct quadrant.
926: ;
927: Set PI=$$PI(),X=X#(PI*2),SIGN=1
928: Set:X>PI X=2*PI-X,SIGN=-1
929: ;
930: Set:X*2<PI X=PI-X
931: ;;;
932: ;
933: ;
934: Set XX=X*X,A(1)=-0.4999999963,A(2)=0.0416666418
935: Set A(3)=-0.0013888397,A(4)=0.0000247609,A(5)=-0.0000002605
936: Set (X,R)=1 For N=1:1:5 Set X=X*XX,R=A(N)*X+R
937: Quit R*SIGN
938: ;===
939: ;
940: ;
941: SINH(X,PREC) ;
942: ;
943: New E,F,I,P,R,T,XX
944: ;;;
945: ;
946: Set PREC=$Get(PREC,11)+1
947: Set @("E=1E-"_PREC)
948: Set XX=X*X,F=1,I=2,(P,R,T)=X
949: For Set T=T*XX,F=I+1*I*F,R=T/F+R,P=P-R/R,I=I+2 If -E<P,P<E Quit
950: Quit R
951: ;===
952: ;
953: ;
954: SQRT(X,PREC) ;
955: If X<0 Set $Ecode=",M28,"
956: If X=0 Quit 0
957: ;
958: Set PREC=$Get(PREC,11)
959: ;;;
960: ;
961: ;
962: If X<1 Quit 1/$$SQRT(1/X,PREC)
963: ;;;
964: ;
965: New P,R,E
966: Set PREC=$Get(PREC,11)+1
967: ;
968: Set @("E=1E-"_PREC)
969: ;;;
970: ;
971: Set R=X
972: For Set P=R,R=X/R+R/2,P=P-R/R If -E<P,P<E Quit
973: Quit R
974: ;===
975: ;
976: ;
977: TAN(X,PREC) ;
978: New L,LIM,K,S,SIGN,VALUE
979: ;
980: Set:X[":" X=$$DMSDEC(X)
981: ;;;
982: ;
983: Set PREC=$Get(PREC,11)
984: Set X=X#(2*$$PI())
985: Set (VALUE,L)=X,SIGN=-1
986: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
987: For K=3:2 Do Quit:($Translate(L,"-")<LIM) Set SIGN=SIGN*-1
988: . Set L=L/(K-1)*X/K*X,VALUE=VALUE+(SIGN*L)
989: . Quit
990: Set S=VALUE
991: Set X=X#(2*$$PI())
992: Set (VALUE,L)=1,SIGN=-1
993: Set LIM=$Select((PREC+3)'>11:PREC+3,1:11),@("LIM=1E-"_LIM)
994: For K=2:2 Do Quit:($Translate(L,"-")<LIM) Set SIGN=SIGN*-1
995: . Set L=L*X*X/(K-1*K),VALUE=VALUE+(SIGN*L)
996: . Quit
997: If 'VALUE Quit "INFINITE"
998: Quit S/VALUE
999: ;===
1000: ;
1001: ;
1002: TANH(X,PREC) ;
1003: ;
1004: Set PREC=$Get(PREC,11)
1005: ;;;
1006: ;
1007: Quit $$SINH(X,PREC)/$$COSH(X,PREC)
1008: ;===
1009:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>