Project

General

Profile

Download (14.3 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / helpers / RubikScores.java @ 5df54787

1 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6 1c327853 Leszek Koltunski
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
9
10 7ff3cacb leszek
package org.distorted.helpers;
11 f0e87514 Leszek Koltunski
12 a7d8c3cd Leszek Koltunski
import java.util.HashMap;
13 3f7a4363 Leszek Koltunski
import java.util.UUID;
14
15 82ce8e64 Leszek Koltunski
import android.content.Context;
16 f0e87514 Leszek Koltunski
import android.content.SharedPreferences;
17 82ce8e64 Leszek Koltunski
import android.telephony.TelephonyManager;
18 1c90c64a Leszek Koltunski
19 25445dcf Leszek Koltunski
import com.google.firebase.crashlytics.FirebaseCrashlytics;
20
21 3f7a4363 Leszek Koltunski
import org.distorted.main.BuildConfig;
22 a7d8c3cd Leszek Koltunski
import org.distorted.objects.RubikObject;
23
import org.distorted.objects.RubikObjectList;
24 f0e87514 Leszek Koltunski
25 eb9263dd leszek
import static org.distorted.objectlib.metadata.ListObjects.MAX_SCRAMBLES;
26 d433b50e Leszek Koltunski
27 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
28 82ce8e64 Leszek Koltunski
// hold my own scores, and some other statistics.
29 f0e87514 Leszek Koltunski
30
public class RubikScores
31
  {
32 7bb30586 leszek
  public static final int LEVELS_SHOWN   = 8;
33 0c233a9a Leszek Koltunski
  public static final int RECORD_FIRST   = 0;
34
  public static final int RECORD_NEW     = 1;
35
  public static final int RECORD_NOT_NEW = 2;
36
37 5bda8973 Leszek Koltunski
  public static final int MAX_RECORD = 10;
38 97201782 leszek
  private static final int MULT = 1000000;
39 65bc1da3 Leszek Koltunski
  public static final int NO_RECORD = Integer.MAX_VALUE;
40 714292f1 Leszek Koltunski
  private static RubikScores mThis;
41
42 82ce8e64 Leszek Koltunski
  private String mName, mCountry;
43 c3ffcf58 Leszek Koltunski
  private boolean mNameIsVerified;
44
  private int mNumRuns;
45
  private int mNumPlays;
46 59aee296 Leszek Koltunski
  private int mNumWins;
47 82ce8e64 Leszek Koltunski
  private int mDeviceID;
48 0c233a9a Leszek Koltunski
  private int mNumStars;
49 f0e87514 Leszek Koltunski
50 a7d8c3cd Leszek Koltunski
  private static class MapValue
51
    {
52 65bc1da3 Leszek Koltunski
    int record;
53 a7d8c3cd Leszek Koltunski
    boolean submitted;
54
55 65bc1da3 Leszek Koltunski
    MapValue(int rec,int sub)
56 a7d8c3cd Leszek Koltunski
      {
57
      record    = rec;
58
      submitted = sub!=0;
59
      }
60
    }
61
62
  private final HashMap<Integer,MapValue> mMap;
63
64 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
65
66 714292f1 Leszek Koltunski
  private RubikScores()
67 f0e87514 Leszek Koltunski
    {
68 a7d8c3cd Leszek Koltunski
    mMap = new HashMap<>();
69 c3ffcf58 Leszek Koltunski
70 f895e77a Leszek Koltunski
    mName = "";
71 82ce8e64 Leszek Koltunski
    mCountry = "un";
72
73 c3ffcf58 Leszek Koltunski
    mNameIsVerified = false;
74 82ce8e64 Leszek Koltunski
75 59aee296 Leszek Koltunski
    mNumPlays= -1;
76
    mNumRuns = -1;
77
    mDeviceID= -1;
78
    mNumWins =  0;
79 0c233a9a Leszek Koltunski
    mNumStars=  0;
80 f0e87514 Leszek Koltunski
    }
81
82 a7d8c3cd Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
83
84
  private int mapKey(int object,int level)
85
    {
86 97201782 leszek
    return object*MULT + (level<0 ? MULT-1 : level);
87 a7d8c3cd Leszek Koltunski
    }
88
89 17f9a695 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
90
91
  private int privateGetDeviceID()
92
    {
93
    int id;
94
95
    try
96
      {
97
      String s = UUID.randomUUID().toString();
98 874c37b1 Leszek Koltunski
      id = s.hashCode();
99 17f9a695 Leszek Koltunski
      }
100
    catch(Exception ex)
101
      {
102
      id = 0;
103
      android.util.Log.e("scores", "Exception in getDeviceID()");
104
      }
105
106
    return id<0 ? -id : id;
107
    }
108
109
///////////////////////////////////////////////////////////////////////////////////////////////////
110 e06e1b7e Leszek Koltunski
111
  synchronized void successfulSubmit()
112
    {
113
    mNameIsVerified = true;
114
115 a7d8c3cd Leszek Koltunski
    for(int key: mMap.keySet())
116
      {
117
      MapValue value = mMap.get(key);
118
      if( value!=null ) value.submitted = true;
119
      }
120 e06e1b7e Leszek Koltunski
    }
121
122 714292f1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
123
124 e06e1b7e Leszek Koltunski
  int getDeviceID()
125 714292f1 Leszek Koltunski
    {
126 e06e1b7e Leszek Koltunski
    return mDeviceID;
127
    }
128 714292f1 Leszek Koltunski
129 f0e87514 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
130
131 e06e1b7e Leszek Koltunski
  synchronized boolean thereAreUnsubmittedRecords()
132 f0e87514 Leszek Koltunski
    {
133 a7d8c3cd Leszek Koltunski
    for(int key: mMap.keySet())
134
      {
135
      MapValue value = mMap.get(key);
136
      if( value!=null && !value.submitted && value.record<NO_RECORD) return true;
137
      }
138 c3ffcf58 Leszek Koltunski
139 e06e1b7e Leszek Koltunski
    return false;
140 f0e87514 Leszek Koltunski
    }
141
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143
144 e06e1b7e Leszek Koltunski
  synchronized String getRecordList(String strObj, String strLvl, String strTim)
145 f0e87514 Leszek Koltunski
    {
146 e06e1b7e Leszek Koltunski
    StringBuilder builderObj = new StringBuilder();
147
    StringBuilder builderLvl = new StringBuilder();
148
    StringBuilder builderTim = new StringBuilder();
149
    boolean first = true;
150 f0e87514 Leszek Koltunski
151 a7d8c3cd Leszek Koltunski
    for(int key: mMap.keySet())
152 f0e87514 Leszek Koltunski
      {
153 a7d8c3cd Leszek Koltunski
      MapValue value = mMap.get(key);
154 97201782 leszek
      int level = key%MULT;
155 f0e87514 Leszek Koltunski
156 97201782 leszek
      if( level<MULT-1 && value!=null && !value.submitted && value.record<NO_RECORD)
157 f0e87514 Leszek Koltunski
        {
158 a7d8c3cd Leszek Koltunski
        if( !first )
159 f0e87514 Leszek Koltunski
          {
160 a7d8c3cd Leszek Koltunski
          builderObj.append(',');
161
          builderLvl.append(',');
162
          builderTim.append(',');
163
          }
164
        first=false;
165 7ac0ee88 Leszek Koltunski
166 a7d8c3cd Leszek Koltunski
        RubikObject object = RubikObjectList.getObject(key/MULT);
167
168
        if( object!=null )
169
          {
170 84d746d7 Leszek Koltunski
          builderObj.append(object.getUpperName());
171 97201782 leszek
          builderLvl.append(level);
172 a7d8c3cd Leszek Koltunski
          builderTim.append(value.record);
173 f0e87514 Leszek Koltunski
          }
174
        }
175
      }
176 c3ffcf58 Leszek Koltunski
177 97201782 leszek
    return strObj+builderObj+strLvl+builderLvl+strTim+builderTim;
178 e06e1b7e Leszek Koltunski
    }
179 82ce8e64 Leszek Koltunski
180 e06e1b7e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
181
// Public API
182 ee4e7896 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
183
184 68484d1b leszek
  public boolean isVerified()     { return mNameIsVerified; }
185
  public int getNumPlays()        { return mNumPlays; }
186
  public int getNumRuns()         { return mNumRuns; }
187
  public String getName()         { return mName; }
188
  public String getCountry()      { return mCountry; }
189
  public void incrementNumPlays() { mNumPlays++; }
190
  public void incrementNumRuns()  { mNumRuns++; }
191 82ce8e64 Leszek Koltunski
192 e06e1b7e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
193 82ce8e64 Leszek Koltunski
194 59aee296 Leszek Koltunski
  public int incrementNumWins()
195 e06e1b7e Leszek Koltunski
    {
196 59aee296 Leszek Koltunski
    mNumWins++;
197
    return mNumWins;
198 e06e1b7e Leszek Koltunski
    }
199
200 0c233a9a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
201
202
  public void changeNumStars(int stars)
203
    {
204
    mNumStars += stars;
205 e9e744f7 Leszek Koltunski
    if( mNumStars<0 ) mNumStars = 0;
206 0c233a9a Leszek Koltunski
    }
207
208
///////////////////////////////////////////////////////////////////////////////////////////////////
209
210
  public int getNumStars()
211
    {
212
    return mNumStars;
213
    }
214
215 00fcfefa Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
216
217
  public void correctNumStars()
218
    {
219
    int numObjects = RubikObjectList.getNumObjects();
220
221
    for(int obj=0; obj<numObjects; obj++)
222
      {
223 7bb30586 leszek
      for(int level=0; level<=LEVELS_SHOWN; level++)
224 00fcfefa Leszek Koltunski
        {
225
        if( isSolved(obj,level) )
226
          {
227
          int numStars = computeNumStars(level+1);
228
          mNumStars += numStars;
229
          }
230
        }
231
      }
232
    }
233
234
///////////////////////////////////////////////////////////////////////////////////////////////////
235
236
  public int computeNumStars(int level)
237
    {
238 305f368e Leszek Koltunski
    return level>LEVELS_SHOWN ? 50 : level;
239 00fcfefa Leszek Koltunski
    }
240
241 e06e1b7e Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
242
243
  public void setName(String newName)
244
    {
245
    mName = newName;
246 82ce8e64 Leszek Koltunski
    }
247
248 d7e539d0 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
249
250 65bc1da3 Leszek Koltunski
  public synchronized int setRecord(int object, int level, int record)
251 a7d8c3cd Leszek Koltunski
    {
252 83018ac4 Leszek Koltunski
    int key = mapKey(object,level);
253 a7d8c3cd Leszek Koltunski
    MapValue oldValue = mMap.get(key);
254
255
    if( oldValue==null )
256
      {
257
      MapValue value = new MapValue(record,0);
258
      mMap.put(key,value);
259 0c233a9a Leszek Koltunski
      return RECORD_FIRST;
260 a7d8c3cd Leszek Koltunski
      }
261
262
    long oldRecord = oldValue.record;
263
264 ff377568 leszek
    if( oldRecord>record )
265 a7d8c3cd Leszek Koltunski
      {
266
      MapValue value = new MapValue(record,0);
267
      mMap.put(key,value);
268 0c233a9a Leszek Koltunski
      return RECORD_NEW;
269 a7d8c3cd Leszek Koltunski
      }
270
271 0c233a9a Leszek Koltunski
    return RECORD_NOT_NEW;
272 a7d8c3cd Leszek Koltunski
    }
273 0c233a9a Leszek Koltunski
274 a7d8c3cd Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
275
276 65bc1da3 Leszek Koltunski
  public synchronized int getRecord(int object, int level)
277 a7d8c3cd Leszek Koltunski
    {
278
    int key = mapKey(object,level);
279
    MapValue value = mMap.get(key);
280
    return value!=null ? value.record : NO_RECORD;
281
    }
282
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284
285
  public synchronized boolean isSolved(int object, int level)
286 d7e539d0 Leszek Koltunski
    {
287 a7d8c3cd Leszek Koltunski
    int key = mapKey(object,level);
288
    MapValue value = mMap.get(key);
289
    return value!=null && value.record<NO_RECORD;
290 d7e539d0 Leszek Koltunski
    }
291
292 714292f1 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
293
294 e06e1b7e Leszek Koltunski
  public void setCountry(Context context)
295 714292f1 Leszek Koltunski
    {
296 e06e1b7e Leszek Koltunski
    TelephonyManager tM =((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
297 714292f1 Leszek Koltunski
298 e06e1b7e Leszek Koltunski
    if( tM!=null )
299 714292f1 Leszek Koltunski
      {
300 e06e1b7e Leszek Koltunski
      mCountry = tM.getSimCountryIso();
301
302
      if( mCountry==null || mCountry.length()<=1 )
303
        {
304
        mCountry = tM.getNetworkCountryIso();
305
        }
306 714292f1 Leszek Koltunski
      }
307
308 e06e1b7e Leszek Koltunski
    // Special case: Dominicana. Its ISO-3166-alpha-2 country code is 'do' which we can't have here
309
    // because we later on map this to a resource name (the flag) and 'do' is a reserved Java keyword
310
    // and can't be a resource name.
311
312
    if( mCountry.equals("do") ) mCountry = "dm";
313 714292f1 Leszek Koltunski
    }
314 c3ffcf58 Leszek Koltunski
315 c8249cf6 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
316
317
  public void setCountry(String country)
318
    {
319
    mCountry = country;
320
321
    if( mCountry.equals("do") ) mCountry = "dm";  // see above
322
    }
323
324 286d73ae Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
325
326 e06e1b7e Leszek Koltunski
  public static RubikScores getInstance()
327 286d73ae Leszek Koltunski
    {
328 9333086d Leszek Koltunski
    if( mThis==null ) mThis = new RubikScores();
329 e06e1b7e Leszek Koltunski
    return mThis;
330 286d73ae Leszek Koltunski
    }
331
332 17f9a695 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
333
334 e06e1b7e Leszek Koltunski
  public synchronized void savePreferences(SharedPreferences.Editor editor)
335 17f9a695 Leszek Koltunski
    {
336 a7d8c3cd Leszek Koltunski
    int numObjects = RubikObjectList.getNumObjects();
337 e06e1b7e Leszek Koltunski
    StringBuilder builder = new StringBuilder();
338 1c90c64a Leszek Koltunski
339 5bda8973 Leszek Koltunski
    for(int level=0; level<=MAX_RECORD; level++)
340 e06e1b7e Leszek Koltunski
      {
341
      builder.setLength(0);
342 1c90c64a Leszek Koltunski
343 a7d8c3cd Leszek Koltunski
      for(int object=0; object<numObjects; object++)
344 e06e1b7e Leszek Koltunski
        {
345 a7d8c3cd Leszek Koltunski
        int key = mapKey(object,level);
346
        RubikObject obj = RubikObjectList.getObject(object);
347
        MapValue value = mMap.get(key);
348
349
        if( obj!=null && value!=null && value.record<NO_RECORD )
350
          {
351 84d746d7 Leszek Koltunski
          builder.append(obj.getUpperName());
352 a7d8c3cd Leszek Koltunski
          builder.append("=");
353
          builder.append(value.record);
354
          builder.append(",");
355
          builder.append(value.submitted ? 1:0 );
356
          builder.append(" ");
357
          }
358 e06e1b7e Leszek Koltunski
        }
359 c3ffcf58 Leszek Koltunski
360 e06e1b7e Leszek Koltunski
      editor.putString("scores_record"+level, builder.toString());
361
      }
362 c3ffcf58 Leszek Koltunski
363 e06e1b7e Leszek Koltunski
    editor.putString("scores_name"  , mName  );
364
    editor.putBoolean("scores_isVerified", mNameIsVerified);
365
    editor.putInt("scores_numPlays", mNumPlays);
366 59aee296 Leszek Koltunski
    editor.putInt("scores_numRuns" , mNumRuns );
367 e06e1b7e Leszek Koltunski
    editor.putInt("scores_deviceid", mDeviceID);
368 0c233a9a Leszek Koltunski
    editor.putInt("scores_review"  , mNumWins );   // legacy name
369
    editor.putInt("scores_numStars", mNumStars );
370 c3ffcf58 Leszek Koltunski
    }
371
372 7654a99d Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
373
374
  public synchronized void savePreferencesMinimal(SharedPreferences.Editor editor)
375
    {
376
    editor.putInt("scores_numStars", mNumStars );
377
    }
378
379 c3ffcf58 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
380
381 e06e1b7e Leszek Koltunski
  public synchronized void restorePreferences(SharedPreferences preferences)
382 c3ffcf58 Leszek Koltunski
    {
383 9333086d Leszek Koltunski
    String recordStr, subStr, nameStr, timeStr, submStr, errorStr="";
384 65bc1da3 Leszek Koltunski
    int start, end, equals, comma, ordinal, subm, time;
385 25445dcf Leszek Koltunski
    boolean thereWasError = false;
386 a7d8c3cd Leszek Koltunski
    int numObjects = RubikObjectList.getNumObjects();
387 c3ffcf58 Leszek Koltunski
388 5bda8973 Leszek Koltunski
    for(int level=0; level<=MAX_SCRAMBLES; level++)
389 e06e1b7e Leszek Koltunski
      {
390 5bda8973 Leszek Koltunski
      recordStr = preferences.getString("scores_record"+level, null);
391
      if( recordStr==null ) continue;
392 e06e1b7e Leszek Koltunski
      start = end = 0;
393 c3ffcf58 Leszek Koltunski
394 e06e1b7e Leszek Koltunski
      while( end!=-1 )
395
        {
396
        end = recordStr.indexOf(" ", start);
397 4895fff6 Leszek Koltunski
398 e06e1b7e Leszek Koltunski
        if( end==-1 ) subStr = recordStr.substring(start);
399
        else          subStr = recordStr.substring(start,end);
400 4895fff6 Leszek Koltunski
401 e06e1b7e Leszek Koltunski
        start = end+1;
402 4895fff6 Leszek Koltunski
403 e06e1b7e Leszek Koltunski
        equals = subStr.indexOf("=");
404 9333086d Leszek Koltunski
        comma  = subStr.indexOf(",");
405 4895fff6 Leszek Koltunski
406 9333086d Leszek Koltunski
        if( equals>=0 && comma>=0 )
407 4c0cd600 Leszek Koltunski
          {
408 9333086d Leszek Koltunski
          nameStr = subStr.substring(0,equals);
409 e06e1b7e Leszek Koltunski
          timeStr = subStr.substring(equals+1,comma);
410
          submStr = subStr.substring(comma+1);
411
412 5bda8973 Leszek Koltunski
          ordinal = RubikObjectList.getOrdinal(nameStr);
413 e06e1b7e Leszek Koltunski
414 5bda8973 Leszek Koltunski
          if( ordinal>=0 && ordinal<numObjects )
415 4895fff6 Leszek Koltunski
            {
416 fefb4739 Leszek Koltunski
            try
417
              {
418
              time = Integer.parseInt(timeStr);
419
              subm = Integer.parseInt(submStr);
420
              }
421
            catch(NumberFormatException ex)
422
              {
423
              subm = 1;
424
              time = 0;
425
              errorStr += ("error1: timeStr="+timeStr+" submStr: "+submStr+"\n");
426
              thereWasError= true;
427
              }
428 e06e1b7e Leszek Koltunski
429 7ac0ee88 Leszek Koltunski
            if( subm>=0 && subm<=1 )
430 e06e1b7e Leszek Koltunski
              {
431 a7d8c3cd Leszek Koltunski
              MapValue value = new MapValue(time,subm);
432 5bda8973 Leszek Koltunski
              int key = mapKey(ordinal,level);
433 a7d8c3cd Leszek Koltunski
              mMap.put(key,value);
434 e06e1b7e Leszek Koltunski
              }
435
            else
436
              {
437 9333086d Leszek Koltunski
              errorStr += ("error1: subm="+subm+" obj: "+nameStr+"\n");
438 25445dcf Leszek Koltunski
              thereWasError= true;
439 e06e1b7e Leszek Koltunski
              }
440
            }
441
          else
442
            {
443 5bda8973 Leszek Koltunski
            errorStr += ("error2: object="+ordinal+" obj: "+nameStr+"\n");
444 25445dcf Leszek Koltunski
            thereWasError = true;
445 4895fff6 Leszek Koltunski
            }
446 4c0cd600 Leszek Koltunski
          }
447 e06e1b7e Leszek Koltunski
        }
448 4895fff6 Leszek Koltunski
      }
449
450 e06e1b7e Leszek Koltunski
    mName           = preferences.getString("scores_name"  , "" );
451
    mNameIsVerified = preferences.getBoolean("scores_isVerified", false);
452
    mNumPlays       = preferences.getInt("scores_numPlays", 0);
453
    mNumRuns        = preferences.getInt("scores_numRuns" , 0);
454
    mDeviceID       = preferences.getInt("scores_deviceid",-1);
455 59aee296 Leszek Koltunski
    mNumWins        = preferences.getInt("scores_review"  , 0);
456 0c233a9a Leszek Koltunski
    mNumStars       = preferences.getInt("scores_numStars", 0);
457 e06e1b7e Leszek Koltunski
458
    if( mDeviceID==-1 ) mDeviceID = privateGetDeviceID();
459 25445dcf Leszek Koltunski
460
    if( thereWasError ) recordDBError(errorStr);
461
    }
462
463 0c233a9a Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
464
465
  public int numberOfSolvedMAXes()
466
    {
467
    int numObjects = RubikObjectList.getNumObjects();
468 7bb30586 leszek
    int ret=0;
469 0c233a9a Leszek Koltunski
470
    for(int obj=0; obj<numObjects; obj++)
471
      {
472 7bb30586 leszek
      if( isSolved(obj,LEVELS_SHOWN) ) ret++;
473 0c233a9a Leszek Koltunski
      }
474
475
    return ret;
476
    }
477
478 25445dcf Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
479
480
  public void recordDBError(String message)
481
    {
482
    if( BuildConfig.DEBUG )
483
      {
484
      android.util.Log.e("scores", message);
485
      }
486
    else
487
      {
488
      Exception ex = new Exception(message);
489
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
490
      crashlytics.setCustomKey("scores" , message);
491
      crashlytics.recordException(ex);
492
      }
493 4895fff6 Leszek Koltunski
    }
494 f0e87514 Leszek Koltunski
  }