Austin Schuh | 405fa6c | 2015-09-06 18:13:55 -0700 | [diff] [blame] | 1 | /* cdd.h: Header file for cddlib.c |
| 2 | written by Komei Fukuda, fukuda@math.ethz.ch |
| 3 | Version 0.94h, April 30, 2015 |
| 4 | */ |
| 5 | |
| 6 | /* cddlib.c : C-Implementation of the double description method for |
| 7 | computing all vertices and extreme rays of the polyhedron |
| 8 | P= {x : b - A x >= 0}. |
| 9 | Please read COPYING (GNU General Public Licence) and |
| 10 | the manual cddlibman.tex for detail. |
| 11 | */ |
| 12 | |
| 13 | #ifndef __CDD_H |
| 14 | #define __CDD_H |
| 15 | #endif /* __CDD_H */ |
| 16 | |
| 17 | #ifndef __CDDMP_H |
| 18 | #include "cddmp.h" |
| 19 | #endif /* __CDDMP_H */ |
| 20 | |
| 21 | #ifndef __CDDTYPES_H |
| 22 | #include "cddtypes.h" |
| 23 | #endif /* __CDDTYPES_H */ |
| 24 | |
| 25 | #ifdef GMPRATIONAL |
| 26 | #ifndef __CDD_HF |
| 27 | #include "cdd_f.h" |
| 28 | #endif |
| 29 | #endif |
| 30 | |
| 31 | /* GLOBAL CONSTANTS and STATISTICS VARIABLES (to be set by dd_set_global_constants() */ |
| 32 | extern mytype dd_zero; |
| 33 | extern mytype dd_one; |
| 34 | extern mytype dd_purezero; |
| 35 | extern mytype dd_minuszero; |
| 36 | extern mytype dd_minusone; |
| 37 | |
| 38 | extern time_t dd_statStartTime; /* cddlib starting time */ |
| 39 | extern long dd_statBApivots; /* basis finding pivots */ |
| 40 | extern long dd_statCCpivots; /* criss-cross pivots */ |
| 41 | extern long dd_statDS1pivots; /* phase 1 pivots */ |
| 42 | extern long dd_statDS2pivots; /* phase 2 pivots */ |
| 43 | extern long dd_statACpivots; /* anticycling (cc) pivots */ |
| 44 | #ifdef GMPRATIONAL |
| 45 | extern long dd_statBSpivots; /* basis status checking pivots */ |
| 46 | #endif |
| 47 | extern dd_LPSolverType dd_choiceLPSolverDefault; /* Default LP solver Algorithm */ |
| 48 | extern dd_LPSolverType dd_choiceRedcheckAlgorithm; /* Redundancy Checking Algorithm */ |
| 49 | extern dd_boolean dd_choiceLexicoPivotQ; /* whether to use the lexicographic pivot */ |
| 50 | |
| 51 | /* to be used to avoid creating temporary spaces for mytype */ |
| 52 | #define dd_almostzero 1.0E-7 |
| 53 | |
| 54 | /* ---------- FUNCTIONS MEANT TO BE PUBLIC ---------- */ |
| 55 | |
| 56 | #if defined(__cplusplus) |
| 57 | extern "C" { |
| 58 | #endif |
| 59 | |
| 60 | /* basic matrix manipulations */ |
| 61 | void dd_InitializeArow(dd_colrange,dd_Arow *); |
| 62 | void dd_InitializeAmatrix(dd_rowrange,dd_colrange,dd_Amatrix *); |
| 63 | void dd_InitializeBmatrix(dd_colrange, dd_Bmatrix *); |
| 64 | dd_SetFamilyPtr dd_CreateSetFamily(dd_bigrange,dd_bigrange); |
| 65 | void dd_FreeSetFamily(dd_SetFamilyPtr); |
| 66 | dd_MatrixPtr dd_CreateMatrix(dd_rowrange,dd_colrange); |
| 67 | void dd_FreeAmatrix(dd_rowrange,dd_colrange,dd_Amatrix); |
| 68 | void dd_FreeArow(dd_colrange, dd_Arow); |
| 69 | void dd_FreeBmatrix(dd_colrange,dd_Bmatrix); |
| 70 | void dd_FreeDDMemory(dd_PolyhedraPtr); |
| 71 | void dd_FreePolyhedra(dd_PolyhedraPtr); |
| 72 | void dd_FreeMatrix(dd_MatrixPtr); |
| 73 | void dd_SetToIdentity(dd_colrange, dd_Bmatrix); |
| 74 | |
| 75 | /* sign recognitions */ |
| 76 | dd_boolean dd_Nonnegative(mytype); |
| 77 | dd_boolean dd_Nonpositive(mytype); |
| 78 | dd_boolean dd_Positive(mytype); |
| 79 | dd_boolean dd_Negative(mytype); |
| 80 | dd_boolean dd_EqualToZero(mytype); |
| 81 | dd_boolean dd_Nonzero(mytype); |
| 82 | dd_boolean dd_Equal(mytype,mytype); |
| 83 | dd_boolean dd_Larger(mytype,mytype); |
| 84 | dd_boolean dd_Smaller(mytype,mytype); |
| 85 | void dd_abs(mytype, mytype); |
| 86 | void dd_LinearComb(mytype, mytype, mytype, mytype, mytype); |
| 87 | void dd_InnerProduct(mytype, dd_colrange, dd_Arow, dd_Arow); |
| 88 | |
| 89 | /* major cddlib operations */ |
| 90 | dd_MatrixPtr dd_CopyInput(dd_PolyhedraPtr); |
| 91 | dd_MatrixPtr dd_CopyOutput(dd_PolyhedraPtr); |
| 92 | dd_MatrixPtr dd_CopyInequalities(dd_PolyhedraPtr); |
| 93 | dd_MatrixPtr dd_CopyGenerators(dd_PolyhedraPtr); |
| 94 | dd_SetFamilyPtr dd_CopyIncidence(dd_PolyhedraPtr); |
| 95 | dd_SetFamilyPtr dd_CopyAdjacency(dd_PolyhedraPtr); |
| 96 | dd_SetFamilyPtr dd_CopyInputIncidence(dd_PolyhedraPtr); |
| 97 | dd_SetFamilyPtr dd_CopyInputAdjacency(dd_PolyhedraPtr); |
| 98 | dd_boolean dd_DDFile2File(char *ifile, char *ofile, dd_ErrorType *err); |
| 99 | dd_boolean dd_DDInputAppend(dd_PolyhedraPtr*, dd_MatrixPtr, dd_ErrorType*); |
| 100 | dd_MatrixPtr dd_PolyFile2Matrix(FILE *f, dd_ErrorType *); |
| 101 | |
| 102 | dd_PolyhedraPtr dd_DDMatrix2Poly(dd_MatrixPtr, dd_ErrorType *); |
| 103 | dd_PolyhedraPtr dd_DDMatrix2Poly2(dd_MatrixPtr, dd_RowOrderType, dd_ErrorType *); |
| 104 | dd_boolean dd_Redundant(dd_MatrixPtr, dd_rowrange, dd_Arow, dd_ErrorType *); /* 092 */ |
| 105 | dd_rowset dd_RedundantRows(dd_MatrixPtr, dd_ErrorType *); /* 092 */ |
| 106 | dd_boolean dd_SRedundant(dd_MatrixPtr, dd_rowrange, dd_Arow, dd_ErrorType *); /* 093a */ |
| 107 | dd_rowset dd_SRedundantRows(dd_MatrixPtr, dd_ErrorType *); /* 093a */ |
| 108 | dd_rowset dd_RedundantRowsViaShooting(dd_MatrixPtr, dd_ErrorType *); /* 092 */ |
| 109 | dd_rowrange dd_RayShooting(dd_MatrixPtr, dd_Arow intpt, dd_Arow direction); /* 092 */ |
| 110 | /* 092, find the first inequality "hit" by a ray from an intpt. */ |
| 111 | dd_boolean dd_ImplicitLinearity(dd_MatrixPtr, dd_rowrange, dd_Arow, dd_ErrorType *); /* 092 */ |
| 112 | dd_rowset dd_ImplicitLinearityRows(dd_MatrixPtr, dd_ErrorType *); /* 092 */ |
| 113 | int dd_FreeOfImplicitLinearity(dd_MatrixPtr, dd_Arow, dd_rowset *, dd_ErrorType *) ; /* 094 */ |
| 114 | dd_boolean dd_MatrixCanonicalizeLinearity(dd_MatrixPtr *, dd_rowset *,dd_rowindex *, dd_ErrorType *); /* 094 */ |
| 115 | dd_boolean dd_MatrixCanonicalize(dd_MatrixPtr *, dd_rowset *, dd_rowset *, dd_rowindex *, dd_ErrorType *); /* 094 */ |
| 116 | dd_boolean dd_MatrixRedundancyRemove(dd_MatrixPtr *M, dd_rowset *redset,dd_rowindex *newpos, dd_ErrorType *); /* 094 */ |
| 117 | dd_boolean dd_FindRelativeInterior(dd_MatrixPtr, dd_rowset *, dd_rowset *, dd_LPSolutionPtr *, dd_ErrorType *); /* 094 */ |
| 118 | dd_boolean dd_ExistsRestrictedFace(dd_MatrixPtr, dd_rowset, dd_rowset, dd_ErrorType *); /* 0.94 */ |
| 119 | dd_boolean dd_ExistsRestrictedFace2(dd_MatrixPtr, dd_rowset, dd_rowset, dd_LPSolutionPtr *, dd_ErrorType *); /* 0.94 */ |
| 120 | |
| 121 | dd_SetFamilyPtr dd_Matrix2Adjacency(dd_MatrixPtr, dd_ErrorType *); /* 093 */ |
| 122 | dd_SetFamilyPtr dd_Matrix2WeakAdjacency(dd_MatrixPtr, dd_ErrorType *); /* 093a */ |
| 123 | long dd_MatrixRank(dd_MatrixPtr, dd_rowset, dd_colset, dd_rowset *, dd_colset *); |
| 124 | |
| 125 | /* Matrix Basic Operations */ |
| 126 | dd_MatrixPtr dd_MatrixCopy(dd_MatrixPtr); /* a new name for dd_CopyMatrix */ |
| 127 | dd_MatrixPtr dd_CopyMatrix(dd_MatrixPtr); /* 090c, kept for compatibility */ |
| 128 | dd_MatrixPtr dd_MatrixNormalizedCopy(dd_MatrixPtr); /* 094 */ |
| 129 | dd_MatrixPtr dd_MatrixNormalizedSortedCopy(dd_MatrixPtr,dd_rowindex*); /* 094 */ |
| 130 | dd_MatrixPtr dd_MatrixUniqueCopy(dd_MatrixPtr,dd_rowindex*); /* 094 */ |
| 131 | dd_MatrixPtr dd_MatrixNormalizedSortedUniqueCopy(dd_MatrixPtr,dd_rowindex*); /* 094 */ |
| 132 | dd_MatrixPtr dd_MatrixSortedUniqueCopy(dd_MatrixPtr,dd_rowindex*); /* 094 */ |
| 133 | |
| 134 | dd_MatrixPtr dd_MatrixAppend(dd_MatrixPtr, dd_MatrixPtr); /* a name for dd_AppendMatrix */ |
| 135 | dd_MatrixPtr dd_AppendMatrix(dd_MatrixPtr, dd_MatrixPtr); /* 090c, kept for compatibility */ |
| 136 | |
| 137 | int dd_MatrixAppendTo(dd_MatrixPtr*, dd_MatrixPtr); /* 092 */ |
| 138 | int dd_Remove(dd_MatrixPtr*, dd_rowrange); /* 092 */ |
| 139 | dd_MatrixPtr dd_MatrixSubmatrix(dd_MatrixPtr, dd_rowset delset); /* 092 */ |
| 140 | dd_MatrixPtr dd_MatrixSubmatrix2(dd_MatrixPtr, dd_rowset delset,dd_rowindex*); /* 094. It returns new row positions. */ |
| 141 | dd_MatrixPtr dd_MatrixSubmatrix2L(dd_MatrixPtr, dd_rowset delset,dd_rowindex*); /* 094. Linearity shifted up. */ |
| 142 | int dd_MatrixShiftupLinearity(dd_MatrixPtr *,dd_rowindex *); /* 094 */ |
| 143 | int dd_MatrixRowRemove(dd_MatrixPtr *M, dd_rowrange r); /* 092 */ |
| 144 | int dd_MatrixRowRemove2(dd_MatrixPtr *M, dd_rowrange r,dd_rowindex*); /* 094*/ |
| 145 | int dd_MatrixRowsRemove(dd_MatrixPtr *M, dd_rowset delset); /* 094 */ |
| 146 | int dd_MatrixRowsRemove2(dd_MatrixPtr *M, dd_rowset delset,dd_rowindex*); /* 094 */ |
| 147 | |
| 148 | /* input/output */ |
| 149 | void dd_SetInputFile(FILE **f,dd_DataFileType inputfile, dd_ErrorType *); |
| 150 | void dd_SetWriteFileName(dd_DataFileType, dd_DataFileType, char, dd_RepresentationType); |
| 151 | |
| 152 | void dd_WriteAmatrix(FILE *, dd_Amatrix, dd_rowrange, dd_colrange); |
| 153 | void dd_WriteArow(FILE *f, dd_Arow a, dd_colrange); |
| 154 | void dd_WriteBmatrix(FILE *, dd_colrange, dd_Bmatrix T); |
| 155 | void dd_WriteMatrix(FILE *, dd_MatrixPtr); |
| 156 | void dd_MatrixIntegerFilter(dd_MatrixPtr); |
| 157 | void dd_WriteReal(FILE *, mytype); |
| 158 | void dd_WriteNumber(FILE *f, mytype x); |
| 159 | /* write a number depending on the arithmetic used. */ |
| 160 | void dd_WritePolyFile(FILE *, dd_PolyhedraPtr); |
| 161 | void dd_WriteRunningMode(FILE *, dd_PolyhedraPtr); |
| 162 | void dd_WriteErrorMessages(FILE *, dd_ErrorType); |
| 163 | void dd_WriteSetFamily(FILE *, dd_SetFamilyPtr); |
| 164 | void dd_WriteSetFamilyCompressed(FILE *, dd_SetFamilyPtr); |
| 165 | void dd_WriteProgramDescription(FILE *); |
| 166 | void dd_WriteDDTimes(FILE *, dd_PolyhedraPtr); |
| 167 | void dd_WriteTimes(FILE *, time_t, time_t); |
| 168 | void dd_WriteIncidence(FILE *, dd_PolyhedraPtr); |
| 169 | void dd_WriteAdjacency(FILE *, dd_PolyhedraPtr); |
| 170 | void dd_WriteInputAdjacency(FILE *, dd_PolyhedraPtr); |
| 171 | void dd_WriteInputIncidence(FILE *, dd_PolyhedraPtr); |
| 172 | |
| 173 | /* functions and types for LP solving */ |
| 174 | |
| 175 | dd_LPPtr dd_Matrix2LP(dd_MatrixPtr, dd_ErrorType *); |
| 176 | /* Load a matrix to create an LP object. */ |
| 177 | |
| 178 | dd_LPPtr dd_Matrix2Feasibility(dd_MatrixPtr, dd_ErrorType *); |
| 179 | /* Load a matrix to create an LP object for feasibility (obj == 0) .*/ /* 094 */ |
| 180 | |
| 181 | dd_LPPtr dd_Matrix2Feasibility2(dd_MatrixPtr, dd_rowset, dd_rowset, dd_ErrorType *); |
| 182 | /* Load a matrix to create an LP object for feasibility with additional equality and |
| 183 | strict inequality constraints. */ /* 094 */ |
| 184 | |
| 185 | dd_boolean dd_LPSolve(dd_LPPtr,dd_LPSolverType,dd_ErrorType *); |
| 186 | dd_boolean dd_LPSolve0(dd_LPPtr,dd_LPSolverType,dd_ErrorType *); |
| 187 | void dd_CrissCrossSolve(dd_LPPtr lp,dd_ErrorType *); |
| 188 | void dd_DualSimplexSolve(dd_LPPtr lp,dd_ErrorType *); |
| 189 | |
| 190 | dd_LPPtr dd_MakeLPforInteriorFinding(dd_LPPtr); |
| 191 | dd_LPSolutionPtr dd_CopyLPSolution(dd_LPPtr); /* 0.90c */ |
| 192 | void dd_WriteLP(FILE *, dd_LPPtr); /* 092 */ |
| 193 | |
| 194 | dd_LPPtr dd_CreateLPData(dd_LPObjectiveType,dd_NumberType,dd_rowrange,dd_colrange); |
| 195 | int dd_LPReverseRow(dd_LPPtr, dd_rowrange); |
| 196 | /* reverse the i-th row (1 <= i <= no. of rows) */ |
| 197 | int dd_LPReplaceRow(dd_LPPtr, dd_rowrange, dd_Arow); |
| 198 | /* replace the i-th row (1 <= i <= no. of rows) */ |
| 199 | dd_Arow dd_LPCopyRow(dd_LPPtr, dd_rowrange); |
| 200 | /* copy the i-th row (1 <= i <= no. of rows) */ |
| 201 | |
| 202 | void dd_FreeLPData(dd_LPPtr); |
| 203 | void dd_FreeLPSolution(dd_LPSolutionPtr); |
| 204 | |
| 205 | void dd_WriteLPResult(FILE *, dd_LPPtr, dd_ErrorType); |
| 206 | void dd_WriteLPErrorMessages(FILE *, dd_ErrorType); |
| 207 | void dd_WriteLPTimes(FILE *, dd_LPPtr); |
| 208 | void dd_WriteLPStats(FILE *f); |
| 209 | void dd_WriteLPMode(FILE *f); |
| 210 | |
| 211 | dd_MatrixPtr dd_FourierElimination(dd_MatrixPtr,dd_ErrorType *); |
| 212 | dd_MatrixPtr dd_BlockElimination(dd_MatrixPtr, dd_colset, dd_ErrorType *); |
| 213 | |
| 214 | #if defined(__cplusplus) |
| 215 | } |
| 216 | #endif |
| 217 | |
| 218 | /* ---------- FUNCTIONS MEANT TO BE NON-PUBLIC ---------- */ |
| 219 | void dd_QuickSort(dd_rowindex, long, long, dd_Amatrix, long); |
| 220 | void dd_RandomPermutation(dd_rowindex, long, unsigned int seed); |
| 221 | void dd_UniqueRows(dd_rowindex, long, long, dd_Amatrix, long, dd_rowset, long *); |
| 222 | |
| 223 | dd_boolean dd_DoubleDescription(dd_PolyhedraPtr, dd_ErrorType*); |
| 224 | dd_boolean dd_DoubleDescription2(dd_PolyhedraPtr, dd_RowOrderType, dd_ErrorType *); |
| 225 | |
| 226 | void dd_FreeDDMemory0(dd_ConePtr); |
| 227 | void dd_fread_rational_value (FILE *f, mytype value); |
Brian Silverman | f1cff39 | 2015-10-11 19:36:18 -0400 | [diff] [blame] | 228 | void dd_sread_rational_value (char *s, mytype value); |
Austin Schuh | 405fa6c | 2015-09-06 18:13:55 -0700 | [diff] [blame] | 229 | void dd_AddNewHalfspace1(dd_ConePtr, dd_rowrange); |
| 230 | void dd_AddNewHalfspace2(dd_ConePtr, dd_rowrange); |
| 231 | void dd_AddRay(dd_ConePtr, mytype *); |
| 232 | void dd_AddArtificialRay(dd_ConePtr); |
| 233 | void dd_AValue(mytype*,dd_colrange, dd_Amatrix, mytype *, dd_rowrange); |
| 234 | void dd_CheckAdjacency(dd_ConePtr, dd_RayPtr*, dd_RayPtr*, dd_boolean *); |
| 235 | void dd_CheckEquality(dd_colrange, dd_RayPtr *, dd_RayPtr *, dd_boolean *); |
| 236 | void dd_ComputeRowOrderVector(dd_ConePtr); |
| 237 | void dd_ConditionalAddEdge(dd_ConePtr,dd_RayPtr, dd_RayPtr, dd_RayPtr); |
| 238 | void dd_CopyArow(mytype *, mytype *, dd_colrange); |
| 239 | void dd_CopyNormalizedAmatrix(mytype **, mytype **, dd_rowrange, dd_colrange); |
| 240 | void dd_CopyNormalizedArow(mytype *, mytype *, dd_colrange); |
| 241 | void dd_CopyAmatrix(mytype **, mytype **, dd_rowrange, dd_colrange); |
| 242 | void dd_PermuteCopyAmatrix(mytype **, mytype **, dd_rowrange, dd_colrange, dd_rowindex); |
| 243 | void dd_PermutePartialCopyAmatrix(mytype **, mytype **, dd_rowrange, dd_colrange, dd_rowindex,dd_rowrange, dd_rowrange); |
| 244 | void dd_CopyBmatrix(dd_colrange, dd_Bmatrix T, dd_Bmatrix TCOPY); |
| 245 | void dd_CopyRay(mytype *, dd_colrange, dd_RayPtr, |
| 246 | dd_RepresentationType, dd_colindex); |
| 247 | void dd_CreateInitialEdges(dd_ConePtr); |
| 248 | void dd_CreateNewRay(dd_ConePtr, dd_RayPtr, dd_RayPtr, dd_rowrange); |
| 249 | void dd_Eliminate(dd_ConePtr, dd_RayPtr*); |
| 250 | void dd_EvaluateARay1(dd_rowrange, dd_ConePtr); |
| 251 | void dd_EvaluateARay2(dd_rowrange, dd_ConePtr); |
| 252 | void dd_FeasibilityIndices(long *, long *, dd_rowrange, dd_ConePtr); |
| 253 | void dd_FindBasis(dd_ConePtr, long *rank); |
| 254 | void dd_FindInitialRays(dd_ConePtr, dd_boolean *); |
| 255 | void dd_ColumnReduce(dd_ConePtr); |
| 256 | void dd_GaussianColumnPivot(dd_rowrange, dd_colrange, dd_Amatrix, dd_Bmatrix, dd_rowrange, dd_colrange); |
| 257 | dd_boolean dd_LexSmaller(mytype *, mytype *, long); |
| 258 | dd_boolean dd_LexLarger(mytype *, mytype *, long); |
| 259 | dd_boolean dd_LexEqual(mytype *, mytype *, long); |
| 260 | void dd_Normalize(dd_colrange, mytype *); |
| 261 | void dd_MatrixIntegerFilter(dd_MatrixPtr); |
| 262 | void dd_ProcessCommandLine(FILE*,dd_MatrixPtr, const char *); |
| 263 | void dd_SelectNextHalfspace(dd_ConePtr, dd_rowset, dd_rowrange *); |
| 264 | void dd_SelectPivot2(dd_rowrange,dd_colrange,dd_Amatrix, |
| 265 | dd_Bmatrix,dd_RowOrderType,dd_rowindex, dd_rowset,dd_rowrange,dd_rowset, |
| 266 | dd_colset,dd_rowrange *,dd_colrange *,dd_boolean *); |
| 267 | void dd_SelectPreorderedNext(dd_ConePtr, dd_rowset, dd_rowrange *); |
| 268 | void dd_SetInequalitySets(dd_ConePtr); |
| 269 | void dd_SnapToInteger(mytype, mytype); |
| 270 | void dd_StoreRay1(dd_ConePtr, mytype *, dd_boolean *); |
| 271 | void dd_StoreRay2(dd_ConePtr, mytype *, dd_boolean *, dd_boolean *); |
| 272 | void dd_TableauEntry(mytype *, dd_rowrange, dd_colrange, dd_Amatrix, dd_Bmatrix T, dd_rowrange, dd_colrange); |
| 273 | void dd_UpdateEdges(dd_ConePtr, dd_RayPtr, dd_RayPtr); |
| 274 | void dd_UpdateRowOrderVector(dd_ConePtr, dd_rowset PriorityRows); |
| 275 | void dd_WriteRay(FILE *, dd_colrange, dd_RayPtr, |
| 276 | dd_RepresentationType, dd_colindex); |
| 277 | void dd_ZeroIndexSet(dd_rowrange, dd_colrange, dd_Amatrix, mytype *, dd_rowset); |
| 278 | |
| 279 | /* New functions to handle data loading, NON-PUBLIC */ |
| 280 | dd_NumberType dd_GetNumberType(const char *); |
| 281 | dd_ConePtr dd_ConeDataLoad(dd_PolyhedraPtr); |
| 282 | dd_PolyhedraPtr dd_CreatePolyhedraData(dd_rowrange, dd_colrange); |
| 283 | dd_boolean dd_InitializeConeData(dd_rowrange, dd_colrange, dd_ConePtr*); |
| 284 | dd_boolean dd_AppendMatrix2Poly(dd_PolyhedraPtr*, dd_MatrixPtr); |
| 285 | |
| 286 | |
| 287 | |
| 288 | |
| 289 | |
| 290 | /* end of cddlib.h */ |