示例#1
0
文件: private.c 项目: cran/scs
static scs_int factorize(const ScsMatrix *A, const ScsSettings *stgs,
                         ScsLinSysWork *p) {
  scs_float *info;
  scs_int *Pinv, amd_status, ldl_status;
  cs *C, *K = form_kkt(A, stgs);
  if (!K) {
    return -1;
  }
  amd_status = _ldl_init(K, p->P, &info);
  if (amd_status < 0) {
    return (amd_status);
  }
#if EXTRA_VERBOSE > 0
  if (stgs->verbose) {
    scs_printf("Matrix factorization info:\n");
#ifdef DLONG
    amd_l_info(info);
#else
    amd_info(info);
#endif
  }
#endif
  Pinv = SCS(cs_pinv)(p->P, A->n + A->m);
  C = SCS(cs_symperm)(K, Pinv, 1);
  ldl_status = _ldl_factor(C, SCS_NULL, SCS_NULL, &p->L, &p->D);
  SCS(cs_spfree)(C);
  SCS(cs_spfree)(K);
  scs_free(Pinv);
  scs_free(info);
  return (ldl_status);
}
示例#2
0
文件: private.c 项目: zhangrj7/scs
scs_int factorize(const AMatrix * A, const Settings * stgs, Priv * p) {
	scs_float *info;
	scs_int *Pinv, amd_status, ldl_status;
	cs *C, *K = formKKT(A, stgs);
	if (!K) {
		return -1;
	}
	amd_status = LDLInit(K, p->P, &info);
	if (amd_status < 0)
		return (amd_status);
#if EXTRAVERBOSE > 0
	if(stgs->verbose) {
		scs_printf("Matrix factorization info:\n");
#ifdef DLONG
		amd_l_info(info);
#else
		amd_info(info);
#endif
	}
#endif
	Pinv = cs_pinv(p->P, A->n + A->m);
	C = cs_symperm(K, Pinv, 1);
	ldl_status = LDLFactor(C, NULL, NULL, &p->L, &p->D);
	cs_spfree(C);
	cs_spfree(K);
	scs_free(Pinv);
	scs_free(info);
	return (ldl_status);
}
示例#3
0
int main (void)
#endif
{

    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

#ifdef USE_AMD
    double Info [AMD_INFO] ;
#endif
    double r, rnorm, flops, maxrnorm = 0. ;
    double *Ax, *Lx, *B, *D, *X, *Y ;
    LDL_int matrix, *Ai, *Ap, *Li, *Lp, *P, *Pinv, *Perm, *PermInv, n, i, j, p,
	nz, *Flag, *Pattern, *Lnz, *Parent, trial, lnz, d, jumbled ;
    FILE *f ;
    char s [LEN] ;

    /* ---------------------------------------------------------------------- */
    /* check the error-checking routines with null matrices */
    /* ---------------------------------------------------------------------- */

    i = 1 ;
    n = -1 ;
    if (LDL_valid_perm (n, (LDL_int *) NULL, &i)
	|| !LDL_valid_perm (0, (LDL_int *) NULL, &i)
	|| LDL_valid_matrix (n, (LDL_int *) NULL, (LDL_int *) NULL)
	|| LDL_valid_matrix (0, &i, &i))
    {
	printf (PROGRAM ": ldl error-checking routine failed\n") ;
	EXIT_ERROR ;
    }

    /* ---------------------------------------------------------------------- */
    /* read in a factorize a set of matrices */
    /* ---------------------------------------------------------------------- */

    for (matrix = 1 ; matrix <= NMATRICES ; matrix++)
    {

	/* ------------------------------------------------------------------ */
	/* read in the matrix and the permutation */
	/* ------------------------------------------------------------------ */

	sprintf (s, "../Matrix/A%02d", (int) matrix) ;
	if ((f = fopen (s, "r")) == (FILE *) NULL)
	{
	    printf (PROGRAM ": could not open file: %s\n", s) ;
	    EXIT_ERROR ;
	}
	fgets (s, LEN, f) ;
	printf ("\n\n--------------------------------------------------------");
	printf ("\nInput matrix: %s", s) ;
	printf ("--------------------------------------------------------\n\n");
	fscanf (f, LDL_ID " " LDL_ID, &n, &jumbled) ;
	n = (n < 0) ? (0) : (n) ;
	ALLOC_MEMORY (P, LDL_int, n) ;
	ALLOC_MEMORY (Ap, LDL_int, n+1) ;
	for (j = 0 ; j <= n ; j++)
	{
	    fscanf (f, LDL_ID, &Ap [j]) ;
	}
	nz = Ap [n] ;
	ALLOC_MEMORY (Ai, LDL_int, nz) ;
	ALLOC_MEMORY (Ax, double, nz) ;
	for (p = 0 ; p < nz ; p++)
	{
	    fscanf (f, LDL_ID , &Ai [p]) ;
	}
	for (p = 0 ; p < nz ; p++)
	{
	    fscanf (f, "%lg", &Ax [p]) ;
	}
	for (j = 0 ; j < n  ; j++)
	{
	    fscanf (f, LDL_ID , &P  [j]) ;
	}
	fclose (f) ;

	/* ------------------------------------------------------------------ */
	/* check the matrix A and the permutation P */
	/* ------------------------------------------------------------------ */

	ALLOC_MEMORY (Flag, LDL_int, n) ;

	/* To test the error-checking routines, some of the input matrices
	 * are not valid.  So this error is expected to occur. */
	if (!LDL_valid_matrix (n, Ap, Ai) || !LDL_valid_perm (n, P, Flag))
	{
	    printf (PROGRAM ": invalid matrix and/or permutation\n") ;
	    FREE_MEMORY (P, LDL_int) ;
	    FREE_MEMORY (Ap, LDL_int) ;
	    FREE_MEMORY (Ai, LDL_int) ;
	    FREE_MEMORY (Ax, double) ;
	    FREE_MEMORY (Flag, LDL_int) ;
	    continue ;
	}

	/* ------------------------------------------------------------------ */
	/* get the AMD permutation, if available */
	/* ------------------------------------------------------------------ */

#ifdef USE_AMD

	/* recompute the permutation with AMD */
	/* Assume that AMD produces a valid permutation P. */

#ifdef LDL_LONG

	if (amd_l_order (n, Ap, Ai, P, (double *) NULL, Info) < AMD_OK)
	{
	    printf (PROGRAM ": call to AMD failed\n") ;
	    EXIT_ERROR ;
	}
	amd_l_control ((double *) NULL) ;
	amd_l_info (Info) ;

#else

	if (amd_order (n, Ap, Ai, P, (double *) NULL, Info) < AMD_OK)
	{
	    printf (PROGRAM ": call to AMD failed\n") ;
	    EXIT_ERROR ;
	}
	amd_control ((double *) NULL) ;
	amd_info (Info) ;

#endif
#endif

	/* ------------------------------------------------------------------ */
	/* allocate workspace and the first part of LDL factorization */
	/* ------------------------------------------------------------------ */

	ALLOC_MEMORY (Pinv, LDL_int, n) ;
	ALLOC_MEMORY (Y, double, n) ;
	ALLOC_MEMORY (Pattern, LDL_int, n) ;
	ALLOC_MEMORY (Lnz, LDL_int, n) ;
	ALLOC_MEMORY (Lp, LDL_int, n+1) ;
	ALLOC_MEMORY (Parent, LDL_int, n) ;
	ALLOC_MEMORY (D, double, n) ;
	ALLOC_MEMORY (B, double, n) ;
	ALLOC_MEMORY (X, double, n) ;

	/* ------------------------------------------------------------------ */
	/* factorize twice, with and without permutation */
	/* ------------------------------------------------------------------ */

	for (trial = 1 ; trial <= 2 ; trial++)
	{

	    if (trial == 1)
	    {
		printf ("Factorize PAP'=LDL' and solve Ax=b\n") ;
		Perm = P ;
		PermInv = Pinv ;
	    }
	    else
	    {
		printf ("Factorize A=LDL' and solve Ax=b\n") ;
		Perm = (LDL_int *) NULL ;
		PermInv = (LDL_int *) NULL ;
	    }

	    /* -------------------------------------------------------------- */
	    /* symbolic factorization to get Lp, Parent, Lnz, and Pinv */
	    /* -------------------------------------------------------------- */

	    LDL_symbolic (n, Ap, Ai, Lp, Parent, Lnz, Flag, Perm, PermInv) ;
	    lnz = Lp [n] ;

	    /* find # of nonzeros in L, and flop count for LDL_numeric */
	    flops = 0 ;
	    for (j = 0 ; j < n ; j++)
	    {
		flops += ((double) Lnz [j]) * (Lnz [j] + 2) ;
	    }
	    printf ("Nz in L: "LDL_ID"  Flop count: %g\n", lnz, flops) ;

	    /* -------------------------------------------------------------- */
	    /* allocate remainder of L, of size lnz */
	    /* -------------------------------------------------------------- */

	    ALLOC_MEMORY (Li, LDL_int, lnz) ;
	    ALLOC_MEMORY (Lx, double, lnz) ;

	    /* -------------------------------------------------------------- */
	    /* numeric factorization to get Li, Lx, and D */
	    /* -------------------------------------------------------------- */

	    d = LDL_numeric (n, Ap, Ai, Ax, Lp, Parent, Lnz, Li, Lx, D,
		Y, Flag, Pattern, Perm, PermInv) ;

	    /* -------------------------------------------------------------- */
	    /* solve, or report singular case */
	    /* -------------------------------------------------------------- */

	    if (d != n)
	    {
		printf ("Ax=b not solved since D("LDL_ID","LDL_ID") is zero.\n", d, d) ;
	    }
	    else
	    {
		/* construct the right-hand-side, B */
		for (i = 0 ; i < n ; i++)
		{
		    B [i] = 1 + ((double) i) / 100 ;
		}

		/* solve Ax=b */
		if (trial == 1)
		{
		    /* the factorization is LDL' = PAP' */
		    LDL_perm (n, Y, B, P) ;			/* y = Pb */
		    LDL_lsolve (n, Y, Lp, Li, Lx) ;		/* y = L\y */
		    LDL_dsolve (n, Y, D) ;			/* y = D\y */
		    LDL_ltsolve (n, Y, Lp, Li, Lx) ;		/* y = L'\y */
		    LDL_permt (n, X, Y, P) ;			/* x = P'y */
		}
		else
		{
		    /* the factorization is LDL' = A */
		    for (i = 0 ; i < n ; i++)			/* x = b */
		    {
			X [i] = B [i] ;
		    }
		    LDL_lsolve (n, X, Lp, Li, Lx) ;		/* x = L\x */
		    LDL_dsolve (n, X, D) ;			/* x = D\x */
		    LDL_ltsolve (n, X, Lp, Li, Lx) ;		/* x = L'\x */
		}

		/* compute the residual y = Ax-b */
		/* note that this code can tolerate a jumbled matrix */
		for (i = 0 ; i < n ; i++)
		{
		    Y [i] = -B [i] ;
		}
		for (j = 0 ; j < n ; j++)
		{
		    for (p = Ap [j] ; p < Ap [j+1] ; p++)
		    {
			Y [Ai [p]] += Ax [p] * X [j] ;
		    }
		}
		/* rnorm = norm (y, inf) */
		rnorm = 0 ;
		for (i = 0 ; i < n ; i++)
		{
		    r = (Y [i] > 0) ? (Y [i]) : (-Y [i]) ;
		    rnorm = (r > rnorm) ? (r) : (rnorm) ;
		}
		maxrnorm = (rnorm > maxrnorm) ? (rnorm) : (maxrnorm) ;
		printf ("relative maxnorm of residual: %g\n", rnorm) ;
	    }

	    /* -------------------------------------------------------------- */
	    /* free the size-lnz part of L */
	    /* -------------------------------------------------------------- */

	    FREE_MEMORY (Li, LDL_int) ;
	    FREE_MEMORY (Lx, double) ;

	}

	/* free everything */
	FREE_MEMORY (P, LDL_int) ;
	FREE_MEMORY (Ap, LDL_int) ;
	FREE_MEMORY (Ai, LDL_int) ;
	FREE_MEMORY (Ax, double) ;
	FREE_MEMORY (Pinv, LDL_int) ;
	FREE_MEMORY (Y, double) ;
	FREE_MEMORY (Flag, LDL_int) ;
	FREE_MEMORY (Pattern, LDL_int) ;
	FREE_MEMORY (Lnz, LDL_int) ;
	FREE_MEMORY (Lp, LDL_int) ;
	FREE_MEMORY (Parent, LDL_int) ;
	FREE_MEMORY (D, double) ;
	FREE_MEMORY (B, double) ;
	FREE_MEMORY (X, double) ;
    }

    printf ("\nLargest residual during all tests: %g\n", maxrnorm) ;
    if (maxrnorm < 1e-8)
    {
	printf ("\n" PROGRAM ": all tests passed\n") ;
	EXIT_OK ;
    }
    else
    {
	printf ("\n" PROGRAM ": one more tests failed (residual too high)\n") ;
	EXIT_ERROR ;
    }
}
示例#4
0
void mexFunction
(
    int	nargout,
    mxArray *pargout [ ],
    int	nargin,
    const mxArray *pargin [ ]
)
{
    UF_long i, m, n, *Ap, *Ai, *P, nc, result, spumoni, full ;
    double *Pout, *InfoOut, Control [AMD_CONTROL], Info [AMD_INFO], *ControlIn ;
    mxArray *A ;

    /* --------------------------------------------------------------------- */
    /* get control parameters */
    /* --------------------------------------------------------------------- */

    amd_malloc = mxMalloc ;
    amd_free = mxFree ;
    amd_calloc = mxCalloc ;
    amd_realloc = mxRealloc ;
    amd_printf = mexPrintf ;

    spumoni = 0 ;
    if (nargin == 0)
    {
	/* get the default control parameters, and return */
	pargout [0] = mxCreateDoubleMatrix (AMD_CONTROL, 1, mxREAL) ;
	amd_l_defaults (mxGetPr (pargout [0])) ;
	if (nargout == 0)
	{
	    amd_l_control (mxGetPr (pargout [0])) ;
	}
	return ;
    }

    amd_l_defaults (Control) ;
    if (nargin > 1)
    {
	ControlIn = mxGetPr (pargin [1]) ;
	nc = mxGetM (pargin [1]) * mxGetN (pargin [1]) ;
	Control [AMD_DENSE]
	    = (nc > 0) ? ControlIn [AMD_DENSE] : AMD_DEFAULT_DENSE ;
	Control [AMD_AGGRESSIVE]
	    = (nc > 1) ? ControlIn [AMD_AGGRESSIVE] : AMD_DEFAULT_AGGRESSIVE ;
	spumoni = (nc > 2) ? (ControlIn [2] != 0) : 0 ;
    }

    if (spumoni > 0)
    {
	amd_l_control (Control) ;
    }

    /* --------------------------------------------------------------------- */
    /* get inputs */
    /* --------------------------------------------------------------------- */

    if (nargout > 2 || nargin > 2)
    {
	mexErrMsgTxt ("Usage: p = amd (A)\nor [p, Info] = amd (A, Control)") ;
    }

    A = (mxArray *) pargin [0] ;
    n = mxGetN (A) ;
    m = mxGetM (A) ;
    if (spumoni > 0)
    {
	mexPrintf ("    input matrix A is %d-by-%d\n", m, n) ;
    }
    if (mxGetNumberOfDimensions (A) != 2)
    {
	mexErrMsgTxt ("amd: A must be 2-dimensional") ;
    }
    if (m != n)
    {
    	mexErrMsgTxt ("amd: A must be square") ;
    }

    /* --------------------------------------------------------------------- */
    /* allocate workspace for output permutation */
    /* --------------------------------------------------------------------- */

    P = mxMalloc ((n+1) * sizeof (UF_long)) ;

    /* --------------------------------------------------------------------- */
    /* if A is full, convert to a sparse matrix */
    /* --------------------------------------------------------------------- */

    full = !mxIsSparse (A) ;
    if (full)
    {
	if (spumoni > 0)
	{
	    mexPrintf (
	    "    input matrix A is full (sparse copy of A will be created)\n");
	}
	mexCallMATLAB (1, &A, 1, (mxArray **) pargin, "sparse") ;
    }
    Ap = (UF_long *) mxGetJc (A) ;
    Ai = (UF_long *) mxGetIr (A) ;
    if (spumoni > 0)
    {
	mexPrintf ("    input matrix A has %d nonzero entries\n", Ap [n]) ;
    }

    /* --------------------------------------------------------------------- */
    /* order the matrix */
    /* --------------------------------------------------------------------- */

    result = amd_l_order (n, Ap, Ai, P, Control, Info) ;

    /* --------------------------------------------------------------------- */
    /* if A is full, free the sparse copy of A */
    /* --------------------------------------------------------------------- */

    if (full)
    {
	mxDestroyArray (A) ;
    }

    /* --------------------------------------------------------------------- */
    /* print results (including return value) */
    /* --------------------------------------------------------------------- */

    if (spumoni > 0)
    {
	amd_l_info (Info) ;
    }

    /* --------------------------------------------------------------------- */
    /* check error conditions */
    /* --------------------------------------------------------------------- */

    if (result == AMD_OUT_OF_MEMORY)
    {
	mexErrMsgTxt ("amd: out of memory") ;
    }
    else if (result == AMD_INVALID)
    {
	mexErrMsgTxt ("amd: input matrix A is corrupted") ;
    }

    /* --------------------------------------------------------------------- */
    /* copy the outputs to MATLAB */
    /* --------------------------------------------------------------------- */

    /* output permutation, P */
    pargout [0] = mxCreateDoubleMatrix (1, n, mxREAL) ;
    Pout = mxGetPr (pargout [0])  ;
    for (i = 0 ; i < n ; i++)
    {
	Pout [i] = P [i] + 1 ;	    /* change to 1-based indexing for MATLAB */
    }
    mxFree (P) ;

    /* Info */
    if (nargout > 1)
    {
	pargout [1] = mxCreateDoubleMatrix (AMD_INFO, 1, mxREAL) ;
	InfoOut = mxGetPr (pargout [1]) ;
	for (i = 0 ; i < AMD_INFO ; i++)
	{
	    InfoOut [i] = Info [i] ;
	}
    }
}
示例#5
0
文件: amd_l_demo.c 项目: GHilmarG/Ua
int main (void)
{
    /* The symmetric can_24 Harwell/Boeing matrix, including upper and lower
     * triangular parts, and the diagonal entries.  Note that this matrix is
     * 0-based, with row and column indices in the range 0 to n-1. */
    Long n = 24, nz,
    Ap [ ] = { 0, 9, 15, 21, 27, 33, 39, 48, 57, 61, 70, 76, 82, 88, 94, 100,
	106, 110, 119, 128, 137, 143, 152, 156, 160 },
    Ai [ ] = {
	/* column  0: */    0, 5, 6, 12, 13, 17, 18, 19, 21,
	/* column  1: */    1, 8, 9, 13, 14, 17,
	/* column  2: */    2, 6, 11, 20, 21, 22,
	/* column  3: */    3, 7, 10, 15, 18, 19,
	/* column  4: */    4, 7, 9, 14, 15, 16,
	/* column  5: */    0, 5, 6, 12, 13, 17,
	/* column  6: */    0, 2, 5, 6, 11, 12, 19, 21, 23,
	/* column  7: */    3, 4, 7, 9, 14, 15, 16, 17, 18,
	/* column  8: */    1, 8, 9, 14,
	/* column  9: */    1, 4, 7, 8, 9, 13, 14, 17, 18,
	/* column 10: */    3, 10, 18, 19, 20, 21,
	/* column 11: */    2, 6, 11, 12, 21, 23,
	/* column 12: */    0, 5, 6, 11, 12, 23,
	/* column 13: */    0, 1, 5, 9, 13, 17,
	/* column 14: */    1, 4, 7, 8, 9, 14,
	/* column 15: */    3, 4, 7, 15, 16, 18,
	/* column 16: */    4, 7, 15, 16,
	/* column 17: */    0, 1, 5, 7, 9, 13, 17, 18, 19,
	/* column 18: */    0, 3, 7, 9, 10, 15, 17, 18, 19,
	/* column 19: */    0, 3, 6, 10, 17, 18, 19, 20, 21,
	/* column 20: */    2, 10, 19, 20, 21, 22,
	/* column 21: */    0, 2, 6, 10, 11, 19, 20, 21, 22,
	/* column 22: */    2, 20, 21, 22,
	/* column 23: */    6, 11, 12, 23 } ;

    Long P [24], Pinv [24], i, j, k, jnew, p, inew, result ;
    double Control [AMD_CONTROL], Info [AMD_INFO] ;
    char A [24][24] ;

    /* here is an example of how to use AMD_VERSION.  This code will work in
     * any version of AMD. */
#if defined(AMD_VERSION) && (AMD_VERSION >= AMD_VERSION_CODE(1,2))
    printf ("AMD version %d.%d.%d, date: %s\n",
        AMD_MAIN_VERSION, AMD_SUB_VERSION, AMD_SUBSUB_VERSION, AMD_DATE) ;
#else
    printf ("AMD version: 1.1 or earlier\n") ;
#endif

    printf ("AMD demo, with the 24-by-24 Harwell/Boeing matrix, can_24:\n") ;

    /* get the default parameters, and print them */
    amd_l_defaults (Control) ;
    amd_l_control  (Control) ;

    /* print the input matrix */
    nz = Ap [n] ;
    printf ("\nInput matrix:  %ld-by-%ld, with %ld entries.\n"
	   "   Note that for a symmetric matrix such as this one, only the\n"
	   "   strictly lower or upper triangular parts would need to be\n"
	   "   passed to AMD, since AMD computes the ordering of A+A'.  The\n"
	   "   diagonal entries are also not needed, since AMD ignores them.\n"
	   , n, n, nz) ;
    for (j = 0 ; j < n ; j++)
    {
	printf ("\nColumn: %ld, number of entries: %ld, with row indices in"
		" Ai [%ld ... %ld]:\n    row indices:",
		j, Ap [j+1] - Ap [j], Ap [j], Ap [j+1]-1) ;
	for (p = Ap [j] ; p < Ap [j+1] ; p++)
	{
	    i = Ai [p] ;
	    printf (" %ld", i) ;
	}
	printf ("\n") ;
    }

    /* print a character plot of the input matrix.  This is only reasonable
     * because the matrix is small. */
    printf ("\nPlot of input matrix pattern:\n") ;
    for (j = 0 ; j < n ; j++)
    {
	for (i = 0 ; i < n ; i++) A [i][j] = '.' ;
	for (p = Ap [j] ; p < Ap [j+1] ; p++)
	{
	    i = Ai [p] ;
	    A [i][j] = 'X' ;
	}
    }
    printf ("    ") ;
    for (j = 0 ; j < n ; j++) printf (" %1ld", j % 10) ;
    printf ("\n") ;
    for (i = 0 ; i < n ; i++)
    {
	printf ("%2ld: ", i) ;
	for (j = 0 ; j < n ; j++)
	{
	    printf (" %c", A [i][j]) ;
	}
	printf ("\n") ;
    }

    /* order the matrix */
    result = amd_l_order (n, Ap, Ai, P, Control, Info) ;
    printf ("return value from amd_l_order: %ld (should be %d)\n",
	result, AMD_OK) ;

    /* print the statistics */
    amd_l_info (Info) ;

    if (result != AMD_OK)
    {
	printf ("AMD failed\n") ;
	exit (1) ;
    }

    /* print the permutation vector, P, and compute the inverse permutation */
    printf ("Permutation vector:\n") ;
    for (k = 0 ; k < n ; k++)
    {
	/* row/column j is the kth row/column in the permuted matrix */
	j = P [k] ;
	Pinv [j] = k ;
	printf (" %2ld", j) ;
    }
    printf ("\n\n") ;

    printf ("Inverse permutation vector:\n") ;
    for (j = 0 ; j < n ; j++)
    {
	k = Pinv [j] ;
	printf (" %2ld", k) ;
    }
    printf ("\n\n") ;

    /* print a character plot of the permuted matrix. */
    printf ("\nPlot of permuted matrix pattern:\n") ;
    for (jnew = 0 ; jnew < n ; jnew++)
    {
	j = P [jnew] ;
	for (inew = 0 ; inew < n ; inew++) A [inew][jnew] = '.' ;
	for (p = Ap [j] ; p < Ap [j+1] ; p++)
	{
	    inew = Pinv [Ai [p]] ;
	    A [inew][jnew] = 'X' ;
	}
    }
    printf ("    ") ;
    for (j = 0 ; j < n ; j++) printf (" %1ld", j % 10) ;
    printf ("\n") ;
    for (i = 0 ; i < n ; i++)
    {
	printf ("%2ld: ", i) ;
	for (j = 0 ; j < n ; j++)
	{
	    printf (" %c", A [i][j]) ;
	}
	printf ("\n") ;
    }

    return (0) ;
}