00001 #include <math.h>
00002 #include <stdlib.h>
00003 #include <check.h>
00004
00005 #include <stdio.h>
00006 #include "check_utils.h"
00007
00008 #include <linear_algebra.h>
00009
00010 #define LINALG_TOL 1e-10
00011 #define LINALG_NUM 22
00012 #define MATRIX_MIN -1e3
00013 #define MATRIX_MAX 1e3
00014 #define mrand frand(MATRIX_MIN, MATRIX_MAX)
00015 #define MSIZE_MAX 64
00016
00017
00018
00019 START_TEST(test_matrix_inverse_2x2) {
00020 u32 i, j, t;
00021 double A[4];
00022 double B[4];
00023 double I[4];
00024
00025 seed_rng();
00026
00027 for (t = 0; t < LINALG_NUM; t++) {
00028 do {
00029 for (i = 0; i < 2; i++)
00030 for (j = 0; j < 2; j++)
00031 A[2*i + j] = mrand;
00032 } while (matrix_inverse(2, A, B) < 0);
00033 matrix_multiply(2, 2, 2, A, B, I);
00034 fail_unless(fabs(I[0] - 1) < LINALG_TOL,
00035 "Matrix differs from identity: %lf", I[0]);
00036 fail_unless(fabs(I[3] - 1) < LINALG_TOL,
00037 "Matrix differs from identity: %lf", I[3]);
00038 }
00039 for (i = 0; i < 2; i++)
00040 for (j = 0; j < 2; j++)
00041 if (j == 0)
00042 A[2*i + j] = 22;
00043 else
00044 A[2*i + j] = 1;
00045 s32 mi = matrix_inverse(2, A, B);
00046 fail_unless(mi < 0, "Singular matrix not detected.");
00047 }
00048 END_TEST
00049
00050 START_TEST(test_matrix_inverse_3x3) {
00051 u32 i, j, t;
00052 double A[9];
00053 double B[9];
00054 double I[9];
00055
00056 seed_rng();
00057
00058 for (t = 0; t < LINALG_NUM; t++) {
00059 do {
00060 for (i = 0; i < 3; i++)
00061 for (j = 0; j < 3; j++)
00062 A[3*i + j] = mrand;
00063 } while (matrix_inverse(3, A, B) < 0);
00064 matrix_multiply(3, 3, 3, A, B, I);
00065 fail_unless(fabs(I[0] - 1) < LINALG_TOL,
00066 "Matrix differs from identity: %lf", I[0]);
00067 fail_unless(fabs(I[4] - 1) < LINALG_TOL,
00068 "Matrix differs from identity: %lf", I[4]);
00069 fail_unless(fabs(I[8] - 1) < LINALG_TOL,
00070 "Matrix differs from identity: %lf", I[8]);
00071 }
00072 for (i = 0; i < 3; i++)
00073 for (j = 0; j < 3; j++)
00074 if (j == 0)
00075 A[3*i + j] = 33;
00076 else
00077 A[3*i + j] = 1;
00078 s32 mi = matrix_inverse(3, A, B);
00079 fail_unless(mi < 0, "Singular matrix not detected.");
00080 }
00081 END_TEST
00082
00083 START_TEST(test_matrix_inverse_4x4) {
00084 u32 i, j, t;
00085 double A[16];
00086 double B[16];
00087 double I[16];
00088
00089 seed_rng();
00090
00091 for (t = 0; t < LINALG_NUM; t++) {
00092 do {
00093 for (i = 0; i < 4; i++)
00094 for (j = 0; j < 4; j++)
00095 A[4*i + j] = mrand;
00096 } while (matrix_inverse(4, A, B) < 0);
00097 matrix_multiply(4, 4, 4, A, B, I);
00098 fail_unless(fabs(I[0] - 1) < LINALG_TOL,
00099 "Matrix differs from identity: %lf", I[0]);
00100 fail_unless(fabs(I[5] - 1) < LINALG_TOL,
00101 "Matrix differs from identity: %lf", I[5]);
00102 fail_unless(fabs(I[10] - 1) < LINALG_TOL,
00103 "Matrix differs from identity: %lf", I[10]);
00104 fail_unless(fabs(I[15] - 1) < LINALG_TOL,
00105 "Matrix differs from identity: %lf", I[15]);
00106 }
00107 for (i = 0; i < 4; i++)
00108 for (j = 0; j < 4; j++)
00109 if (j == 0)
00110 A[4*i + j] = 44;
00111 else
00112 A[4*i + j] = 1;
00113 s32 mi = matrix_inverse(4, A, B);
00114 fail_unless(mi < 0, "Singular matrix not detected.");
00115 }
00116 END_TEST
00117
00118 START_TEST(test_matrix_inverse_5x5) {
00119 u32 i, j, t;
00120 double A[25];
00121 double B[25];
00122 double I[25];
00123
00124 seed_rng();
00125
00126 for (t = 0; t < LINALG_NUM; t++) {
00127 do {
00128 for (i = 0; i < 5; i++)
00129 for (j = 0; j < 5; j++)
00130 A[5*i + j] = mrand;
00131 } while (matrix_inverse(5, A, B) < 0);
00132 matrix_multiply(5, 5, 5, A, B, I);
00133 fail_unless(fabs(I[0] - 1) < LINALG_TOL,
00134 "Matrix differs from identity: %lf", I[0]);
00135 fail_unless(fabs(I[6] - 1) < LINALG_TOL,
00136 "Matrix differs from identity: %lf", I[6]);
00137 fail_unless(fabs(I[12] - 1) < LINALG_TOL,
00138 "Matrix differs from identity: %lf", I[12]);
00139 fail_unless(fabs(I[18] - 1) < LINALG_TOL,
00140 "Matrix differs from identity: %lf", I[18]);
00141 fail_unless(fabs(I[24] - 1) < LINALG_TOL,
00142 "Matrix differs from identity: %lf", I[24]);
00143 }
00144 for (i = 0; i < 5; i++)
00145 for (j = 0; j < 5; j++)
00146 if (j == 0)
00147 A[5*i + j] = 55;
00148 else
00149 A[5*i + j] = 1;
00150 s32 mi = matrix_inverse(5, A, B);
00151 fail_unless(mi < 0, "Singular matrix not detected.");
00152 }
00153 END_TEST
00154
00155 START_TEST(test_matrix_eye)
00156 {
00157 double M[10][10];
00158
00159 matrix_eye(10, (double *)M);
00160
00161 for (u32 i=0; i<10; i++) {
00162 for (u32 j=0; j<10; j++) {
00163 if (i == j) {
00164 fail_unless(M[i][j] == 1, "Identity diagonal element != 1");
00165 } else {
00166 fail_unless(M[i][j] == 0, "Identity off-diagonal element != 0");
00167 }
00168 }
00169 }
00170 }
00171 END_TEST
00172
00173 START_TEST(test_matrix_triu)
00174 {
00175 double M[4][4] = {
00176 { 1, 2, 3, 4},
00177 { 5, 6, 7, 8},
00178 { 9, 10, 11, 12},
00179 {13, 14, 15, 16}
00180 };
00181
00182 double M_[4][4] = {
00183 {1, 2, 3, 4},
00184 {0, 6, 7, 8},
00185 {0, 0, 11, 12},
00186 {0, 0, 0, 16}
00187 };
00188
00189 matrix_triu(4, (double *)M);
00190
00191 for (u32 i=0; i<4; i++) {
00192 for (u32 j=0; j<4; j++) {
00193 fail_unless(M[i][j] == M_[i][j], "triu result != test matrix");
00194 }
00195 }
00196 }
00197 END_TEST
00198
00199 START_TEST(test_matrix_udu_1)
00200 {
00201 double M[4][4] = {
00202 {100, 145, 121, 16},
00203 {145, 221, 183, 24},
00204 {121, 183, 199, 28},
00205 { 16, 24, 28, 4}
00206 };
00207
00208 double U[4][4] = {{0}};
00209 double D[4] = {0};
00210
00211 matrix_udu(4, (double *)M, (double *)U, D);
00212
00213 double U_[4][4] = {
00214 {1, 2, 3, 4},
00215 {0, 1, 5, 6},
00216 {0, 0, 1, 7},
00217 {0, 0, 0, 1}
00218 };
00219
00220 double D_[4] = {1, 2, 3, 4};
00221
00222 for (u32 i=0; i<4; i++) {
00223 for (u32 j=0; j<4; j++) {
00224 fail_unless(U[i][j] == U_[i][j], "U result != test matrix");
00225 }
00226 }
00227 for (u32 i=0; i<4; i++) {
00228 fail_unless(D[i] == D_[i], "D result != test D");
00229 }
00230 }
00231 END_TEST
00232
00233 START_TEST(test_matrix_udu_2)
00234 {
00235 u32 n = sizerand(MSIZE_MAX);
00236 double M[n][n];
00237 double M_orig[n][n];
00238
00239 for (u32 i=0; i<n; i++) {
00240 for (u32 j=0; j<=i; j++) {
00241 M[i][j] = M[j][i] = mrand;
00242 }
00243 }
00244 memcpy(M_orig, M, n * n * sizeof(double));
00245
00246 double U[n][n];
00247 memset(U, 0, n * n * sizeof(double));
00248 double D[n];
00249 memset(D, 0, n * sizeof(double));
00250
00251 matrix_udu(n, (double *)M, (double *)U, D);
00252
00253
00254 for (u32 i=0; i<n; i++) {
00255 for (u32 j=0; j<n; j++) {
00256 if (i == j) {
00257 fail_unless(fabs(U[i][j] - 1) < LINALG_TOL,
00258 "U diagonal element != 1 (was %f)", M[i][j]);
00259 }
00260 if (i > j) {
00261 fail_unless(fabs(U[i][j]) < LINALG_TOL, "U lower triangle element != 0");
00262 }
00263 }
00264 }
00265
00266
00267 double M_[n][n];
00268 memset(M_, 0, n * n * sizeof(double));
00269 matrix_reconstruct_udu(n, (double *)U, D, (double *)M_);
00270
00271 for (u32 i=0; i<n; i++) {
00272 for (u32 j=0; j<n; j++) {
00273 fail_unless(fabs(M_orig[i][j] - M_[i][j]) < LINALG_TOL * MATRIX_MAX,
00274 "reconstructed result != original matrix, delta[%d][%d] = %f",
00275 i, j, fabs(M_orig[i][j] - M_[i][j]));
00276 }
00277 }
00278 }
00279 END_TEST
00280
00281 START_TEST(test_matrix_udu_3)
00282 {
00283 double M[3][3] = {
00284 {36, 49, 9},
00285 {49, 77, 15},
00286 { 9, 15, 3},
00287 };
00288
00289 double U[3][3] = {{0}};
00290 double D[3] = {0};
00291
00292 matrix_udu(3, (double *)M, (double *)U, D);
00293
00294
00295 fail_unless(D[2] == M[2][2], "D[2] incorrect");
00296 fail_unless(U[2][1] == M[2][1] / D[2], "U[2][1] incorrect");
00297 fail_unless(U[2][0] == M[2][0] / D[2], "U[2][0] incorrect");
00298 fail_unless(D[1] == (M[1][1] - U[2][1]*U[2][1]*D[2]), "D[1] incorrect");
00299 fail_unless(U[1][0] == ((M[1][0] - U[2][0]*U[2][1]*D[2])/D[1]),
00300 "U[1][0] incorrect");
00301 fail_unless(D[0] == (M[0][0] - U[1][0]*U[1][0]*D[1] - U[2][0]*U[2][0]*D[2]),
00302 "D[0] incorrect");
00303 }
00304 END_TEST
00305
00306 START_TEST(test_matrix_reconstruct_udu)
00307 {
00308 double U[4][4] = {
00309 {1, 2, 3, 4},
00310 {0, 1, 5, 6},
00311 {0, 0, 1, 7},
00312 {0, 0, 0, 1}
00313 };
00314
00315 double D[4] = {1, 2, 3, 4};
00316
00317 double M[4][4] = {{0}};
00318
00319 double M_[4][4] = {
00320 {100, 145, 121, 16},
00321 {145, 221, 183, 24},
00322 {121, 183, 199, 28},
00323 { 16, 24, 28, 4}
00324 };
00325
00326 matrix_reconstruct_udu(4, (double *)U, D, (double *)M);
00327
00328 for (u32 i=0; i<4; i++) {
00329 for (u32 j=0; j<4; j++) {
00330 fail_unless(M[i][j] == M_[i][j], "reconstructed result != test matrix");
00331 }
00332 }
00333 }
00334 END_TEST
00335
00336 START_TEST(test_matrix_add_sc) {
00337 u32 i, j, t;
00338
00339 seed_rng();
00340 for (t = 0; t < LINALG_NUM; t++) {
00341 u32 n = sizerand(MSIZE_MAX);
00342 u32 m = sizerand(MSIZE_MAX);
00343 double A[n*m];
00344 double B[m*n];
00345 for (i = 0; i < n; i++)
00346 for (j = 0; j < m; j++) {
00347 A[m*i + j] = mrand;
00348 }
00349 matrix_add_sc(n, m, A, A, -1, B);
00350 for (i = 0; i < n; i++)
00351 for (j = 0; j < m; j++)
00352 fail_unless(fabs(B[m*i + j]) < LINALG_TOL,
00353 "Matrix differs from zero: %lf", B[m*i + j]);
00354 }
00355 }
00356 END_TEST
00357
00358 START_TEST(test_matrix_copy) {
00359 u32 i, j, t;
00360 double tmp;
00361
00362 seed_rng();
00363 for (t = 0; t < LINALG_NUM; t++) {
00364 u32 n = sizerand(MSIZE_MAX);
00365 u32 m = sizerand(MSIZE_MAX);
00366 double A[n*m];
00367 double B[m*n];
00368 for (i = 0; i < n; i++)
00369 for (j = 0; j < m; j++)
00370 A[m*i + j] = mrand;
00371 matrix_copy(n, m, A, B);
00372 for (i = 0; i < n; i++)
00373 for (j = 0; j < m; j++) {
00374 tmp = fabs(B[m*i + j] - A[m*i + j]);
00375 fail_unless(tmp < LINALG_TOL,
00376 "Matrix differs from zero: %lf", tmp);
00377 }
00378 }
00379 }
00380 END_TEST
00381
00382 START_TEST(test_matrix_transpose) {
00383 u32 i, j, t;
00384
00385 seed_rng();
00386 for (t = 0; t < LINALG_NUM; t++) {
00387 u32 n = sizerand(MSIZE_MAX);
00388 u32 m = sizerand(MSIZE_MAX);
00389 double A[n*m];
00390 double B[m*n];
00391 double C[n*m];
00392 for (i = 0; i < n; i++)
00393 for (j = 0; j < m; j++)
00394 A[m*i + j] = mrand;
00395 matrix_transpose(n, m, A, B);
00396 matrix_transpose(m, n, B, C);
00397 for (i = 0; i < n; i++)
00398 for (j = 0; j < m; j++)
00399 fail_unless(fabs(A[m*i + j] - C[m*i + j]) < LINALG_TOL,
00400 "Matrix element differs from original: %lf, %lf",
00401 A[m*i + j], C[m*i + j]);
00402 }
00403 }
00404 END_TEST
00405
00406 START_TEST(test_vector_dot) {
00407 u32 i, t;
00408
00409 seed_rng();
00410 for (t = 0; t < LINALG_NUM; t++) {
00411 u32 n = sizerand(MSIZE_MAX);
00412 u32 mid;
00413 if (n % 2 == 0)
00414 mid = n / 2;
00415 else
00416 mid = (n - 1) / 2;
00417
00418 double A[n], B[n];
00419 for (i = 0; i < n; i++) {
00420 A[i] = mrand/1e20;
00421 if (i < mid)
00422 B[n-i-1] = -A[i];
00423 else
00424 B[n-i-1] = A[i];
00425 }
00426 double dot = vector_dot(n, A, B);
00427 if (n % 2 == 0)
00428 fail_unless(fabs(dot) < LINALG_TOL,
00429 "Dot product differs from zero: %lf", vector_dot(n, A, B));
00430 else
00431 fail_unless(fabs(dot - A[mid]*B[mid]) < LINALG_TOL,
00432 "Dot product differs from square of middle element "
00433 "%lf: %lf (%lf)",
00434 A[mid]*B[mid], dot, dot - A[mid]*B[mid]);
00435 }
00436 }
00437 END_TEST
00438
00439 START_TEST(test_vector_mean) {
00440 u32 i, t;
00441
00442 seed_rng();
00443 for (t = 0; t < LINALG_NUM; t++) {
00444 u32 n = sizerand(MSIZE_MAX);
00445 double A[n];
00446 double test = mrand/1e22;
00447 for (i = 0; i < n; i++)
00448 A[i] = test + i;
00449 double mean = vector_mean(n, A);
00450 double expect = test + (n - 1.0) / 2.0;
00451 fail_unless(fabs(mean - expect)
00452 < LINALG_TOL,
00453 "Mean differs from expected %lf: %lf (%lf)",
00454 expect, mean, fabs(mean - expect));
00455 }
00456 }
00457 END_TEST
00458
00459 START_TEST(test_vector_norm) {
00460 u32 i, t;
00461
00462 seed_rng();
00463 for (t = 0; t < LINALG_NUM; t++) {
00464 u32 n = sizerand(MSIZE_MAX);
00465 double test = mrand/1e22;
00466 double A[n];
00467 for (i = 0; i < n; i++)
00468 A[i] = test;
00469 fail_unless(fabs(vector_norm(n, A)*vector_norm(n, A) - n * test * test)
00470 < LINALG_TOL * vector_norm(n, A),
00471 "Norm differs from expected %lf: %lf (%lf)",
00472 n * test * test, vector_norm(n, A) * vector_norm(n, A),
00473 fabs(vector_norm(n, A) * vector_norm(n, A) - n * test * test));
00474 }
00475 }
00476 END_TEST
00477
00478 START_TEST(test_vector_normalize) {
00479 u32 i, t;
00480
00481 seed_rng();
00482 for (t = 0; t < LINALG_NUM; t++) {
00483 u32 n = sizerand(MSIZE_MAX);
00484 double A[n];
00485 for (i = 0; i < n; i++)
00486 A[i] = mrand;
00487 vector_normalize(n, A);
00488 double vnorm = vector_norm(n, A);
00489 fail_unless(fabs(vnorm - 1) < LINALG_TOL,
00490 "Norm differs from 1: %lf",
00491 vector_norm(n, A));
00492 }
00493 }
00494 END_TEST
00495
00496 START_TEST(test_vector_add_sc) {
00497 u32 i, t;
00498
00499 seed_rng();
00500 for (t = 0; t < LINALG_NUM; t++) {
00501 u32 n = sizerand(MSIZE_MAX);
00502 double A[n], B[n];
00503 for (i = 0; i < n; i++)
00504 A[i] = mrand;
00505 vector_add_sc(n, A, A, -1, B);
00506 for (i = 0; i < n; i++)
00507 fail_unless(fabs(B[i]) < LINALG_TOL,
00508 "Vector element differs from 0: %lf",
00509 B[i]);
00510 }
00511 }
00512 END_TEST
00513
00514 START_TEST(test_vector_add) {
00515 u32 i, t;
00516
00517 seed_rng();
00518 for (t = 0; t < LINALG_NUM; t++) {
00519 u32 n = sizerand(MSIZE_MAX);
00520 double A[n], B[n], C[n];
00521 for (i = 0; i < n; i++) {
00522 A[i] = mrand;
00523 B[i] = -A[i];
00524 }
00525 vector_add(n, A, B, C);
00526 for (i = 0; i < n; i++)
00527 fail_unless(fabs(C[i]) < LINALG_TOL,
00528 "Vector element differs from 0: %lf",
00529 C[i]);
00530 }
00531 }
00532 END_TEST
00533
00534 START_TEST(test_vector_subtract) {
00535 u32 i, t;
00536
00537 seed_rng();
00538 for (t = 0; t < LINALG_NUM; t++) {
00539 u32 n = sizerand(MSIZE_MAX);
00540 double A[n], B[n], C[n];
00541 for (i = 0; i < n; i++) {
00542 A[i] = mrand;
00543 B[i] = A[i];
00544 }
00545 vector_subtract(n, A, B, C);
00546 for (i = 0; i < n; i++)
00547 fail_unless(fabs(C[i]) < LINALG_TOL,
00548 "Vector element differs from 0: %lf",
00549 C[i]);
00550 }
00551 }
00552 END_TEST
00553
00554 START_TEST(test_vector_cross) {
00555 u32 i, t;
00556
00557 seed_rng();
00558 for (t = 0; t < LINALG_NUM; t++) {
00559 double A[3], B[3], C[3], D[3];
00560 for (i = 0; i < 3; i++) {
00561 A[i] = mrand;
00562 B[i] = A[i];
00563 }
00564 vector_cross(A, B, C);
00565 for (i = 0; i < 3; i++)
00566 fail_unless(fabs(C[i]) < LINALG_TOL,
00567 "Vector element differs from 0: %lf",
00568 C[i]);
00569 for (i = 0; i < 3; i++) {
00570 A[i] = mrand;
00571 B[i] = mrand;
00572 }
00573 vector_cross(A, B, C);
00574 for (i = 0; i < 3; i++) B[i] *= -1;
00575 vector_cross(B, A, D);
00576 for (i = 0; i < 3; i++)
00577 fail_unless(fabs(C[i] - D[i]) < LINALG_TOL,
00578 "Vector equality fails: %lf != %lf",
00579 C[i], D[i]);
00580 }
00581 }
00582 END_TEST
00583
00584 START_TEST(test_vector_three) {
00585 u32 i, t;
00586
00587 seed_rng();
00588 for (t = 0; t < LINALG_NUM; t++) {
00589 double A[3], B[3], C[3], tmp[3];
00590 double D, E, F, norm;
00591 for (i = 0; i < 3; i++) {
00592 A[i] = mrand / 1e20;
00593 B[i] = mrand / 1e20;
00594 C[i] = mrand / 1e20;
00595 }
00596
00597 vector_cross(A, B, tmp);
00598 D = vector_dot(3, C, tmp);
00599 vector_cross(B, C, tmp);
00600 E = vector_dot(3, A, tmp);
00601 vector_cross(C, A, tmp);
00602 F = vector_dot(3, B, tmp);
00603
00604 norm = (vector_norm(3, A) + vector_norm(3, B) + vector_norm(3, C))/3;
00605 fail_unless(fabs(E - D) < LINALG_TOL * norm,
00606 "Triple product failure between %lf and %lf",
00607 D, E);
00608 fail_unless(fabs(E - F) < LINALG_TOL * norm,
00609 "Triple product failure between %lf and %lf",
00610 E, F);
00611 fail_unless(fabs(F - D) < LINALG_TOL * norm,
00612 "Triple product failure between %lf and %lf",
00613 F, D);
00614 }
00615 }
00616 END_TEST
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669 Suite* linear_algebra_suite(void) {
00670 Suite *s = suite_create("Linear algebra");
00671
00672
00673 TCase *tc_core = tcase_create("Core");
00674 tcase_add_test(tc_core, test_matrix_eye);
00675 tcase_add_test(tc_core, test_matrix_triu);
00676 tcase_add_test(tc_core, test_matrix_reconstruct_udu);
00677 tcase_add_test(tc_core, test_matrix_udu_1);
00678 tcase_add_test(tc_core, test_matrix_udu_2);
00679 tcase_add_test(tc_core, test_matrix_udu_3);
00680 tcase_add_test(tc_core, test_matrix_add_sc);
00681 tcase_add_test(tc_core, test_matrix_copy);
00682 tcase_add_test(tc_core, test_matrix_transpose);
00683 tcase_add_test(tc_core, test_matrix_inverse_2x2);
00684 tcase_add_test(tc_core, test_matrix_inverse_3x3);
00685 tcase_add_test(tc_core, test_matrix_inverse_4x4);
00686 tcase_add_test(tc_core, test_matrix_inverse_5x5);
00687
00688 tcase_add_test(tc_core, test_vector_dot);
00689 tcase_add_test(tc_core, test_vector_mean);
00690 tcase_add_test(tc_core, test_vector_norm);
00691 tcase_add_test(tc_core, test_vector_normalize);
00692 tcase_add_test(tc_core, test_vector_add_sc);
00693 tcase_add_test(tc_core, test_vector_add);
00694 tcase_add_test(tc_core, test_vector_subtract);
00695 tcase_add_test(tc_core, test_vector_cross);
00696 tcase_add_test(tc_core, test_vector_three);
00697
00698
00699 suite_add_tcase(s, tc_core);
00700
00701 return s;
00702 }
00703