Changeset 14764 for branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionLatexFormatter.cs
- Timestamp:
- 03/18/17 14:22:46 (8 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/symbreg-factors-2650/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Formatters/SymbolicDataAnalysisExpressionLatexFormatter.cs
r14399 r14764 72 72 strBuilder.AppendLine(FormatRecursively(symbolicExpressionTree.Root)); 73 73 return strBuilder.ToString(); 74 } catch 74 } catch(NotImplementedException ex) { 75 75 return ex.Message + Environment.NewLine + ex.StackTrace; 76 76 } … … 85 85 FormatBegin(node, strBuilder); 86 86 87 if 87 if(node.SubtreeCount > 0) { 88 88 strBuilder.Append(FormatRecursively(node.GetSubtree(0))); 89 89 } 90 90 int i = 1; 91 foreach 91 foreach(var subTree in node.Subtrees.Skip(1)) { 92 92 FormatSep(node, strBuilder, i); 93 93 // format the whole subtree … … 102 102 103 103 private void FormatBegin(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) { 104 if 104 if(node.Symbol is Addition) { 105 105 strBuilder.Append(@" \left( "); 106 } else if 107 if 106 } else if(node.Symbol is Subtraction) { 107 if(node.SubtreeCount == 1) { 108 108 strBuilder.Append(@"- \left( "); 109 109 } else { 110 110 strBuilder.Append(@" \left( "); 111 111 } 112 } else if 113 } else if 114 if 112 } else if(node.Symbol is Multiplication) { 113 } else if(node.Symbol is Division) { 114 if(node.SubtreeCount == 1) { 115 115 strBuilder.Append(@" \cfrac{1}{"); 116 116 } else { 117 117 strBuilder.Append(@" \cfrac{ "); 118 118 } 119 } else if 119 } else if(node.Symbol is Average) { 120 120 // skip output of (1/1) if only one subtree 121 if 121 if(node.SubtreeCount > 1) { 122 122 strBuilder.Append(@" \cfrac{1}{" + node.SubtreeCount + @"}"); 123 123 } 124 124 strBuilder.Append(@" \left( "); 125 } else if 125 } else if(node.Symbol is Logarithm) { 126 126 strBuilder.Append(@"\log \left( "); 127 } else if 127 } else if(node.Symbol is Exponential) { 128 128 strBuilder.Append(@"\exp \left( "); 129 } else if 129 } else if(node.Symbol is Square) { 130 130 strBuilder.Append(@"\left("); 131 } else if 131 } else if(node.Symbol is SquareRoot) { 132 132 strBuilder.Append(@"\sqrt{"); 133 } else if 133 } else if(node.Symbol is Sine) { 134 134 strBuilder.Append(@"\sin \left( "); 135 } else if 135 } else if(node.Symbol is Cosine) { 136 136 strBuilder.Append(@"\cos \left( "); 137 } else if 137 } else if(node.Symbol is Tangent) { 138 138 strBuilder.Append(@"\tan \left( "); 139 } else if 139 } else if(node.Symbol is AiryA) { 140 140 strBuilder.Append(@"\operatorname{airy}_a \left( "); 141 } else if 141 } else if(node.Symbol is AiryB) { 142 142 strBuilder.Append(@"\operatorname{airy}_b \left( "); 143 } else if 143 } else if(node.Symbol is Bessel) { 144 144 strBuilder.Append(@"\operatorname{bessel}_1 \left( "); 145 } else if 145 } else if(node.Symbol is CosineIntegral) { 146 146 strBuilder.Append(@"\operatorname{cosInt} \left( "); 147 } else if 147 } else if(node.Symbol is Dawson) { 148 148 strBuilder.Append(@"\operatorname{dawson} \left( "); 149 } else if 149 } else if(node.Symbol is Erf) { 150 150 strBuilder.Append(@"\operatorname{erf} \left( "); 151 } else if 151 } else if(node.Symbol is ExponentialIntegralEi) { 152 152 strBuilder.Append(@"\operatorname{expInt}_i \left( "); 153 } else if 153 } else if(node.Symbol is FresnelCosineIntegral) { 154 154 strBuilder.Append(@"\operatorname{fresnel}_\operatorname{cosInt} \left( "); 155 } else if 155 } else if(node.Symbol is FresnelSineIntegral) { 156 156 strBuilder.Append(@"\operatorname{fresnel}_\operatorname{sinInt} \left( "); 157 } else if 157 } else if(node.Symbol is Gamma) { 158 158 strBuilder.Append(@"\Gamma \left( "); 159 } else if 159 } else if(node.Symbol is HyperbolicCosineIntegral) { 160 160 strBuilder.Append(@"\operatorname{hypCosInt} \left( "); 161 } else if 161 } else if(node.Symbol is HyperbolicSineIntegral) { 162 162 strBuilder.Append(@"\operatorname{hypSinInt} \left( "); 163 } else if 163 } else if(node.Symbol is Norm) { 164 164 strBuilder.Append(@"\operatorname{norm} \left( "); 165 } else if 165 } else if(node.Symbol is Psi) { 166 166 strBuilder.Append(@"\operatorname{digamma} \left( "); 167 } else if 167 } else if(node.Symbol is SineIntegral) { 168 168 strBuilder.Append(@"\operatorname{sinInt} \left( "); 169 } else if 169 } else if(node.Symbol is GreaterThan) { 170 170 strBuilder.Append(@" \left( "); 171 } else if 171 } else if(node.Symbol is LessThan) { 172 172 strBuilder.Append(@" \left( "); 173 } else if 173 } else if(node.Symbol is And) { 174 174 strBuilder.Append(@" \left( \left( "); 175 } else if 175 } else if(node.Symbol is Or) { 176 176 strBuilder.Append(@" \left( \left( "); 177 } else if 177 } else if(node.Symbol is Not) { 178 178 strBuilder.Append(@" \neg \left( "); 179 } else if 179 } else if(node.Symbol is IfThenElse) { 180 180 strBuilder.Append(@" \operatorname{if} \left( "); 181 } else if 181 } else if(node.Symbol is Constant) { 182 182 var constName = "c_{" + constIndex + "}"; 183 183 constIndex++; 184 184 var constNode = node as ConstantTreeNode; 185 if 185 if(constNode.Value.IsAlmost(1.0)) { 186 186 strBuilder.Append("1 "); 187 187 } else { … … 190 190 } 191 191 192 } else if 192 } else if(node.Symbol is FactorVariable) { 193 193 var factorNode = node as FactorVariableTreeNode; 194 194 var constName = "c_{" + constIndex + "}"; 195 195 strBuilder.Append(constName + " "); 196 foreach 196 foreach(var e in factorNode.Symbol.GetVariableValues(factorNode.VariableName) 197 197 .Zip(factorNode.Weights, Tuple.Create)) { 198 198 constants.Add(new KeyValuePair<string, double>("c_{" + constIndex + ", " + EscapeLatexString(factorNode.VariableName) + "=" + EscapeLatexString(e.Item1) + "}", e.Item2)); 199 199 } 200 200 constIndex++; 201 } else if 201 } else if(node.Symbol is BinaryFactorVariable) { 202 202 var binFactorNode = node as BinaryFactorVariableTreeNode; 203 if 203 if(!binFactorNode.Weight.IsAlmost((1.0))) { 204 204 var constName = "c_{" + constIndex + "}"; 205 205 strBuilder.Append(constName + " \\cdot"); … … 207 207 constIndex++; 208 208 } 209 strBuilder.Append( EscapeLatexString(binFactorNode.VariableName));209 strBuilder.Append("(" + EscapeLatexString(binFactorNode.VariableName)); 210 210 strBuilder.Append(LagToString(currentLag)); 211 strBuilder.Append(" = " + EscapeLatexString(binFactorNode.VariableValue) );212 } else if 211 strBuilder.Append(" = " + EscapeLatexString(binFactorNode.VariableValue) + " )"); 212 } else if(node.Symbol is LaggedVariable) { 213 213 var laggedVarNode = node as LaggedVariableTreeNode; 214 if 214 if(!laggedVarNode.Weight.IsAlmost(1.0)) { 215 215 var constName = "c_{" + constIndex + "}"; 216 216 strBuilder.Append(constName + " \\cdot"); … … 221 221 strBuilder.Append(LagToString(currentLag + laggedVarNode.Lag)); 222 222 223 } else if 223 } else if(node.Symbol is Variable) { 224 224 var varNode = node as VariableTreeNode; 225 if 225 if(!varNode.Weight.IsAlmost((1.0))) { 226 226 var constName = "c_{" + constIndex + "}"; 227 227 strBuilder.Append(constName + " \\cdot"); … … 231 231 strBuilder.Append(EscapeLatexString(varNode.VariableName)); 232 232 strBuilder.Append(LagToString(currentLag)); 233 } else if 233 } else if(node.Symbol is ProgramRootSymbol) { 234 234 strBuilder 235 235 .AppendLine("\\begin{align*}") 236 236 .AppendLine("\\nonumber"); 237 } else if 237 } else if(node.Symbol is Defun) { 238 238 var defunNode = node as DefunTreeNode; 239 239 strBuilder.Append(defunNode.FunctionName + " & = "); 240 } else if 240 } else if(node.Symbol is InvokeFunction) { 241 241 var invokeNode = node as InvokeFunctionTreeNode; 242 242 strBuilder.Append(invokeNode.Symbol.FunctionName + @" \left( "); 243 } else if 243 } else if(node.Symbol is StartSymbol) { 244 244 FormatStartSymbol(strBuilder); 245 } else if 245 } else if(node.Symbol is Argument) { 246 246 var argSym = node.Symbol as Argument; 247 247 strBuilder.Append(" ARG+" + argSym.ArgumentIndex + " "); 248 } else if 248 } else if(node.Symbol is Derivative) { 249 249 strBuilder.Append(@" \cfrac{d \left( "); 250 } else if 250 } else if(node.Symbol is TimeLag) { 251 251 var laggedNode = node as ILaggedTreeNode; 252 252 currentLag += laggedNode.Lag; 253 } else if 253 } else if(node.Symbol is Power) { 254 254 strBuilder.Append(@" \left( "); 255 } else if 255 } else if(node.Symbol is Root) { 256 256 strBuilder.Append(@" \left( "); 257 } else if 257 } else if(node.Symbol is Integral) { 258 258 // actually a new variable for t is needed in all subtrees (TODO) 259 259 var laggedTreeNode = node as ILaggedTreeNode; 260 260 strBuilder.Append(@"\sum_{t=" + (laggedTreeNode.Lag + currentLag) + @"}^0 \left( "); 261 } else if 261 } else if(node.Symbol is VariableCondition) { 262 262 var conditionTreeNode = node as VariableConditionTreeNode; 263 263 var constName = "c_{" + constants.Count + "}"; … … 276 276 277 277 private void FormatSep(ISymbolicExpressionTreeNode node, StringBuilder strBuilder, int step) { 278 if 278 if(node.Symbol is Addition) { 279 279 strBuilder.Append(" + "); 280 } else if 280 } else if(node.Symbol is Subtraction) { 281 281 strBuilder.Append(" - "); 282 } else if 282 } else if(node.Symbol is Multiplication) { 283 283 strBuilder.Append(@" \cdot "); 284 } else if 285 if 284 } else if(node.Symbol is Division) { 285 if(step + 1 == node.SubtreeCount) 286 286 strBuilder.Append(@"}{"); 287 287 else 288 288 strBuilder.Append(@" }{ \cfrac{ "); 289 } else if 289 } else if(node.Symbol is Average) { 290 290 strBuilder.Append(@" + "); 291 } else if 292 throw new InvalidOperationException(); 293 } else if 294 throw new InvalidOperationException(); 295 } else if 296 throw new InvalidOperationException(); 297 } else if 298 throw new InvalidOperationException(); 299 } else if 300 throw new InvalidOperationException(); 301 } else if 302 throw new InvalidOperationException(); 303 } else if 304 throw new InvalidOperationException(); 305 } else if 306 throw new InvalidOperationException(); 307 } else if 308 throw new InvalidOperationException(); 309 } else if 310 throw new InvalidOperationException(); 311 } else if 312 throw new InvalidOperationException(); 313 } else if 314 throw new InvalidOperationException(); 315 } else if 316 throw new InvalidOperationException(); 317 } else if 318 throw new InvalidOperationException(); 319 } else if 320 throw new InvalidOperationException(); 321 } else if 322 throw new InvalidOperationException(); 323 } else if 324 throw new InvalidOperationException(); 325 } else if 326 throw new InvalidOperationException(); 327 } else if 328 throw new InvalidOperationException(); 329 } else if 330 throw new InvalidOperationException(); 331 } else if 332 throw new InvalidOperationException(); 333 } else if 334 throw new InvalidOperationException(); 335 } else if 291 } else if(node.Symbol is Logarithm) { 292 throw new InvalidOperationException(); 293 } else if(node.Symbol is Exponential) { 294 throw new InvalidOperationException(); 295 } else if(node.Symbol is Square) { 296 throw new InvalidOperationException(); 297 } else if(node.Symbol is SquareRoot) { 298 throw new InvalidOperationException(); 299 } else if(node.Symbol is Sine) { 300 throw new InvalidOperationException(); 301 } else if(node.Symbol is Cosine) { 302 throw new InvalidOperationException(); 303 } else if(node.Symbol is Tangent) { 304 throw new InvalidOperationException(); 305 } else if(node.Symbol is AiryA) { 306 throw new InvalidOperationException(); 307 } else if(node.Symbol is AiryB) { 308 throw new InvalidOperationException(); 309 } else if(node.Symbol is Bessel) { 310 throw new InvalidOperationException(); 311 } else if(node.Symbol is CosineIntegral) { 312 throw new InvalidOperationException(); 313 } else if(node.Symbol is Dawson) { 314 throw new InvalidOperationException(); 315 } else if(node.Symbol is Erf) { 316 throw new InvalidOperationException(); 317 } else if(node.Symbol is ExponentialIntegralEi) { 318 throw new InvalidOperationException(); 319 } else if(node.Symbol is FresnelCosineIntegral) { 320 throw new InvalidOperationException(); 321 } else if(node.Symbol is FresnelSineIntegral) { 322 throw new InvalidOperationException(); 323 } else if(node.Symbol is Gamma) { 324 throw new InvalidOperationException(); 325 } else if(node.Symbol is HyperbolicCosineIntegral) { 326 throw new InvalidOperationException(); 327 } else if(node.Symbol is HyperbolicSineIntegral) { 328 throw new InvalidOperationException(); 329 } else if(node.Symbol is Norm) { 330 throw new InvalidOperationException(); 331 } else if(node.Symbol is Psi) { 332 throw new InvalidOperationException(); 333 } else if(node.Symbol is SineIntegral) { 334 throw new InvalidOperationException(); 335 } else if(node.Symbol is GreaterThan) { 336 336 strBuilder.Append(@" > "); 337 } else if 337 } else if(node.Symbol is LessThan) { 338 338 strBuilder.Append(@" < "); 339 } else if 339 } else if(node.Symbol is And) { 340 340 strBuilder.Append(@" > 0 \right) \land \left("); 341 } else if 341 } else if(node.Symbol is Or) { 342 342 strBuilder.Append(@" > 0 \right) \lor \left("); 343 } else if 344 throw new InvalidOperationException(); 345 } else if 343 } else if(node.Symbol is Not) { 344 throw new InvalidOperationException(); 345 } else if(node.Symbol is IfThenElse) { 346 346 strBuilder.Append(@" , "); 347 } else if 347 } else if(node.Symbol is ProgramRootSymbol) { 348 348 strBuilder.Append(@"\\" + Environment.NewLine); 349 } else if 350 } else if 349 } else if(node.Symbol is Defun) { 350 } else if(node.Symbol is InvokeFunction) { 351 351 strBuilder.Append(" , "); 352 } else if 352 } else if(node.Symbol is StartSymbol) { 353 353 strBuilder.Append(@"\\" + Environment.NewLine); 354 354 FormatStartSymbol(strBuilder); 355 } else if 355 } else if(node.Symbol is Power) { 356 356 strBuilder.Append(@"\right) ^ { \operatorname{round} \left("); 357 } else if 357 } else if(node.Symbol is Root) { 358 358 strBuilder.Append(@"\right) ^ { \cfrac{1}{ \operatorname{round} \left("); 359 } else if 359 } else if(node.Symbol is VariableCondition) { 360 360 var conditionTreeNode = node as VariableConditionTreeNode; 361 361 var const1Name = "c_{" + constants.Count + "}"; … … 374 374 375 375 private void FormatEnd(ISymbolicExpressionTreeNode node, StringBuilder strBuilder) { 376 if 377 strBuilder.Append(@" \right) "); 378 } else if 379 strBuilder.Append(@" \right) "); 380 } else if 381 } else if 376 if(node.Symbol is Addition) { 377 strBuilder.Append(@" \right) "); 378 } else if(node.Symbol is Subtraction) { 379 strBuilder.Append(@" \right) "); 380 } else if(node.Symbol is Multiplication) { 381 } else if(node.Symbol is Division) { 382 382 strBuilder.Append(" } "); 383 for 383 for(int i = 2; i < node.SubtreeCount; i++) 384 384 strBuilder.Append(" } "); 385 } else if 386 strBuilder.Append(@" \right) "); 387 } else if 388 strBuilder.Append(@" \right) "); 389 } else if 390 strBuilder.Append(@" \right) "); 391 } else if 385 } else if(node.Symbol is Average) { 386 strBuilder.Append(@" \right) "); 387 } else if(node.Symbol is Logarithm) { 388 strBuilder.Append(@" \right) "); 389 } else if(node.Symbol is Exponential) { 390 strBuilder.Append(@" \right) "); 391 } else if(node.Symbol is Square) { 392 392 strBuilder.Append(@"\right)^2"); 393 } else if 393 } else if(node.Symbol is SquareRoot) { 394 394 strBuilder.Append(@"}"); 395 } else if 396 strBuilder.Append(@" \right) "); 397 } else if 398 strBuilder.Append(@" \right) "); 399 } else if 400 strBuilder.Append(@" \right) "); 401 } else if 402 strBuilder.Append(@" \right) "); 403 } else if 404 strBuilder.Append(@" \right) "); 405 } else if 406 strBuilder.Append(@" \right) "); 407 } else if 408 strBuilder.Append(@" \right) "); 409 } else if 410 strBuilder.Append(@" \right) "); 411 } else if 412 strBuilder.Append(@" \right) "); 413 } else if 414 strBuilder.Append(@" \right) "); 415 } else if 416 strBuilder.Append(@" \right) "); 417 } else if 418 strBuilder.Append(@" \right) "); 419 } else if 420 strBuilder.Append(@" \right) "); 421 } else if 422 strBuilder.Append(@" \right) "); 423 } else if 424 strBuilder.Append(@" \right) "); 425 } else if 426 strBuilder.Append(@" \right) "); 427 } else if 428 strBuilder.Append(@" \right) "); 429 } else if 430 strBuilder.Append(@" \right) "); 431 } else if 432 strBuilder.Append(@" \right) "); 433 } else if 434 strBuilder.Append(@" \right) "); 435 } else if 395 } else if(node.Symbol is Sine) { 396 strBuilder.Append(@" \right) "); 397 } else if(node.Symbol is Cosine) { 398 strBuilder.Append(@" \right) "); 399 } else if(node.Symbol is Tangent) { 400 strBuilder.Append(@" \right) "); 401 } else if(node.Symbol is AiryA) { 402 strBuilder.Append(@" \right) "); 403 } else if(node.Symbol is AiryB) { 404 strBuilder.Append(@" \right) "); 405 } else if(node.Symbol is Bessel) { 406 strBuilder.Append(@" \right) "); 407 } else if(node.Symbol is CosineIntegral) { 408 strBuilder.Append(@" \right) "); 409 } else if(node.Symbol is Dawson) { 410 strBuilder.Append(@" \right) "); 411 } else if(node.Symbol is Erf) { 412 strBuilder.Append(@" \right) "); 413 } else if(node.Symbol is ExponentialIntegralEi) { 414 strBuilder.Append(@" \right) "); 415 } else if(node.Symbol is FresnelCosineIntegral) { 416 strBuilder.Append(@" \right) "); 417 } else if(node.Symbol is FresnelSineIntegral) { 418 strBuilder.Append(@" \right) "); 419 } else if(node.Symbol is Gamma) { 420 strBuilder.Append(@" \right) "); 421 } else if(node.Symbol is HyperbolicCosineIntegral) { 422 strBuilder.Append(@" \right) "); 423 } else if(node.Symbol is HyperbolicSineIntegral) { 424 strBuilder.Append(@" \right) "); 425 } else if(node.Symbol is Norm) { 426 strBuilder.Append(@" \right) "); 427 } else if(node.Symbol is Psi) { 428 strBuilder.Append(@" \right) "); 429 } else if(node.Symbol is SineIntegral) { 430 strBuilder.Append(@" \right) "); 431 } else if(node.Symbol is GreaterThan) { 432 strBuilder.Append(@" \right) "); 433 } else if(node.Symbol is LessThan) { 434 strBuilder.Append(@" \right) "); 435 } else if(node.Symbol is And) { 436 436 strBuilder.Append(@" > 0 \right) \right) "); 437 } else if 437 } else if(node.Symbol is Or) { 438 438 strBuilder.Append(@" > 0 \right) \right) "); 439 } else if 440 strBuilder.Append(@" \right) "); 441 } else if 442 strBuilder.Append(@" \right) "); 443 } else if 444 } else if 445 } else if 446 } else if 447 } else if 448 } else if 439 } else if(node.Symbol is Not) { 440 strBuilder.Append(@" \right) "); 441 } else if(node.Symbol is IfThenElse) { 442 strBuilder.Append(@" \right) "); 443 } else if(node.Symbol is Constant) { 444 } else if(node.Symbol is LaggedVariable) { 445 } else if(node.Symbol is Variable) { 446 } else if(node.Symbol is FactorVariable) { 447 } else if(node.Symbol is BinaryFactorVariable) { 448 } else if(node.Symbol is ProgramRootSymbol) { 449 449 strBuilder 450 450 .AppendLine("\\end{align*}") … … 452 452 .AppendLine("\\nonumber"); 453 453 // output all constant values 454 if 455 foreach 454 if(constants.Count > 0) { 455 foreach(var constant in constants) { 456 456 // replace "." with ".&" to align decimal points 457 457 var constStr = string.Format(System.Globalization.NumberFormatInfo.InvariantInfo, "{0:G5}", constant.Value); 458 if 458 if(!constStr.Contains(".")) constStr = constStr + ".0"; 459 459 constStr = constStr.Replace(".", "&."); // fix problem in rendering of aligned expressions 460 460 strBuilder.Append(constant.Key + "& = & " + constStr); … … 463 463 } 464 464 strBuilder.AppendLine("\\end{align*}"); 465 } else if 466 } else if 467 strBuilder.Append(@" \right) "); 468 } else if 469 } else if 470 } else if 465 } else if(node.Symbol is Defun) { 466 } else if(node.Symbol is InvokeFunction) { 467 strBuilder.Append(@" \right) "); 468 } else if(node.Symbol is StartSymbol) { 469 } else if(node.Symbol is Argument) { 470 } else if(node.Symbol is Derivative) { 471 471 strBuilder.Append(@" \right) }{dt} "); 472 } else if 472 } else if(node.Symbol is TimeLag) { 473 473 var laggedNode = node as ILaggedTreeNode; 474 474 currentLag -= laggedNode.Lag; 475 } else if 475 } else if(node.Symbol is Power) { 476 476 strBuilder.Append(@" \right) } "); 477 } else if 477 } else if(node.Symbol is Root) { 478 478 strBuilder.Append(@" \right) } } "); 479 } else if 480 strBuilder.Append(@" \right) "); 481 } else if 479 } else if(node.Symbol is Integral) { 480 strBuilder.Append(@" \right) "); 481 } else if(node.Symbol is VariableCondition) { 482 482 strBuilder.Append(@"\right) "); 483 483 } else { … … 488 488 private void FormatStartSymbol(StringBuilder strBuilder) { 489 489 strBuilder.Append(targetVariable ?? "target_" + (targetCount++)); 490 if 490 if(containsTimeSeriesSymbol) 491 491 strBuilder.Append("(t)"); 492 492 strBuilder.Append(" & = "); … … 494 494 495 495 private string LagToString(int lag) { 496 if 496 if(lag < 0) { 497 497 return "(t" + lag + ")"; 498 } else if 498 } else if(lag > 0) { 499 499 return "(t+" + lag + ")"; 500 500 } else return "";
Note: See TracChangeset
for help on using the changeset viewer.