Merge branch 'master' of git://repo.or.cz/mqlkit
[mqlkit.git] / indicators / GRFKalmanAGASJW.mq4
blob0d2fdd8ef86182bf72aca1f648c2e1f0c6fb32f2
1 \r
2 #property copyright "Copyleft © 2007, GammaRat Forex"\r
3 #property link      "http://www.gammarat.com/Forex/"\r
4 \r
5    \r
6 \r
7 //#include <GRFMatrixMath.mqh>\r
8 #property indicator_chart_window\r
9 #property indicator_buffers 7\r
10 #property indicator_color1 Orange\r
11 #property indicator_color2 Orange\r
12 #property indicator_color3 Orange\r
13 #property indicator_color4 Red\r
14 #property indicator_color5 Red\r
15 #property indicator_color6 Red\r
16 #property indicator_color7 Red\r
17 #property indicator_style1 0\r
18 #property indicator_style2 0\r
19 #property indicator_style3 0\r
20 #property indicator_style4 2\r
21 #property indicator_style5 2\r
22 #property indicator_style6 0\r
23 #property indicator_style7 0\r
26 //---- input parameters\r
27 extern double Samples=15;\r
28 extern double DevLevel1 = 2;\r
29 extern double DevLevel2 = 0;\r
30 extern double Suppression_dB = 0;\r
31 extern double PredictBars1 = 0;\r
32 extern double PredictBars2 = 0;\r
33 extern bool PrintCurrentState = true;\r
34 extern bool AutoGainOn = true;\r
37 //---- buffers\r
38 double KalmanBuffer[];\r
39 double KalmanBufferPlus1[];\r
40 double KalmanBufferNeg1[];\r
41 double KalmanBufferPlus2[];\r
42 double KalmanBufferNeg2[];\r
43 double KalmanBufferPlus3[];\r
44 double KalmanBufferNeg3[];\r
45 double xkk[2][1],xkk1[2][1],xk1k1[2][1],yk[1][1],zk[1][1];\r
46 double Pkk[2][2],Pkk1[2][2],Pk1k1[2][2],Pkk_inv[2][2];\r
47 double Qkk[2][2],Hk[1][2],Hk_t[2][1],Sk[1][1],Sk_inv[1][1],Rk[1][1],Kk[2][1];\r
48 double Fk[2][2],Fk_t[2][2],GGT[2][2];\r
49 double Eye[2][2];\r
50 double temp22[2][2],temp21[2][1],temp12[1][2],temp11[1][1];\r
51 double xp[1][1],xpt[1][1],xpt_t[1][1];\r
52 double Phi1[1][2],Phi2[1][2];\r
53 double xdel[1][1],xdel_t[1][1];\r
54 double LookAhead=0;\r
55 static bool initiated=false;\r
56 static double c0_above=0, c0_below = 0;\r
57 static double c1_above=0, c1_below = 0;\r
58 static int last_time=-1;\r
59 static int first_time = -1;\r
60 static int tick_count=0;\r
61 static int counted_bars = -1;\r
62 static double TestGain;\r
63 static bool PrintOn=false;\r
64 //+------------------------------------------------------------------+\r
65 //| Custom indicator initialization function                         |\r
66 //+------------------------------------------------------------------+\r
67 int init()\r
68   {\r
69   string shortname;\r
70   shortname = "KalmanAGAS "+DoubleToStr(Samples,1)+ " ";\r
71 //---- indicators\r
72    if(LookAhead <0)LookAhead=0;\r
73    SetIndexStyle(0,DRAW_LINE);\r
74    \r
75    SetIndexBuffer(0,KalmanBuffer);\r
76    SetIndexShift(0,LookAhead);\r
77    SetIndexDrawBegin(0,LookAhead);\r
78    SetIndexLabel(0,shortname);\r
79    if(MathAbs(DevLevel1) > 0) {\r
80       SetIndexStyle(1,DRAW_LINE);\r
81       SetIndexBuffer(1,KalmanBufferPlus1);\r
82       SetIndexShift(1,0);\r
83       SetIndexDrawBegin(1,1);\r
84       SetIndexLabel(1,shortname + DoubleToStr(DevLevel1,1)  + " Dev");\r
85       SetIndexStyle(2,DRAW_LINE);\r
86       SetIndexBuffer(2,KalmanBufferNeg1);\r
87       SetIndexShift(2,0);\r
88       SetIndexDrawBegin(2,1);\r
89       SetIndexLabel(2,shortname + DoubleToStr(-DevLevel1,1) + " Dev");\r
90     }\r
91    if(MathAbs(DevLevel2) > 0){\r
92       SetIndexStyle(3,DRAW_LINE);\r
93       SetIndexBuffer(3,KalmanBufferPlus2);\r
94       SetIndexShift(3,0);\r
95       SetIndexDrawBegin(3,1);\r
96       SetIndexLabel(3,shortname + DoubleToStr(DevLevel2,1) + " Dev");\r
97       SetIndexStyle(4,DRAW_LINE);\r
98       SetIndexBuffer(4,KalmanBufferNeg2);\r
99       SetIndexShift(4,0);\r
100       SetIndexDrawBegin(4,1);\r
101       SetIndexLabel(4,shortname + DoubleToStr(-DevLevel2,1) + " Dev");\r
102       \r
103       SetIndexStyle(5,DRAW_LINE);\r
104       SetIndexBuffer(5,KalmanBufferPlus3);\r
105       SetIndexShift(5,0);\r
106       SetIndexDrawBegin(5,1);\r
107       SetIndexLabel(5,shortname + " Highs " + DoubleToStr(DevLevel2,1) + " Dev");\r
108       SetIndexStyle(6,DRAW_LINE);\r
109       SetIndexBuffer(6,KalmanBufferNeg3);\r
110       SetIndexShift(6,0);\r
111       SetIndexDrawBegin(6,1);\r
112       SetIndexLabel(6,shortname + " Lows " + DoubleToStr(-DevLevel2,1) + " Dev");\r
113    }\r
114 //----\r
115    return(0);\r
116   }\r
117 //| Point and figure                                |\r
118 //+------------------------------------------------------------------+\r
119 int start()\r
121    int i;\r
122    double TestGain_saved;\r
123    i=0;\r
124    if(counted_bars == -1 && AutoGainOn){\r
125       if(Bars<Samples*2)Samples =Bars/4;\r
126       \r
127       //ramp up the gain\r
128       //first time through\r
129       TestGain=-1;\r
130       TestGain_saved = Suppression_dB;\r
131       while(true){\r
132          PrintOn=false;\r
133          initiated = 0;\r
134          counted_bars = 0;\r
135          last_time=-1;\r
136          compute();\r
137          //Print("TestGain ",TestGain," trial ", i);\r
138          Suppression_dB=TestGain;\r
139          if(MathAbs(TestGain-TestGain_saved) < 0.5)break;\r
140          TestGain_saved=TestGain;\r
141          i++;\r
142          if(i>20)break; \r
143       }\r
144       return;\r
145     }\r
146    PrintOn=true;\r
147    counted_bars=IndicatorCounted();\r
148    if( counted_bars<0) counted_bars=0;   \r
149    compute();\r
150    return(0);\r
152 int compute(){\r
153    int    i,j;\r
154    \r
155    double g,g1;\r
156    double c0;\r
157  \r
158    static double acc_sigma2;\r
159    static double z_sigma2;\r
160    static double normalization_time;\r
161    static double working_samples;\r
162    double z,diff; \r
163    double ts[3][1];\r
164    static double dev_level1,dev_level2; \r
165    double temp11a[1][1],temp21a[2][1];\r
166    double temp22a[2][2]; \r
167    static double ZGain = 0;\r
168    static double AGain=1;\r
169    static double tgain=1;\r
170    static double time_last;\r
171    static double zk_saved[2][1];\r
172    int trial;\r
173    \r
174    //----\r
175    //give it some time to warm up\r
176    if(Bars<Samples*2) \r
177       return(0);\r
178    //rewrite the last bar\r
179    //if(counted_bars > Bars-2)return(0);\r
180    //Print(Time[0]);\r
181   \r
182    if(Time[1] <= last_time) return(0);\r
183     Sleep(500);\r
184    //tick_count--;\r
185    //if(tick_count>0)return(0);\r
186    //tick_count=3;\r
187    if(counted_bars<0) counted_bars=0;\r
188    \r
189    //if(counted_bars>0)counted_bars--;\r
190    \r
191    //if(counted_bars >2) counted_bars -= 2;\r
192    \r
193    if( !initiated){\r
194        \r
195       Phi1[0][0] = 1;\r
196       Phi1[0][1] = PredictBars1;\r
197       Phi2[0][0] = 1;\r
198       Phi2[0][1] = PredictBars2;\r
199             \r
200       dev_level1 = DevLevel1*MathSqrt(2);\r
201       dev_level2 = DevLevel2*MathSqrt(2);\r
202       ArrayCopy(KalmanBuffer,High);\r
203       xkk1[0][0] = get_avg(Bars-1);\r
204       xkk1[1][0] = 0;\r
205       //MatrixPrint(xkk);\r
206       MatrixEye(Pkk1);\r
207       Fk[0][0] = 1;\r
208       Fk[0][1]=1;\r
209       Fk[1][0]=0;\r
210       Fk[1][1] =1;\r
211       MatrixTranspose(Fk,Fk_t);\r
212       Qkk[0][0] = 1;\r
213       Qkk[0][1] = 0;\r
214       Qkk[1][0]=0;\r
215       Qkk[1][1] = 1;\r
216       GGT[0][0]=.25;\r
217       GGT[1][0]=.5;\r
218       GGT[0][1]=.5;\r
219       GGT[1][1]=1;\r
220       Hk[0][0] = 1;\r
221       Hk[0][1] = 0;\r
222       MatrixTranspose(Hk,Hk_t);\r
223       \r
224       Rk[0][0] = .1;\r
225       MatrixEye(Eye);\r
226       ZGain = 0;\r
227       z_sigma2 = MathPow(10,-ZGain);\r
228       acc_sigma2 = Point*MathPow(10,-Suppression_dB/20.);//*MathSqrt(0.0001/Point);\r
229       MatrixScalarMul(acc_sigma2,Pkk1);\r
230       working_samples = Samples;\r
231       acc_sigma2 *= acc_sigma2;\r
232        Rk[0][0] = z_sigma2;\r
233       initiated = true;\r
234       MatrixScalarMul(acc_sigma2,Pkk);\r
235       //Print(1./MathSqrt(acc_sigma2));\r
236       //Print(" ");\r
237       AGain=1;\r
238       zk_saved[0][0] = get_avg(Bars-1);\r
239       zk_saved[1][0] = 0;\r
240       //double A1[20][2],A1t[2][20],A11[2][2],A11_inv[2][2],X[2],B[20];\r
241       //for(i=0;i<20;i++){\r
242       ArrayCopy(xkk1,zk_saved);\r
243          \r
244    }\r
245  \r
246    for(i=Bars-counted_bars-1; i> 0;i--){ //ticks are handled elsewhere\r
247      \r
248      //Start the update procedure\r
249      \r
250       MatrixZero(yk);\r
251       zk[0][0] = get_avg(i);\r
252       zk[1][0]= zk[0][0]-zk_saved[0][0];\r
253       ArrayCopy(zk_saved,zk);\r
254       diff = (zk[0][0]*Point-xkk1[0][0]*Point);\r
255       if(diff>=0) {\r
256          diff = diff*diff;\r
257          c0_above = (c0_above*(working_samples-1)+MathPow(get_avg(i)*Point-KalmanBuffer[i],2))/working_samples;\r
258          c0_below = c0_below*(working_samples-1)/working_samples;\r
259          c1_above = (c1_above*(working_samples-1)+MathPow(High[i]-KalmanBuffer[i],2))/working_samples;\r
260          c1_below = c1_below*(working_samples-1)/working_samples;\r
261       }else{\r
262          diff = diff*diff;\r
263          c0_below = (c0_below*(working_samples-1)+MathPow(get_avg(i)*Point-KalmanBuffer[i],2))/working_samples;\r
264          c0_above = c0_above*(working_samples-1)/working_samples;\r
265          c1_below = (c1_below*(working_samples-1)+MathPow(Low[i]-KalmanBuffer[i],2))/working_samples;\r
266          c1_above = c1_above*(working_samples-1)/working_samples;\r
267       }  \r
268       if(MathAbs(DevLevel1)>0){\r
269          KalmanBufferPlus1[i-1] = KalmanBuffer[i]+dev_level1*MathSqrt(c0_above);\r
270          KalmanBufferNeg1[i-1] = KalmanBuffer[i]-dev_level1*MathSqrt(c0_below);\r
271       }\r
272       if(MathAbs(DevLevel2)>0){   \r
273          KalmanBufferPlus2[i-1] = KalmanBuffer[i]+dev_level2*MathSqrt(c0_above);\r
274          KalmanBufferNeg2[i-1] = KalmanBuffer[i]-dev_level2*MathSqrt(c0_below);\r
275          KalmanBufferPlus3[i-1] = KalmanBuffer[i]+dev_level2*MathSqrt(c1_above);\r
276          KalmanBufferNeg3[i-1] = KalmanBuffer[i]-dev_level2*MathSqrt(c1_below);\r
277       } \r
278       //this is always left alone, for now\r
279            \r
280      \r
281       ArrayCopy(Qkk,GGT);\r
282       \r
283       // set the auto gain\r
284       if(AutoGainOn && MathSqrt((c0_above+c0_below)/2)> Point){   \r
285          if(c0_below >0 || c0_above>0)\r
286             tgain = MathAbs((c0_below)/(c0_below+c0_above));\r
287          else tgain = 0.5;\r
288          if(tgain< 0.2 || tgain > 0.8)\r
289             AGain = AGain*MathPow(10,0.001);\r
290          else if(tgain> 0.45 && tgain < 0.55)\r
291             AGain=AGain*MathPow(10,-0.001);\r
292       }else{\r
293          AGain = 1;\r
294       }   \r
295       tgain = AGain*acc_sigma2;\r
296       TestGain=-20*MathLog(MathSqrt(tgain)/Point)/MathLog(10.);\r
297       MatrixScalarMul(tgain,Qkk);\r
298       MatrixMul(Hk,xkk1,temp11);\r
299       //MatrixPrint(xkk1);\r
300       MatrixAdd(zk,temp11,yk, -1); \r
301       MatrixMul(Hk,Pkk1,temp12);\r
302       MatrixMul(temp12,Hk_t,temp11);\r
303       MatrixAdd(temp11,Rk,Sk,1);\r
304       MatrixInvert(Sk,Sk_inv);\r
305       MatrixMul(Pkk1,Hk_t,temp21);\r
306       MatrixMul(temp21,Sk_inv,Kk);\r
307       MatrixMul(Kk,yk,temp21);\r
308       MatrixAdd(temp21,xkk1,xkk,1);\r
309       \r
310       MatrixMul(Kk,Hk,temp22);\r
311       MatrixAdd(Eye,temp22,temp22a,-1);\r
312       MatrixMul(temp22a,Pkk1,Pkk);\r
313       \r
314       //predict cycle\r
315           //make copies of the last iteration;\r
316       ArrayCopy(Pk1k1,Pkk);\r
317       ArrayCopy(xk1k1,xkk);\r
318       //g = MathSqrt(MathPow(xkk[0][0]-get_avg(i),2))/Pkk[0][0]*Point;\r
319       //g = xkk[1][0];\r
320       //predict the state\r
321       //Print("Doing Predict");\r
322       MatrixMul(Fk,xk1k1,xkk1);\r
323       //MatrixPrint(xkk1);\r
324       MatrixMul(Fk,Pk1k1,temp22);\r
325       MatrixMul(temp22,Fk_t,Pk1k1);\r
326       MatrixAdd(Pk1k1,Qkk,Pkk1,1);\r
327       //Print("Predict Ended");\r
328       KalmanBuffer[i-1] = (xkk1[0][0]*Point);\r
329  \r
330       if(i==1 && PrintOn){\r
331          if(PrintCurrentState){\r
332             Print("Suppression (dB):", -20*MathLog(MathSqrt(tgain)/Point)/MathLog(10.),"dB; Value:",xkk1[0][0]*Point\r
333             ,"; Speed (pips/bar):",xkk1[1][0],"; dp/p:"\r
334             , xkk1[1][0]/xkk1[0][0]*100.*240.*(24.*60.)/Period()\r
335             ,"; Line speed "\r
336             ,(KalmanBuffer[i-1]-KalmanBuffer[i])/KalmanBuffer[i]\r
337             *100.*240.*(24.*60.)/Period());\r
338          }\r
339          if(PredictBars1 > 0){\r
340             MatrixMul(Phi1,xkk,xpt);\r
341             xp[0][0] = xkk[0][0];\r
342          \r
343             MatrixTranspose(Phi1,temp21);\r
344             MatrixMul(Pkk,temp21,temp21a);\r
345             MatrixMul(Phi1,temp21a,temp11a);\r
346             MatrixInvert(temp11a,temp11);\r
347             MatrixAdd(xp,xpt,xdel,-1);\r
348             MatrixTranspose(xdel,xdel_t);\r
349             MatrixMul(temp11,xdel,temp11a);\r
350             MatrixMul(xdel_t,temp11a,temp11);\r
351             g1=MathSqrt(temp11[0][0]);\r
352             Print(Phi1[0][1], " bar bounds ",(xpt[0][0]-g1)*Point\r
353                ,",",(xpt[0][0]+g1)*Point);\r
354             Print(Phi1[0][1], " bar center, r(t) (in pips) ",(xpt[0][0])*Point\r
355                ,",",g1);\r
356          }\r
357          if(PredictBars2 > 0){\r
358             MatrixMul(Phi2,xkk,xpt);\r
359             xp[0][0] = xkk[0][0];\r
360             MatrixTranspose(Phi2,temp21);\r
361             MatrixMul(Pkk,temp21,temp21a);\r
362             MatrixMul(Phi2,temp21a,temp11a);\r
363             MatrixInvert(temp11a,temp11);\r
364             MatrixAdd(xp,xpt,xdel,-1);\r
365             MatrixTranspose(xdel,xdel_t);\r
366             MatrixMul(temp11,xdel,temp11a);\r
367             MatrixMul(xdel_t,temp11a,temp11);\r
368             g1=MathSqrt(temp11[0][0]);\r
369             Print(Phi2[0][1], " bar bounds ",(xpt[0][0]-g1)*Point\r
370                ,",",(xpt[0][0]+g1)*Point);\r
371             Print(Phi2[0][1], " bar center, r(t) (in pips) ",(xpt[0][0])*Point\r
372                ,",",g1);\r
373          }         \r
374       }else g= -1;\r
375       \r
376     last_time=Time[i];\r
377    }\r
378    \r
379    //Print(Pkk1[0][0]/Point," ",Pkk1[1][0]/Point," ",Pkk1[1][1]/Point);\r
380    //Print("24 hours:", xpt[0][0]*Point," ,Gate ",g);\r
381    //Print ("Gate 2 ", MathSqrt(xdel[0][0]*xdel[0][0]*g)*Point," g1 ",g1);\r
384  \r
385  double get_avg(int k){\r
386    return(MathPow((High[k]*Low[k]*Close[k]*Close[k]),1/4.)/Point);\r
387 }        \r
388 //+++++++-----------------------------------------------------+\r
389 double determinant_fixed(double a[][], int k){\r
390    double z;\r
391     switch(k){\r
392       case 1: return(a[0][0]);\r
393          break;\r
394       case 2: return( a[0][0]*a[1][1]-a[1][0]*a[0][1]);\r
395          break;\r
396       case 3:\r
397          z = a[0][0]*a[1][1]*a[2][2] + a[0][1]*a[1][2]*a[2][0] + \r
398              a[0][2]*a[1][0]*a[2][1] -\r
399             (a[2][0]*a[1][1]*a[0][2] + a[1][0]*a[0][1]*a[2][2] + \r
400              a[0][0]*a[2][1]*a[1][2]);\r
401          return(z);\r
402          break;\r
403       default:      \r
404           Print("array_range too large for determinant calculation");\r
405          return(0);\r
406    }  \r
408 double determinant(double a[][]){\r
409    double z;\r
410    if(ArrayRange(a,0) != ArrayRange(a,1)){\r
411       Print("Non-square array entered into determinant");\r
412       return(0);\r
413     }\r
414     switch(ArrayRange(a,0)){\r
415       case 1: return(a[0][0]);\r
416          break;\r
417       case 2: return( a[0][0]*a[1][1]-a[1][0]*a[0][1]);\r
418          break;\r
419       case 3:\r
420          z = a[0][0]*a[1][1]*a[2][2] + a[0][1]*a[1][2]*a[2][0] + \r
421              a[0][2]*a[1][0]*a[2][1] -\r
422             (a[2][0]*a[1][1]*a[0][2] + a[1][0]*a[0][1]*a[2][2] + \r
423              a[0][0]*a[2][1]*a[1][2]);\r
424          return(z);\r
425          break;\r
426       default:      \r
427           Print("array_range too large for determinant calculation");\r
428          return(0);\r
429    }  \r
431   \r
432 int MatrixAdd(double a[][], double b[][],double & c[][],double w=1){\r
433    int i,j;\r
434    for(i=0;i<ArrayRange(c,0);i++){\r
435       for(j=0;j<ArrayRange(c,1);j++){\r
436          c[i][j] = a[i][j]+w*b[i][j];\r
437       }\r
438    }\r
439     return(0);   \r
441 int MatrixMul(double a[][], double b[][],double & c[][]){\r
442    int i,j, k;\r
443    if(ArrayRange(a,1) != ArrayRange(b,0)){\r
444       Print("array Range Mismatch in Matrix Mul");\r
445       return(-1);\r
446    }\r
447    for(i=0;i<ArrayRange(a,0);i++){\r
448       for(j=0;j<ArrayRange(b,1);j++){\r
449          c[i][j] = 0;\r
450          for(k=0;k<ArrayRange(a,1);k++){\r
451            c[i][j] += (a[i][k]*b[k][j]);\r
452          }  \r
453       }\r
454    }\r
455   return(0);   \r
457 int MatrixScalarMul(double b,double & a[][]){\r
458    int i,j;\r
459    for(i =0;i<ArrayRange(a,0);i++){\r
460       for(j=0;j<ArrayRange(a,1);j++){\r
461          a[i][j] *= b;\r
462       }\r
463    }\r
464 }         \r
465 int MatrixInvert(double a[][],double & b[][]){\r
466    double d;\r
467    double temp1[4][4],temp2[4][4];\r
468    int i,j,i1,j1,k;\r
469    \r
470    if(ArrayRange(a,0) != ArrayRange(a,1)){\r
471       Print("Matrix Inverse attempted on non square matrix. Not implemented yet");\r
472       return(-1);\r
473    }\r
474    if(ArrayRange(a,1) > 3){\r
475       Print("Bugger off, not implemented yet");\r
476       return(-1);\r
477    }\r
478    d = determinant(a);\r
479    if(MathAbs(d)<0.000001){\r
480     Print("unstable matrix");\r
481     d = 1;\r
482    }\r
483    k = ArrayRange(a,0);\r
484    if(k==0){\r
485       b[0][0]=1./a[0][0];\r
486       return(0);\r
487    }\r
488    d = determinant(a);\r
489    for(i1=0;i1<k;i1++){\r
490       for(j1=0;j1<k;j1++){\r
491          temp1[i1][j1] = a[i1][j1];\r
492       }\r
493    }   \r
494    for(i=0;i<k;i++){\r
495       for(j=0;j<k;j++){\r
496          //copy the arry. There must be a better way!!\r
497          ArrayCopy(temp2,temp1);\r
498          for(j1=0;j1<k;j1++){\r
499             if(j1==j){\r
500                temp2[i][j1] = 1;\r
501             }else{\r
502                temp2[i][j1] = 0;\r
503             }\r
504          }   \r
505          b[i][j]=determinant_fixed(temp2,k)/d;\r
506       }\r
507    }\r
508 }   \r
509 int MatrixTranspose(double a[][],double & b[][]){\r
510    int i,j;\r
511    for(i=0;i<ArrayRange(a,0);i++){\r
512       for(j=0;j<ArrayRange(a,1);j++){\r
513          b[j][i] = a[i][j];\r
514       }\r
515    }\r
516    return(0);\r
517  }\r
518 int MatrixZero(double & a[][]){\r
519    int i,j; \r
520    for(i= 0;i<ArrayRange(a,0);i++){\r
521       for(j=0;j<ArrayRange(a,1);j++){\r
522          a[i][j]=0;\r
523       }\r
524    }\r
525    return(0);\r
526 \r
527 int MatrixEye(double & a[][]){\r
528    int i,j; \r
529    for(i= 0;i<ArrayRange(a,0);i++){\r
530       for(j=0;j<ArrayRange(a,1);j++){\r
531          a[i][j]=0;\r
532          if(i==j)a[i][j]=1;\r
533       }\r
534    }\r
535    return(0);\r
536 \r
537 int MatrixPrint(double a[][]){\r
538    int i,j; \r
539    for(i= 0;i<ArrayRange(a,0);i++){\r
540       for(j=0;j<ArrayRange(a,1);j++){\r
541       Print(i," ,",j," ,",a[i][j]);\r
542       }\r
543    }\r
544    return(0);\r
545 }                                     \r
546 //+------------------------------------------------------------------+\r