Exemplo n.º 1
0
void setgraphattributes(Agraph_t * g, char *argv[], int argc)
{
    int i;
    Agsym_t *a;

    for (i = 0; i < argc; i++) {
	if (!(a = agfindgraphattr(agroot(g), argv[i])))
	    a = agattr(agroot(g), AGRAPH, argv[i], "");
	agxset(g, a, argv[++i]);
    }
}
Exemplo n.º 2
0
static void dotLayout(Agraph_t * g)
{
    aspect_t aspect;
    aspect_t* asp;
    int maxphase = late_int(g, agfindgraphattr(g,"phase"), -1, 1);

    setEdgeType (g, ET_SPLINE);
    asp = setAspect (g, &aspect);

    dot_init_subg(g,g);
    dot_init_node_edge(g);

    do {
        dot_rank(g, asp);
	if (maxphase == 1) {
	    attach_phase_attrs (g, 1);
	    return;
	}
	if (aspect.badGraph) {
	    agerr(AGWARN, "dot does not support the aspect attribute for disconnected graphs or graphs with clusters\n");
	    asp = NULL;
	    aspect.nextIter = 0;
	}
        dot_mincross(g, (asp != NULL));
	if (maxphase == 2) {
	    attach_phase_attrs (g, 2);
	    return;
	}
        dot_position(g, asp);
	if (maxphase == 3) {
	    attach_phase_attrs (g, 2);  /* positions will be attached on output */
	    return;
	}
	aspect.nPasses--;
    } while (aspect.nextIter && aspect.nPasses);
    if (GD_flags(g) & NEW_RANK)
	removeFill (g);
    dot_sameports(g);
    dot_splines(g);
    if (mapbool(agget(g, "compound")))
	dot_compoundEdges(g);
}
Exemplo n.º 3
0
/* do_graph_label:
 * Set characteristics of graph label if it exists.
 * 
 */
void do_graph_label(graph_t * sg)
{
    char *str, *pos, *just;
    int pos_ix;

    /* it would be nice to allow multiple graph labels in the future */
    if ((str = agget(sg, "label")) && (*str != '\0')) {
	char pos_flag;
	pointf dimen;

	GD_has_labels(sg->root) |= GRAPH_LABEL;

	GD_label(sg) = make_label((void*)sg, str, (aghtmlstr(str) ? LT_HTML : LT_NONE),
	    late_double(sg, agfindgraphattr(sg, "fontsize"),
			DEFAULT_FONTSIZE, MIN_FONTSIZE),
	    late_nnstring(sg, agfindgraphattr(sg, "fontname"),
			DEFAULT_FONTNAME),
	    late_nnstring(sg, agfindgraphattr(sg, "fontcolor"),
			DEFAULT_COLOR));

	/* set label position */
	pos = agget(sg, "labelloc");
	if (sg != agroot(sg)) {
	    if (pos && (pos[0] == 'b'))
		pos_flag = LABEL_AT_BOTTOM;
	    else
		pos_flag = LABEL_AT_TOP;
	} else {
	    if (pos && (pos[0] == 't'))
		pos_flag = LABEL_AT_TOP;
	    else
		pos_flag = LABEL_AT_BOTTOM;
	}
	just = agget(sg, "labeljust");
	if (just) {
	    if (just[0] == 'l')
		pos_flag |= LABEL_AT_LEFT;
	    else if (just[0] == 'r')
		pos_flag |= LABEL_AT_RIGHT;
	}
	GD_label_pos(sg) = pos_flag;

	if (sg == agroot(sg))
	    return;

	/* Set border information for cluster labels to allow space
	 */
	dimen = GD_label(sg)->dimen;
	PAD(dimen);
	if (!GD_flip(agroot(sg))) {
	    if (GD_label_pos(sg) & LABEL_AT_TOP)
		pos_ix = TOP_IX;
	    else
		pos_ix = BOTTOM_IX;
	    GD_border(sg)[pos_ix] = dimen;
	} else {
	    /* when rotated, the labels will be restored to TOP or BOTTOM  */
	    if (GD_label_pos(sg) & LABEL_AT_TOP)
		pos_ix = RIGHT_IX;
	    else
		pos_ix = LEFT_IX;
	    GD_border(sg)[pos_ix].x = dimen.y;
	    GD_border(sg)[pos_ix].y = dimen.x;
	}
    }
}
Exemplo n.º 4
0
/*
	cgraph requires 

*/
void graph_init(graph_t * g, boolean use_rankdir)
{
    char *p;
    double xf;
    static char *rankname[] = { "local", "global", "none", NULL };
    static int rankcode[] = { LOCAL, GLOBAL, NOCLUST, LOCAL };
    static char *fontnamenames[] = {"gd","ps","svg", NULL};
    static int fontnamecodes[] = {NATIVEFONTS,PSFONTS,SVGFONTS,-1};
    int rankdir;
    GD_drawing(g) = NEW(layout_t);

    /* set this up fairly early in case any string sizes are needed */
    if ((p = agget(g, "fontpath")) || (p = getenv("DOTFONTPATH"))) {
	/* overide GDFONTPATH in local environment if dot
	 * wants its own */
#ifdef HAVE_SETENV
	setenv("GDFONTPATH", p, 1);
#else
	static char *buf = 0;

	buf = grealloc(buf, strlen("GDFONTPATH=") + strlen(p) + 1);
	strcpy(buf, "GDFONTPATH=");
	strcat(buf, p);
	putenv(buf);
#endif
    }

    GD_charset(g) = findCharset (g);

    if (!HTTPServerEnVar) {
	Gvimagepath = agget (g, "imagepath");
	if (!Gvimagepath)
	    Gvimagepath = Gvfilepath;
    }

    GD_drawing(g)->quantum =
	late_double(g, agfindgraphattr(g, "quantum"), 0.0, 0.0);

    /* setting rankdir=LR is only defined in dot,
     * but having it set causes shape code and others to use it. 
     * The result is confused output, so we turn it off unless requested.
     * This effective rankdir is stored in the bottom 2 bits of g->u.rankdir.
     * Sometimes, the code really needs the graph's rankdir, e.g., neato -n
     * with record shapes, so we store the real rankdir in the next 2 bits.
     */
    rankdir = RANKDIR_TB;
    if ((p = agget(g, "rankdir"))) {
	if (streq(p, "LR"))
	    rankdir = RANKDIR_LR;
	else if (streq(p, "BT"))
	    rankdir = RANKDIR_BT;
	else if (streq(p, "RL"))
	    rankdir = RANKDIR_RL;
    }
    if (use_rankdir)
	SET_RANKDIR (g, (rankdir << 2) | rankdir);
    else
	SET_RANKDIR (g, (rankdir << 2));

    xf = late_double(g, agfindgraphattr(g, "nodesep"),
		DEFAULT_NODESEP, MIN_NODESEP);
    GD_nodesep(g) = POINTS(xf);

    p = late_string(g, agfindgraphattr(g, "ranksep"), NULL);
    if (p) {
	if (sscanf(p, "%lf", &xf) == 0)
	    xf = DEFAULT_RANKSEP;
	else {
	    if (xf < MIN_RANKSEP)
		xf = MIN_RANKSEP;
	}
	if (strstr(p, "equally"))
	    GD_exact_ranksep(g) = TRUE;
    } else
	xf = DEFAULT_RANKSEP;
    GD_ranksep(g) = POINTS(xf);

    GD_showboxes(g) = late_int(g, agfindgraphattr(g, "showboxes"), 0, 0);
    p = late_string(g, agfindgraphattr(g, "fontnames"), NULL);
    GD_fontnames(g) = maptoken(p, fontnamenames, fontnamecodes);

    setRatio(g);
    GD_drawing(g)->filled =
	getdoubles2ptf(g, "size", &(GD_drawing(g)->size));
    getdoubles2ptf(g, "page", &(GD_drawing(g)->page));

    GD_drawing(g)->centered = mapbool(agget(g, "center"));

    if ((p = agget(g, "rotate")))
	GD_drawing(g)->landscape = (atoi(p) == 90);
    else if ((p = agget(g, "orientation")))
	GD_drawing(g)->landscape = ((p[0] == 'l') || (p[0] == 'L'));
    else if ((p = agget(g, "landscape")))
	GD_drawing(g)->landscape = mapbool(p);

    p = agget(g, "clusterrank");
    CL_type = maptoken(p, rankname, rankcode);
    p = agget(g, "concentrate");
    Concentrate = mapbool(p);
    State = GVBEGIN;
    EdgeLabelsDone = 0;

    GD_drawing(g)->dpi = 0.0;
    if (((p = agget(g, "dpi")) && p[0])
	|| ((p = agget(g, "resolution")) && p[0]))
	GD_drawing(g)->dpi = atof(p);

    do_graph_label(g);

    Initial_dist = MYHUGE;

    G_ordering = agfindgraphattr(g, "ordering");
    G_gradientangle = agfindgraphattr(g,"gradientangle");
    G_margin = agfindgraphattr(g, "margin");

    /* initialize nodes */
    N_height = agfindnodeattr(g, "height");
    N_width = agfindnodeattr(g, "width");
    N_shape = agfindnodeattr(g, "shape");
    N_color = agfindnodeattr(g, "color");
    N_fillcolor = agfindnodeattr(g, "fillcolor");
    N_style = agfindnodeattr(g, "style");
    N_fontsize = agfindnodeattr(g, "fontsize");
    N_fontname = agfindnodeattr(g, "fontname");
    N_fontcolor = agfindnodeattr(g, "fontcolor");
    N_label = agfindnodeattr(g, "label");
    if (!N_label)
	N_label = agattr(g, AGNODE, "label", NODENAME_ESC);
    N_xlabel = agfindnodeattr(g, "xlabel");
    N_showboxes = agfindnodeattr(g, "showboxes");
    N_penwidth = agfindnodeattr(g, "penwidth");
    N_ordering = agfindnodeattr(g, "ordering");
    N_margin = agfindnodeattr(g, "margin");
    /* attribs for polygon shapes */
    N_sides = agfindnodeattr(g, "sides");
    N_peripheries = agfindnodeattr(g, "peripheries");
    N_skew = agfindnodeattr(g, "skew");
    N_orientation = agfindnodeattr(g, "orientation");
    N_distortion = agfindnodeattr(g, "distortion");
    N_fixed = agfindnodeattr(g, "fixedsize");
    N_imagescale = agfindnodeattr(g, "imagescale");
    N_nojustify = agfindnodeattr(g, "nojustify");
    N_layer = agfindnodeattr(g, "layer");
    N_group = agfindnodeattr(g, "group");
    N_comment = agfindnodeattr(g, "comment");
    N_vertices = agfindnodeattr(g, "vertices");
    N_z = agfindnodeattr(g, "z");
    N_gradientangle = agfindnodeattr(g,"gradientangle");

    /* initialize edges */
    E_weight = agfindedgeattr(g, "weight");
    E_color = agfindedgeattr(g, "color");
    E_fillcolor = agfindedgeattr(g, "fillcolor");
    E_fontsize = agfindedgeattr(g, "fontsize");
    E_fontname = agfindedgeattr(g, "fontname");
    E_fontcolor = agfindedgeattr(g, "fontcolor");
    E_label = agfindedgeattr(g, "label");
    E_xlabel = agfindedgeattr(g, "xlabel");
    E_label_float = agfindedgeattr(g, "labelfloat");
    /* vladimir */
    E_dir = agfindedgeattr(g, "dir");
    E_arrowhead = agfindedgeattr(g, "arrowhead");
    E_arrowtail = agfindedgeattr(g, "arrowtail");
    E_headlabel = agfindedgeattr(g, "headlabel");
    E_taillabel = agfindedgeattr(g, "taillabel");
    E_labelfontsize = agfindedgeattr(g, "labelfontsize");
    E_labelfontname = agfindedgeattr(g, "labelfontname");
    E_labelfontcolor = agfindedgeattr(g, "labelfontcolor");
    E_labeldistance = agfindedgeattr(g, "labeldistance");
    E_labelangle = agfindedgeattr(g, "labelangle");
    /* end vladimir */
    E_minlen = agfindedgeattr(g, "minlen");
    E_showboxes = agfindedgeattr(g, "showboxes");
    E_style = agfindedgeattr(g, "style");
    E_decorate = agfindedgeattr(g, "decorate");
    E_arrowsz = agfindedgeattr(g, "arrowsize");
    E_constr = agfindedgeattr(g, "constraint");
    E_layer = agfindedgeattr(g, "layer");
    E_comment = agfindedgeattr(g, "comment");
    E_tailclip = agfindedgeattr(g, "tailclip");
    E_headclip = agfindedgeattr(g, "headclip");
    E_penwidth = agfindedgeattr(g, "penwidth");

    /* background */
    GD_drawing(g)->xdots = init_xdot (g);

    /* initialize id, if any */

    if ((p = agget(g, "id")) && *p)
	GD_drawing(g)->id = strdup_and_subst_obj(p, g);
}
Exemplo n.º 5
0
static void addXLabels(Agraph_t * gp)
{
    Agnode_t *np;
    Agedge_t *ep;
    int cnt, i, n_objs, n_lbls;
    int n_nlbls = 0;		/* # of unset node xlabels */
    int n_elbls = 0;		/* # of unset edge labels or xlabels */
    int n_set_lbls = 0;		/* # of set xlabels and edge labels */
    int n_clbls = 0;		/* # of set cluster labels */
    boxf bb;
    pointf ur;
    textlabel_t* lp;
    label_params_t params;
    object_t* objs;
    xlabel_t* lbls;
    object_t* objp;
    xlabel_t* xlp;
    Agsym_t* force;
    int et = EDGE_TYPE(gp);

    if (!(GD_has_labels(gp) & NODE_XLABEL) &&
	!(GD_has_labels(gp) & EDGE_XLABEL) &&
	!(GD_has_labels(gp) & TAIL_LABEL) &&
	!(GD_has_labels(gp) & HEAD_LABEL) &&
	(!(GD_has_labels(gp) & EDGE_LABEL) || EdgeLabelsDone))
	return;

    for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {
	if (ND_xlabel(np)) {
	    if (ND_xlabel(np)->set)
		n_set_lbls++;
	    else
		n_nlbls++;
	}
	for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
	    if (ED_xlabel(ep)) {
		if (ED_xlabel(ep)->set)
		    n_set_lbls++;
		else if (HAVE_EDGE(ep))
		    n_elbls++;
	    }
	    if (ED_head_label(ep)) {
		if (ED_head_label(ep)->set)
		    n_set_lbls++;
		else if (HAVE_EDGE(ep))
		    n_elbls++;
	    }
	    if (ED_tail_label(ep)) {
		if (ED_tail_label(ep)->set)
		    n_set_lbls++;
		else if (HAVE_EDGE(ep))
		    n_elbls++;
	    }
	    if (ED_label(ep)) {
		if (ED_label(ep)->set)
		    n_set_lbls++;
		else if (HAVE_EDGE(ep))
		    n_elbls++;
	    }
	}
    }
    if (GD_has_labels(gp) & GRAPH_LABEL)
	n_clbls = countClusterLabels (gp);

    /* A label for each unpositioned external label */
    n_lbls = n_nlbls + n_elbls;
    if (n_lbls == 0) return;

    /* An object for each node, each positioned external label, any cluster label, 
     * and all unset edge labels and xlabels.
     */
    n_objs = agnnodes(gp) + n_set_lbls + n_clbls + n_elbls;
    objp = objs = N_NEW(n_objs, object_t);
    xlp = lbls = N_NEW(n_lbls, xlabel_t);
    bb.LL = pointfof(INT_MAX, INT_MAX);
    bb.UR = pointfof(-INT_MAX, -INT_MAX);

    for (np = agfstnode(gp); np; np = agnxtnode(gp, np)) {

	bb = addNodeObj (np, objp, bb);
	if ((lp = ND_xlabel(np))) {
	    if (lp->set) {
		objp++;
		bb = addLabelObj (lp, objp, bb);
	    }
	    else {
		addXLabel (lp, objp, xlp, 0, ur); 
		xlp++;
	    }
	}
	objp++;
	for (ep = agfstout(gp, np); ep; ep = agnxtout(gp, ep)) {
	    if ((lp = ED_label(ep))) {
		if (lp->set) {
		    bb = addLabelObj (lp, objp, bb);
		}
		else if (HAVE_EDGE(ep)) {
		    addXLabel (lp, objp, xlp, 1, edgeMidpoint(gp, ep)); 
		    xlp++;
		}
		else {
		    agerr(AGWARN, "no position for edge with label %s",
			    ED_label(ep)->text);
		    continue;
		}
	        objp++;
	    }
	    if ((lp = ED_tail_label(ep))) {
		if (lp->set) {
		    bb = addLabelObj (lp, objp, bb);
		}
		else if (HAVE_EDGE(ep)) {
		    addXLabel (lp, objp, xlp, 1, edgeTailpoint(ep)); 
		    xlp++;
		}
		else {
		    agerr(AGWARN, "no position for edge with tail label %s",
			    ED_tail_label(ep)->text);
		    continue;
		}
		objp++;
	    }
	    if ((lp = ED_head_label(ep))) {
		if (lp->set) {
		    bb = addLabelObj (lp, objp, bb);
		}
		else if (HAVE_EDGE(ep)) {
		    addXLabel (lp, objp, xlp, 1, edgeHeadpoint(ep)); 
		    xlp++;
		}
		else {
		    agerr(AGWARN, "no position for edge with head label %s",
			    ED_head_label(ep)->text);
		    continue;
		}
		objp++;
	    }
	    if ((lp = ED_xlabel(ep))) {
		if (lp->set) {
		    bb = addLabelObj (lp, objp, bb);
		}
		else if (HAVE_EDGE(ep)) {
		    addXLabel (lp, objp, xlp, 1, edgeMidpoint(gp, ep)); 
		    xlp++;
		}
		else {
		    agerr(AGWARN, "no position for edge with xlabel %s",
			    ED_xlabel(ep)->text);
		    continue;
		}
		objp++;
	    }
	}
    }
    if (n_clbls) {
	cinfo_t info;
	info.bb = bb;
	info.objp = objp;
	info = addClusterObj (gp, info);
	bb = info.bb;
    }

    force = agfindgraphattr(gp, "forcelabels");

    params.force = late_bool(gp, force, TRUE);
    params.bb = bb;
    placeLabels(objs, n_objs, lbls, n_lbls, &params);
    if (Verbose)
	printData(objs, n_objs, lbls, n_lbls, &params);

    xlp = lbls;
    cnt = 0;
    for (i = 0; i < n_lbls; i++) {
	if (xlp->set) {
	    cnt++;
	    lp = (textlabel_t *) (xlp->lbl);
	    lp->set = 1;
	    lp->pos = centerPt(xlp);
	    updateBB (gp, lp);
	}
	xlp++;
    }
    if (Verbose)
	fprintf (stderr, "%d out of %d labels positioned.\n", cnt, n_lbls);
    else if (cnt != n_lbls)
	agerr(AGWARN, "%d out of %d exterior labels positioned.\n", cnt, n_lbls);
    free(objs);
    free(lbls);
}
Exemplo n.º 6
0
static void gv_graph_state(GVJ_t *job, graph_t *g)
{
#ifndef WITH_CGRAPH
    int i;
#endif
    int j;
    Agsym_t *a;
    gv_argvlist_t *list;

    list = &(job->selected_obj_type_name);
    j = 0;
    if (g == agroot(g)) {
        if (agisdirected(g))
            gv_argvlist_set_item(list, j++, s_digraph);
        else
            gv_argvlist_set_item(list, j++, s_graph);
    }
    else {
        gv_argvlist_set_item(list, j++, s_subgraph);
    }
    gv_argvlist_set_item(list, j++, agnameof(g));
    list->argc = j;

    list = &(job->selected_obj_attributes);
#ifndef WITH_CGRAPH
    for (i = 0, j = 0; i < dtsize(g->univ->globattr->dict); i++) {
        a = g->univ->globattr->list[i];
#else
    a = NULL;
    while ((a = agnxtattr(g, AGRAPH, a))) {
#endif
        gv_argvlist_set_item(list, j++, a->name);
#ifndef WITH_CGRAPH
        gv_argvlist_set_item(list, j++, agxget(g, a->index));
#else
        gv_argvlist_set_item(list, j++, agxget(g, a));
#endif
        gv_argvlist_set_item(list, j++, (char*)GVATTR_STRING);
    }
    list->argc = j;

    a = agfindgraphattr(g, s_href);
    if (!a)
        a = agfindgraphattr(g, s_URL);
    if (a)
#ifndef WITH_CGRAPH
        job->selected_href = strdup_and_subst_obj(agxget(g, a->index), (void*)g);
#else
        job->selected_href = strdup_and_subst_obj(agxget(g, a), (void*)g);
#endif
}

static void gv_node_state(GVJ_t *job, node_t *n)
{
#ifndef WITH_CGRAPH
    int i;
#endif
    int j;
    Agsym_t *a;
    Agraph_t *g;
    gv_argvlist_t *list;

    list = &(job->selected_obj_type_name);
    j = 0;
    gv_argvlist_set_item(list, j++, s_node);
    gv_argvlist_set_item(list, j++, agnameof(n));
    list->argc = j;

    list = &(job->selected_obj_attributes);
    g = agroot(agraphof(n));
#ifndef WITH_CGRAPH
    for (i = 0, j = 0; i < dtsize(g->univ->nodeattr->dict); i++) {
        a = g->univ->nodeattr->list[i];
#else
    a = NULL;
    while ((a = agnxtattr(g, AGNODE, a))) {
#endif
        gv_argvlist_set_item(list, j++, a->name);
#ifndef WITH_CGRAPH
        gv_argvlist_set_item(list, j++, agxget(n, a->index));
#else
        gv_argvlist_set_item(list, j++, agxget(n, a));
#endif
    }
    list->argc = j;

    a = agfindnodeattr(agraphof(n), s_href);
    if (!a)
        a = agfindnodeattr(agraphof(n), s_URL);
    if (a)
#ifndef WITH_CGRAPH
        job->selected_href = strdup_and_subst_obj(agxget(n, a->index), (void*)n);
#else
        job->selected_href = strdup_and_subst_obj(agxget(n, a), (void*)n);
#endif
}

static void gv_edge_state(GVJ_t *job, edge_t *e)
{
#ifndef WITH_CGRAPH
    int i;
#endif
    int j;
    Agsym_t *a;
    Agraph_t *g;
    gv_argvlist_t *nlist, *alist;

    nlist = &(job->selected_obj_type_name);

    /* only tail, head, and key are strictly identifying properties,
     * but we commonly alse use edge kind (e.g. "->") and tailport,headport
     * in edge names */
    j = 0;
    gv_argvlist_set_item(nlist, j++, s_edge);
    gv_argvlist_set_item(nlist, j++, agnameof(agtail(e)));
    j++; /* skip tailport slot for now */
    gv_argvlist_set_item(nlist, j++, agisdirected(agraphof(agtail(e)))?"->":"--");
    gv_argvlist_set_item(nlist, j++, agnameof(aghead(e)));
    j++; /* skip headport slot for now */
    j++; /* skip key slot for now */
    nlist->argc = j;

    alist = &(job->selected_obj_attributes);
    g = agroot(agraphof(aghead(e)));
#ifndef WITH_CGRAPH
    for (i = 0, j = 0; i < dtsize(g->univ->edgeattr->dict); i++) {
        a = g->univ->edgeattr->list[i];
#else
    a = NULL;
    while ((a = agnxtattr(g, AGEDGE, a))) {
#endif

        /* tailport and headport can be shown as part of the name, but they
         * are not identifying properties of the edge so we
         * also list them as modifyable attributes. */
        if (strcmp(a->name,s_tailport) == 0)
#ifndef WITH_CGRAPH
            gv_argvlist_set_item(nlist, 2, agxget(e, a->index));
#else
            gv_argvlist_set_item(nlist, 2, agxget(e, a));
#endif
        else if (strcmp(a->name,s_headport) == 0)
#ifndef WITH_CGRAPH
            gv_argvlist_set_item(nlist, 5, agxget(e, a->index));
#else
            gv_argvlist_set_item(nlist, 5, agxget(e, a));
#endif

        /* key is strictly an identifying property to distinguish multiple
         * edges between the same node pair.   Its non-writable, so
         * no need to list it as an attribute as well. */
        else if (strcmp(a->name,s_key) == 0) {
#ifndef WITH_CGRAPH
            gv_argvlist_set_item(nlist, 6, agxget(e, a->index));
#else
            gv_argvlist_set_item(nlist, 6, agxget(e, a));
#endif
            continue;
        }

        gv_argvlist_set_item(alist, j++, a->name);
#ifndef WITH_CGRAPH
        gv_argvlist_set_item(alist, j++, agxget(e, a->index));
#else
        gv_argvlist_set_item(alist, j++, agxget(e, a));
#endif
    }