Changeset 391 for python/trunk/Lib/test/test_itertools.py
- Timestamp:
- Mar 19, 2014, 11:31:01 PM (11 years ago)
- Location:
- python/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
python/trunk
-
Property svn:mergeinfo
set to
/python/vendor/Python-2.7.6 merged eligible /python/vendor/current merged eligible
-
Property svn:mergeinfo
set to
-
python/trunk/Lib/test/test_itertools.py
r2 r391 3 3 from itertools import * 4 4 from weakref import proxy 5 from decimal import Decimal 6 from fractions import Fraction 5 7 import sys 6 8 import operator … … 8 10 import copy 9 11 import pickle 12 from functools import reduce 10 13 maxsize = test_support.MAX_Py_ssize_t 11 14 minsize = -maxsize-1 … … 109 112 yield tuple(pool[i] for i in indices) 110 113 114 def combinations3(iterable, r): 115 'Pure python version from cwr()' 116 pool = tuple(iterable) 117 n = len(pool) 118 for indices in combinations_with_replacement(range(n), r): 119 if len(set(indices)) == r: 120 yield tuple(pool[i] for i in indices) 121 111 122 for n in range(7): 112 123 values = [5*x-12 for x in range(n)] 113 124 for r in range(n+2): 114 125 result = list(combinations(values, r)) 115 self.assertEqual(len(result), 0 if r>n else fact(n) / fact(r)/ fact(n-r)) # right number of combs126 self.assertEqual(len(result), 0 if r>n else fact(n) // fact(r) // fact(n-r)) # right number of combs 116 127 self.assertEqual(len(result), len(set(result))) # no repeats 117 128 self.assertEqual(result, sorted(result)) # lexicographic order … … 120 131 self.assertEqual(len(set(c)), r) # no duplicate elements 121 132 self.assertEqual(list(c), sorted(c)) # keep original ordering 122 self.assert _(all(e in values for e in c)) # elements taken from input iterable133 self.assertTrue(all(e in values for e in c)) # elements taken from input iterable 123 134 self.assertEqual(list(c), 124 135 [e for e in values if e in c]) # comb is a subsequence of the input iterable 125 136 self.assertEqual(result, list(combinations1(values, r))) # matches first pure python version 126 137 self.assertEqual(result, list(combinations2(values, r))) # matches second pure python version 127 128 # Test implementation detail: tuple re-use 138 self.assertEqual(result, list(combinations3(values, r))) # matches second pure python version 139 140 @test_support.impl_detail("tuple reuse is specific to CPython") 141 def test_combinations_tuple_reuse(self): 129 142 self.assertEqual(len(set(map(id, combinations('abcde', 3)))), 1) 130 143 self.assertNotEqual(len(set(map(id, list(combinations('abcde', 3))))), 1) 144 145 def test_combinations_with_replacement(self): 146 cwr = combinations_with_replacement 147 self.assertRaises(TypeError, cwr, 'abc') # missing r argument 148 self.assertRaises(TypeError, cwr, 'abc', 2, 1) # too many arguments 149 self.assertRaises(TypeError, cwr, None) # pool is not iterable 150 self.assertRaises(ValueError, cwr, 'abc', -2) # r is negative 151 self.assertEqual(list(cwr('ABC', 2)), 152 [('A','A'), ('A','B'), ('A','C'), ('B','B'), ('B','C'), ('C','C')]) 153 154 def cwr1(iterable, r): 155 'Pure python version shown in the docs' 156 # number items returned: (n+r-1)! / r! / (n-1)! when n>0 157 pool = tuple(iterable) 158 n = len(pool) 159 if not n and r: 160 return 161 indices = [0] * r 162 yield tuple(pool[i] for i in indices) 163 while 1: 164 for i in reversed(range(r)): 165 if indices[i] != n - 1: 166 break 167 else: 168 return 169 indices[i:] = [indices[i] + 1] * (r - i) 170 yield tuple(pool[i] for i in indices) 171 172 def cwr2(iterable, r): 173 'Pure python version shown in the docs' 174 pool = tuple(iterable) 175 n = len(pool) 176 for indices in product(range(n), repeat=r): 177 if sorted(indices) == list(indices): 178 yield tuple(pool[i] for i in indices) 179 180 def numcombs(n, r): 181 if not n: 182 return 0 if r else 1 183 return fact(n+r-1) // fact(r) // fact(n-1) 184 185 for n in range(7): 186 values = [5*x-12 for x in range(n)] 187 for r in range(n+2): 188 result = list(cwr(values, r)) 189 190 self.assertEqual(len(result), numcombs(n, r)) # right number of combs 191 self.assertEqual(len(result), len(set(result))) # no repeats 192 self.assertEqual(result, sorted(result)) # lexicographic order 193 194 regular_combs = list(combinations(values, r)) # compare to combs without replacement 195 if n == 0 or r <= 1: 196 self.assertEqual(result, regular_combs) # cases that should be identical 197 else: 198 self.assertTrue(set(result) >= set(regular_combs)) # rest should be supersets of regular combs 199 200 for c in result: 201 self.assertEqual(len(c), r) # r-length combinations 202 noruns = [k for k,v in groupby(c)] # combo without consecutive repeats 203 self.assertEqual(len(noruns), len(set(noruns))) # no repeats other than consecutive 204 self.assertEqual(list(c), sorted(c)) # keep original ordering 205 self.assertTrue(all(e in values for e in c)) # elements taken from input iterable 206 self.assertEqual(noruns, 207 [e for e in values if e in c]) # comb is a subsequence of the input iterable 208 self.assertEqual(result, list(cwr1(values, r))) # matches first pure python version 209 self.assertEqual(result, list(cwr2(values, r))) # matches second pure python version 210 211 @test_support.impl_detail("tuple reuse is specific to CPython") 212 def test_combinations_with_replacement_tuple_reuse(self): 213 cwr = combinations_with_replacement 214 self.assertEqual(len(set(map(id, cwr('abcde', 3)))), 1) 215 self.assertNotEqual(len(set(map(id, list(cwr('abcde', 3))))), 1) 131 216 132 217 def test_permutations(self): … … 177 262 for r in range(n+2): 178 263 result = list(permutations(values, r)) 179 self.assertEqual(len(result), 0 if r>n else fact(n) / fact(n-r)) # right number of perms264 self.assertEqual(len(result), 0 if r>n else fact(n) // fact(n-r)) # right number of perms 180 265 self.assertEqual(len(result), len(set(result))) # no repeats 181 266 self.assertEqual(result, sorted(result)) # lexicographic order … … 183 268 self.assertEqual(len(p), r) # r-length permutations 184 269 self.assertEqual(len(set(p)), r) # no duplicate elements 185 self.assert _(all(e in values for e in p)) # elements taken from input iterable270 self.assertTrue(all(e in values for e in p)) # elements taken from input iterable 186 271 self.assertEqual(result, list(permutations1(values, r))) # matches first pure python version 187 272 self.assertEqual(result, list(permutations2(values, r))) # matches second pure python version … … 190 275 self.assertEqual(result, list(permutations(values))) # test default r 191 276 192 # Test implementation detail: tuple re-use 277 @test_support.impl_detail("tuple resuse is CPython specific") 278 def test_permutations_tuple_reuse(self): 193 279 self.assertEqual(len(set(map(id, permutations('abcde', 3)))), 1) 194 280 self.assertNotEqual(len(set(map(id, list(permutations('abcde', 3))))), 1) 281 282 def test_combinatorics(self): 283 # Test relationships between product(), permutations(), 284 # combinations() and combinations_with_replacement(). 285 286 for n in range(6): 287 s = 'ABCDEFG'[:n] 288 for r in range(8): 289 prod = list(product(s, repeat=r)) 290 cwr = list(combinations_with_replacement(s, r)) 291 perm = list(permutations(s, r)) 292 comb = list(combinations(s, r)) 293 294 # Check size 295 self.assertEqual(len(prod), n**r) 296 self.assertEqual(len(cwr), (fact(n+r-1) // fact(r) // fact(n-1)) if n else (not r)) 297 self.assertEqual(len(perm), 0 if r>n else fact(n) // fact(n-r)) 298 self.assertEqual(len(comb), 0 if r>n else fact(n) // fact(r) // fact(n-r)) 299 300 # Check lexicographic order without repeated tuples 301 self.assertEqual(prod, sorted(set(prod))) 302 self.assertEqual(cwr, sorted(set(cwr))) 303 self.assertEqual(perm, sorted(set(perm))) 304 self.assertEqual(comb, sorted(set(comb))) 305 306 # Check interrelationships 307 self.assertEqual(cwr, [t for t in prod if sorted(t)==list(t)]) # cwr: prods which are sorted 308 self.assertEqual(perm, [t for t in prod if len(set(t))==r]) # perm: prods with no dups 309 self.assertEqual(comb, [t for t in perm if sorted(t)==list(t)]) # comb: perms that are sorted 310 self.assertEqual(comb, [t for t in cwr if len(set(t))==r]) # comb: cwrs without dups 311 self.assertEqual(comb, filter(set(cwr).__contains__, perm)) # comb: perm that is a cwr 312 self.assertEqual(comb, filter(set(perm).__contains__, cwr)) # comb: cwr that is a perm 313 self.assertEqual(comb, sorted(set(cwr) & set(perm))) # comb: both a cwr and a perm 314 315 def test_compress(self): 316 self.assertEqual(list(compress(data='ABCDEF', selectors=[1,0,1,0,1,1])), list('ACEF')) 317 self.assertEqual(list(compress('ABCDEF', [1,0,1,0,1,1])), list('ACEF')) 318 self.assertEqual(list(compress('ABCDEF', [0,0,0,0,0,0])), list('')) 319 self.assertEqual(list(compress('ABCDEF', [1,1,1,1,1,1])), list('ABCDEF')) 320 self.assertEqual(list(compress('ABCDEF', [1,0,1])), list('AC')) 321 self.assertEqual(list(compress('ABC', [0,1,1,1,1,1])), list('BC')) 322 n = 10000 323 data = chain.from_iterable(repeat(range(6), n)) 324 selectors = chain.from_iterable(repeat((0, 1))) 325 self.assertEqual(list(compress(data, selectors)), [1,3,5] * n) 326 self.assertRaises(TypeError, compress, None, range(6)) # 1st arg not iterable 327 self.assertRaises(TypeError, compress, range(6), None) # 2nd arg not iterable 328 self.assertRaises(TypeError, compress, range(6)) # too few args 329 self.assertRaises(TypeError, compress, range(6), None) # too many args 195 330 196 331 def test_count(self): … … 200 335 self.assertEqual(take(2, zip('abc',count(-1))), [('a', -1), ('b', 0)]) 201 336 self.assertEqual(take(2, zip('abc',count(-3))), [('a', -3), ('b', -2)]) 202 self.assertRaises(TypeError, count, 2, 3 )337 self.assertRaises(TypeError, count, 2, 3, 4) 203 338 self.assertRaises(TypeError, count, 'a') 204 339 self.assertEqual(list(islice(count(maxsize-5), 10)), range(maxsize-5, maxsize+5)) … … 211 346 self.assertEqual(repr(c), 'count(-9)') 212 347 c.next() 348 self.assertEqual(repr(count(10.25)), 'count(10.25)') 213 349 self.assertEqual(c.next(), -8) 214 350 for i in (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 10, sys.maxint-5, sys.maxint+5): … … 224 360 self.assertEqual(next(copy.deepcopy(c)), value) 225 361 self.assertEqual(next(pickle.loads(pickle.dumps(c))), value) 362 363 def test_count_with_stride(self): 364 self.assertEqual(zip('abc',count(2,3)), [('a', 2), ('b', 5), ('c', 8)]) 365 self.assertEqual(zip('abc',count(start=2,step=3)), 366 [('a', 2), ('b', 5), ('c', 8)]) 367 self.assertEqual(zip('abc',count(step=-1)), 368 [('a', 0), ('b', -1), ('c', -2)]) 369 self.assertEqual(zip('abc',count(2,0)), [('a', 2), ('b', 2), ('c', 2)]) 370 self.assertEqual(zip('abc',count(2,1)), [('a', 2), ('b', 3), ('c', 4)]) 371 self.assertEqual(take(20, count(maxsize-15, 3)), take(20, range(maxsize-15, maxsize+100, 3))) 372 self.assertEqual(take(20, count(-maxsize-15, 3)), take(20, range(-maxsize-15,-maxsize+100, 3))) 373 self.assertEqual(take(3, count(2, 3.25-4j)), [2, 5.25-4j, 8.5-8j]) 374 self.assertEqual(take(3, count(Decimal('1.1'), Decimal('.1'))), 375 [Decimal('1.1'), Decimal('1.2'), Decimal('1.3')]) 376 self.assertEqual(take(3, count(Fraction(2,3), Fraction(1,7))), 377 [Fraction(2,3), Fraction(17,21), Fraction(20,21)]) 378 self.assertEqual(repr(take(3, count(10, 2.5))), repr([10, 12.5, 15.0])) 379 c = count(3, 5) 380 self.assertEqual(repr(c), 'count(3, 5)') 381 c.next() 382 self.assertEqual(repr(c), 'count(8, 5)') 383 c = count(-9, 0) 384 self.assertEqual(repr(c), 'count(-9, 0)') 385 c.next() 386 self.assertEqual(repr(c), 'count(-9, 0)') 387 c = count(-9, -3) 388 self.assertEqual(repr(c), 'count(-9, -3)') 389 c.next() 390 self.assertEqual(repr(c), 'count(-12, -3)') 391 self.assertEqual(repr(c), 'count(-12, -3)') 392 self.assertEqual(repr(count(10.5, 1.25)), 'count(10.5, 1.25)') 393 self.assertEqual(repr(count(10.5, 1)), 'count(10.5)') # suppress step=1 when it's an int 394 self.assertEqual(repr(count(10.5, 1.00)), 'count(10.5, 1.0)') # do show float values lilke 1.0 395 for i in (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 10, sys.maxint-5, sys.maxint+5): 396 for j in (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 1, 10, sys.maxint-5, sys.maxint+5): 397 # Test repr (ignoring the L in longs) 398 r1 = repr(count(i, j)).replace('L', '') 399 if j == 1: 400 r2 = ('count(%r)' % i).replace('L', '') 401 else: 402 r2 = ('count(%r, %r)' % (i, j)).replace('L', '') 403 self.assertEqual(r1, r2) 226 404 227 405 def test_cycle(self): … … 353 531 self.assertRaises(TypeError, izip, 3) 354 532 self.assertRaises(TypeError, izip, range(3), 3) 355 # Check tuple re-use (implementation detail)356 533 self.assertEqual([tuple(list(pair)) for pair in izip('abc', 'def')], 357 534 zip('abc', 'def')) 358 535 self.assertEqual([pair for pair in izip('abc', 'def')], 359 536 zip('abc', 'def')) 537 538 @test_support.impl_detail("tuple reuse is specific to CPython") 539 def test_izip_tuple_resuse(self): 360 540 ids = map(id, izip('abc', 'def')) 361 541 self.assertEqual(min(ids), max(ids)) … … 371 551 [range(1000), range(0), range(3000,3050), range(1200), range(1500), range(0)], 372 552 ]: 373 target = map(None, *args) 553 # target = map(None, *args) <- this raises a py3k warning 554 # this is the replacement: 555 target = [tuple([arg[i] if i < len(arg) else None for arg in args]) 556 for i in range(max(map(len, args)))] 374 557 self.assertEqual(list(izip_longest(*args)), target) 375 558 self.assertEqual(list(izip_longest(*args, **{})), target) … … 383 566 self.assertEqual(list(izip_longest('abcdef')), zip('abcdef')) 384 567 385 self.assertEqual(list(izip_longest('abc', 'defg', **{})), map(None, 'abc', 'defg')) # empty keyword dict 568 self.assertEqual(list(izip_longest('abc', 'defg', **{})), 569 zip(list('abc') + [None], 'defg')) # empty keyword dict 386 570 self.assertRaises(TypeError, izip_longest, 3) 387 571 self.assertRaises(TypeError, izip_longest, range(3), 3) … … 398 582 self.fail('Did not raise Type in: ' + stmt) 399 583 400 # Check tuple re-use (implementation detail)401 584 self.assertEqual([tuple(list(pair)) for pair in izip_longest('abc', 'def')], 402 585 zip('abc', 'def')) 403 586 self.assertEqual([pair for pair in izip_longest('abc', 'def')], 404 587 zip('abc', 'def')) 588 589 @test_support.impl_detail("tuple reuse is specific to CPython") 590 def test_izip_longest_tuple_reuse(self): 405 591 ids = map(id, izip_longest('abc', 'def')) 406 592 self.assertEqual(min(ids), max(ids)) … … 506 692 self.assertEqual(len(list(product(*args))), expected_len) 507 693 508 # Test implementation detail: tuple re-use 694 @test_support.impl_detail("tuple reuse is specific to CPython") 695 def test_product_tuple_reuse(self): 509 696 self.assertEqual(len(set(map(id, product('abc', 'def')))), 1) 510 697 self.assertNotEqual(len(set(map(id, list(product('abc', 'def'))))), 1) 511 698 512 699 def test_repeat(self): 700 self.assertEqual(list(repeat(object='a', times=3)), ['a', 'a', 'a']) 513 701 self.assertEqual(zip(xrange(3),repeat('a')), 514 702 [(0, 'a'), (1, 'a'), (2, 'a')]) … … 600 788 self.assertEqual(len(list(islice(count(), 1, 10, maxsize))), 1) 601 789 790 # Issue #10323: Less islice in a predictable state 791 c = count() 792 self.assertEqual(list(islice(c, 1, 3, 50)), [1]) 793 self.assertEqual(next(c), 3) 794 602 795 def test_takewhile(self): 603 796 data = [1, 3, 5, 20, 2, 4, 6, 8] … … 697 890 a, b = tee('abc') 698 891 c, d = tee(a) 699 self.assert _(a is c)892 self.assertTrue(a is c) 700 893 701 894 # test tee_new … … 705 898 self.assertRaises(TypeError, tnew, 10) 706 899 t3 = tnew(t1) 707 self.assert _(list(t1) == list(t2) == list(t3) == list('abc'))900 self.assertTrue(list(t1) == list(t2) == list(t3) == list('abc')) 708 901 709 902 # test that tee objects are weak referencable … … 714 907 self.assertRaises(ReferenceError, getattr, p, '__class__') 715 908 909 # Issue 13454: Crash when deleting backward iterator from tee() 910 def test_tee_del_backward(self): 911 forward, backward = tee(repeat(None, 20000000)) 912 any(forward) # exhaust the iterator 913 del backward 914 716 915 def test_StopIteration(self): 717 916 self.assertRaises(StopIteration, izip().next) … … 751 950 [(0,1,2), (0,1,3), (0,2,3), (1,2,3)]) 752 951 952 def test_combinations_with_replacement(self): 953 self.assertEqual(list(combinations_with_replacement('ABC', 2)), 954 [('A','A'), ('A','B'), ('A','C'), ('B','B'), ('B','C'), ('C','C')]) 955 956 def test_compress(self): 957 self.assertEqual(list(compress('ABCDEF', [1,0,1,0,1,1])), list('ACEF')) 958 753 959 def test_count(self): 754 960 self.assertEqual(list(islice(count(10), 5)), [10, 11, 12, 13, 14]) … … 830 1036 a = [] 831 1037 self.makecycle(combinations([1,2,a,3], 3), a) 1038 1039 def test_combinations_with_replacement(self): 1040 a = [] 1041 self.makecycle(combinations_with_replacement([1,2,a,3], 3), a) 1042 1043 def test_compress(self): 1044 a = [] 1045 self.makecycle(compress('ABCDEF', [1,0,1,0,1,0]), a) 1046 1047 def test_count(self): 1048 a = [] 1049 Int = type('Int', (int,), dict(x=a)) 1050 self.makecycle(count(Int(0), Int(1)), a) 832 1051 833 1052 def test_cycle(self): … … 983 1202 self.assertRaises(TypeError, list, chain(N(s))) 984 1203 self.assertRaises(ZeroDivisionError, list, chain(E(s))) 1204 1205 def test_compress(self): 1206 for s in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5)): 1207 n = len(s) 1208 for g in (G, I, Ig, S, L, R): 1209 self.assertEqual(list(compress(g(s), repeat(1))), list(g(s))) 1210 self.assertRaises(TypeError, compress, X(s), repeat(1)) 1211 self.assertRaises(TypeError, list, compress(N(s), repeat(1))) 1212 self.assertRaises(ZeroDivisionError, list, compress(E(s), repeat(1))) 985 1213 986 1214 def test_product(self): … … 1175 1403 # count is not subclassable... 1176 1404 for cls in (repeat, izip, ifilter, ifilterfalse, chain, imap, 1177 starmap, islice, takewhile, dropwhile, cycle ):1405 starmap, islice, takewhile, dropwhile, cycle, compress): 1178 1406 class Subclass(cls): 1179 1407 def __init__(self, newarg=None, *args): … … 1183 1411 except TypeError, err: 1184 1412 # we expect type errors because of wrong argument count 1185 self. failIf("does not take keyword arguments" inerr.args[0])1413 self.assertNotIn("does not take keyword arguments", err.args[0]) 1186 1414 1187 1415 … … 1229 1457 # same group. 1230 1458 >>> data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28] 1231 >>> for k, g in groupby(enumerate(data), lambda (i,x):i-x):1459 >>> for k, g in groupby(enumerate(data), lambda t:t[0]-t[1]): 1232 1460 ... print map(operator.itemgetter(1), g) 1233 1461 ... … … 1263 1491 1264 1492 >>> def ncycles(iterable, n): 1265 ... "Returns the seq euence elements n times"1493 ... "Returns the sequence elements n times" 1266 1494 ... return chain(*repeat(iterable, n)) 1267 1495 … … 1309 1537 ... s = list(iterable) 1310 1538 ... return chain.from_iterable(combinations(s, r) for r in range(len(s)+1)) 1311 1312 >>> def compress(data, selectors):1313 ... "compress('ABCDEF', [1,0,1,0,1,1]) --> A C E F"1314 ... return (d for d, s in izip(data, selectors) if s)1315 1316 >>> def combinations_with_replacement(iterable, r):1317 ... "combinations_with_replacement('ABC', 3) --> AA AB AC BB BC CC"1318 ... pool = tuple(iterable)1319 ... n = len(pool)1320 ... if not n and r:1321 ... return1322 ... indices = [0] * r1323 ... yield tuple(pool[i] for i in indices)1324 ... while 1:1325 ... for i in reversed(range(r)):1326 ... if indices[i] != n - 1:1327 ... break1328 ... else:1329 ... return1330 ... indices[i:] = [indices[i] + 1] * (r - i)1331 ... yield tuple(pool[i] for i in indices)1332 1539 1333 1540 >>> def unique_everseen(iterable, key=None): … … 1414 1621 [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] 1415 1622 1416 >>> list(compress('abcdef', [1,0,1,0,1,1])) 1417 ['a', 'c', 'e', 'f'] 1418 1419 >>> list(combinations_with_replacement('abc', 2)) 1420 [('a', 'a'), ('a', 'b'), ('a', 'c'), ('b', 'b'), ('b', 'c'), ('c', 'c')] 1421 1422 >>> list(combinations_with_replacement('01', 3)) 1423 [('0', '0', '0'), ('0', '0', '1'), ('0', '1', '1'), ('1', '1', '1')] 1424 1425 >>> def combinations_with_replacement2(iterable, r): 1426 ... 'Alternate version that filters from product()' 1427 ... pool = tuple(iterable) 1428 ... n = len(pool) 1429 ... for indices in product(range(n), repeat=r): 1430 ... if sorted(indices) == list(indices): 1431 ... yield tuple(pool[i] for i in indices) 1432 1433 >>> list(combinations_with_replacement('abc', 2)) == list(combinations_with_replacement2('abc', 2)) 1623 >>> all(len(list(powerset(range(n)))) == 2**n for n in range(18)) 1434 1624 True 1435 1625 1436 >>> list(combinations_with_replacement('01', 3)) == list(combinations_with_replacement2('01', 3)) 1437 True 1438 1439 >>> list(combinations_with_replacement('2310', 6)) == list(combinations_with_replacement2('2310', 6)) 1626 >>> list(powerset('abcde')) == sorted(sorted(set(powerset('abcde'))), key=len) 1440 1627 True 1441 1628
Note:
See TracChangeset
for help on using the changeset viewer.