1 
  2 __quiet = false;
  3 __magicNoPrint = { __magicNoPrint : 1111 }
  4 
  5 chatty = function(s){
  6     if ( ! __quiet )
  7         print( s );
  8 }
  9 
 10 friendlyEqual = function( a , b ){
 11     if ( a == b )
 12         return true;
 13 
 14     if ( tojson( a ) == tojson( b ) )
 15         return true;
 16 
 17     return false;
 18 }
 19 
 20 
 21 doassert = function( msg ){
 22     print( "assert: " + msg );
 23     throw msg;
 24 }
 25 
 26 assert = function( b , msg ){
 27     if ( assert._debug && msg ) print( "in assert for: " + msg );
 28 
 29     if ( b )
 30         return;
 31     
 32     doassert( "assert failed : " + msg );
 33 }
 34 
 35 assert._debug = false;
 36 
 37 assert.eq = function( a , b , msg ){
 38     if ( assert._debug && msg ) print( "in assert for: " + msg );
 39 
 40     if ( a == b )
 41         return;
 42 
 43     if ( ( a != null && b != null ) && friendlyEqual( a , b ) )
 44         return;
 45 
 46     doassert( "[" + tojson( a ) + "] != [" + tojson( b ) + "] are not equal : " + msg );
 47 }
 48 
 49 assert.neq = function( a , b , msg ){
 50     if ( assert._debug && msg ) print( "in assert for: " + msg );
 51     if ( a != b )
 52         return;
 53 
 54     doassert( "[" + a + "] != [" + b + "] are equal : " + msg );
 55 }
 56 
 57 assert.soon = function( f, msg, timeout, interval ) {
 58     if ( assert._debug && msg ) print( "in assert for: " + msg );
 59 
 60     var start = new Date();
 61     timeout = timeout || 30000;
 62     interval = interval || 200;
 63     var last;
 64     while( 1 ) {
 65         
 66         if ( typeof( f ) == "string" ){
 67             if ( eval( f ) )
 68                 return;
 69         }
 70         else {
 71             if ( f() )
 72                 return;
 73         }
 74         
 75         if ( ( new Date() ).getTime() - start.getTime() > timeout )
 76             doassert( "assert.soon failed: " + f + ", msg:" + msg );
 77         sleep( interval );
 78     }
 79 }
 80 
 81 assert.throws = function( func , params , msg ){
 82     if ( assert._debug && msg ) print( "in assert for: " + msg );
 83     try {
 84         func.apply( null , params );
 85     }
 86     catch ( e ){
 87         return e;
 88     }
 89 
 90     doassert( "did not throw exception: " + msg );
 91 }
 92 
 93 assert.commandWorked = function( res , msg ){
 94     if ( assert._debug && msg ) print( "in assert for: " + msg );
 95 
 96     if ( res.ok == 1 )
 97         return;
 98     
 99     doassert( "command failed: " + tojson( res ) + " : " + msg );
100 }
101 
102 assert.commandFailed = function( res , msg ){
103     if ( assert._debug && msg ) print( "in assert for: " + msg );
104 
105     if ( res.ok == 0 )
106         return;
107     
108     doassert( "command worked when it should have failed: " + tojson( res ) + " : " + msg );
109 }
110 
111 assert.isnull = function( what , msg ){
112     if ( assert._debug && msg ) print( "in assert for: " + msg );
113 
114     if ( what == null )
115         return;
116     
117     doassert( "supposed to null (" + ( msg || "" ) + ") was: " + tojson( what ) );
118 }
119 
120 assert.lt = function( a , b , msg ){
121     if ( assert._debug && msg ) print( "in assert for: " + msg );
122 
123     if ( a < b )
124         return;
125     doassert( a + " is not less than " + b + " : " + msg );
126 }
127 
128 assert.gt = function( a , b , msg ){
129     if ( assert._debug && msg ) print( "in assert for: " + msg );
130 
131     if ( a > b )
132         return;
133     doassert( a + " is not greater than " + b + " : " + msg );
134 }
135 
136 assert.close = function( a , b , msg , places ){
137     if (places === undefined) {
138         places = 4;
139     }
140     if (Math.round((a - b) * Math.pow(10, places)) === 0) {
141         return;
142     }
143     doassert( a + " is not equal to " + b + " within " + places +
144               " places, diff: " + (a-b) + " : " + msg );
145 };
146 
147 Object.extend = function( dst , src , deep ){
148     for ( var k in src ){
149         var v = src[k];
150         if ( deep && typeof(v) == "object" ){
151             v = Object.extend( typeof ( v.length ) == "number" ? [] : {} , v , true );
152         }
153         dst[k] = v;
154     }
155     return dst;
156 }
157 
158 argumentsToArray = function( a ){
159     var arr = [];
160     for ( var i=0; i<a.length; i++ )
161         arr[i] = a[i];
162     return arr;
163 }
164 
165 isString = function( x ){
166     return typeof( x ) == "string";
167 }
168 
169 isNumber = function(x){
170     return typeof( x ) == "number";
171 }
172 
173 isObject = function( x ){
174     return typeof( x ) == "object";
175 }
176 
177 String.prototype.trim = function() {
178     return this.replace(/^\s+|\s+$/g,"");
179 }
180 String.prototype.ltrim = function() {
181     return this.replace(/^\s+/,"");
182 }
183 String.prototype.rtrim = function() {
184     return this.replace(/\s+$/,"");
185 }
186 
187 Date.timeFunc = function( theFunc , numTimes ){
188 
189     var start = new Date();
190     
191     numTimes = numTimes || 1;
192     for ( var i=0; i<numTimes; i++ ){
193         theFunc.apply( null , argumentsToArray( arguments ).slice( 2 ) );
194     }
195 
196     return (new Date()).getTime() - start.getTime();
197 }
198 
199 Date.prototype.tojson = function(){
200     return "\"" + this.toString() + "\"";
201 }
202 
203 RegExp.prototype.tojson = RegExp.prototype.toString;
204 
205 Array.contains = function( a  , x ){
206     for ( var i=0; i<a.length; i++ ){
207         if ( a[i] == x )
208             return true;
209     }
210     return false;
211 }
212 
213 Array.unique = function( a ){
214     var u = [];
215     for ( var i=0; i<a.length; i++){
216         var o = a[i];
217         if ( ! Array.contains( u , o ) ){
218             u.push( o );
219         }
220     }
221     return u;
222 }
223 
224 Array.shuffle = function( arr ){
225     for ( var i=0; i<arr.length-1; i++ ){
226         var pos = i+Random.randInt(arr.length-i);
227         var save = arr[i];
228         arr[i] = arr[pos];
229         arr[pos] = save;
230     }
231     return arr;
232 }
233 
234 
235 Array.tojson = function( a , indent ){
236     if (!indent) 
237         indent = "";
238 
239     if (a.length == 0) {
240         return "[ ]";
241     }
242 
243     var s = "[\n";
244     indent += "\t";
245     for ( var i=0; i<a.length; i++){
246         s += indent + tojson( a[i], indent );
247         if ( i < a.length - 1 ){
248             s += ",\n";
249         }
250     }
251     if ( a.length == 0 ) {
252         s += indent;
253     }
254 
255     indent = indent.substring(1);
256     s += "\n"+indent+"]";
257     return s;
258 }
259 
260 Array.fetchRefs = function( arr , coll ){
261     var n = [];
262     for ( var i=0; i<arr.length; i ++){
263         var z = arr[i];
264         if ( coll && coll != z.getCollection() )
265             continue;
266         n.push( z.fetch() );
267     }
268     
269     return n;
270 }
271 
272 Array.sum = function( arr ){
273     if ( arr.length == 0 )
274         return null;
275     var s = arr[0];
276     for ( var i=1; i<arr.length; i++ )
277         s += arr[i];
278     return s;
279 }
280 
281 Array.avg = function( arr ){
282     if ( arr.length == 0 )
283         return null;
284     return Array.sum( arr ) / arr.length;
285 }
286 
287 Array.stdDev = function( arr ){
288     var avg = Array.avg( arr );
289     var sum = 0;
290 
291     for ( var i=0; i<arr.length; i++ ){
292         sum += Math.pow( arr[i] - avg , 2 );
293     }
294 
295     return Math.sqrt( sum / arr.length );
296 }
297 
298 Object.keySet = function( o ) {
299     var ret = new Array();
300     for( i in o ) {
301         if ( !( i in o.__proto__ && o[ i ] === o.__proto__[ i ] ) ) {
302             ret.push( i );
303         }
304     }
305     return ret;
306 }
307 
308 if ( ! ObjectId.prototype )
309     ObjectId.prototype = {}
310 
311 ObjectId.prototype.toString = function(){
312     return this.str;
313 }
314 
315 ObjectId.prototype.tojson = function(){
316     return "ObjectId(\"" + this.str + "\")";
317 }
318 
319 ObjectId.prototype.isObjectId = true;
320 
321 if ( typeof( DBPointer ) != "undefined" ){
322     DBPointer.prototype.fetch = function(){
323         assert( this.ns , "need a ns" );
324         assert( this.id , "need an id" );
325         
326         return db[ this.ns ].findOne( { _id : this.id } );
327     }
328     
329     DBPointer.prototype.tojson = function(indent){
330         return tojson({"ns" : this.ns, "id" : this.id}, indent);
331     }
332 
333     DBPointer.prototype.getCollection = function(){
334         return this.ns;
335     }
336     
337     DBPointer.prototype.toString = function(){
338         return "DBPointer " + this.ns + ":" + this.id;
339     }
340 }
341 else {
342     print( "warning: no DBPointer" );
343 }
344 
345 if ( typeof( DBRef ) != "undefined" ){
346     DBRef.prototype.fetch = function(){
347         assert( this.$ref , "need a ns" );
348         assert( this.$id , "need an id" );
349         
350         return db[ this.$ref ].findOne( { _id : this.$id } );
351     }
352     
353     DBRef.prototype.tojson = function(indent){
354         return tojson({"$ref" : this.$ref, "$id" : this.$id}, indent);
355     }
356 
357     DBRef.prototype.getCollection = function(){
358         return this.$ref;
359     }
360     
361     DBRef.prototype.toString = function(){
362         return this.tojson();
363     }
364 }
365 else {
366     print( "warning: no DBRef" );
367 }
368 
369 if ( typeof( BinData ) != "undefined" ){
370     BinData.prototype.tojson = function(){
371         return "BinData type: " + this.type + " len: " + this.len;
372     }
373 }
374 else {
375     print( "warning: no BinData" );
376 }
377 
378 if ( typeof _threadInject != "undefined" ){
379     print( "fork() available!" );
380     
381     Thread = function(){
382         this.init.apply( this, arguments );
383     }
384     _threadInject( Thread.prototype );
385     
386     ScopedThread = function() {
387         this.init.apply( this, arguments );
388     }
389     ScopedThread.prototype = new Thread( function() {} );
390     _scopedThreadInject( ScopedThread.prototype );
391     
392     fork = function() {
393         var t = new Thread( function() {} );
394         Thread.apply( t, arguments );
395         return t;
396     }    
397 
398     // Helper class to generate a list of events which may be executed by a ParallelTester
399     EventGenerator = function( me, collectionName, mean ) {
400         this.mean = mean;
401         this.events = new Array( me, collectionName );
402     }
403     
404     EventGenerator.prototype._add = function( action ) {
405         this.events.push( [ Random.genExp( this.mean ), action ] );
406     }
407     
408     EventGenerator.prototype.addInsert = function( obj ) {
409         this._add( "t.insert( " + tojson( obj ) + " )" );
410     }
411 
412     EventGenerator.prototype.addRemove = function( obj ) {
413         this._add( "t.remove( " + tojson( obj ) + " )" );
414     }
415 
416     EventGenerator.prototype.addUpdate = function( objOld, objNew ) {
417         this._add( "t.update( " + tojson( objOld ) + ", " + tojson( objNew ) + " )" );
418     }
419     
420     EventGenerator.prototype.addCheckCount = function( count, query, shouldPrint, checkQuery ) {
421         query = query || {};
422         shouldPrint = shouldPrint || false;
423         checkQuery = checkQuery || false;
424         var action = "assert.eq( " + count + ", t.count( " + tojson( query ) + " ) );"
425         if ( checkQuery ) {
426             action += " assert.eq( " + count + ", t.find( " + tojson( query ) + " ).toArray().length );"
427         }
428         if ( shouldPrint ) {
429             action += " print( me + ' ' + " + count + " );";
430         }
431         this._add( action );
432     }
433     
434     EventGenerator.prototype.getEvents = function() {
435         return this.events;
436     }
437     
438     EventGenerator.dispatch = function() {
439         var args = argumentsToArray( arguments );
440         var me = args.shift();
441         var collectionName = args.shift();
442         var m = new Mongo( db.getMongo().host );
443         var t = m.getDB( "test" )[ collectionName ];
444         for( var i in args ) {
445             sleep( args[ i ][ 0 ] );
446             eval( args[ i ][ 1 ] );
447         }
448     }
449     
450     // Helper class for running tests in parallel.  It assembles a set of tests
451     // and then calls assert.parallelests to run them.
452     ParallelTester = function() {
453         this.params = new Array();
454     }
455     
456     ParallelTester.prototype.add = function( fun, args ) {
457         args = args || [];
458         args.unshift( fun );
459         this.params.push( args );
460     }
461     
462     ParallelTester.prototype.run = function( msg, newScopes ) {
463         newScopes = newScopes || false;
464         assert.parallelTests( this.params, msg, newScopes );
465     }
466     
467     // creates lists of tests from jstests dir in a format suitable for use by
468     // ParallelTester.fileTester.  The lists will be in random order.
469     // n: number of lists to split these tests into
470     ParallelTester.createJstestsLists = function( n ) {
471         var params = new Array();
472         for( var i = 0; i < n; ++i ) {
473             params.push( [] );
474         }
475 
476         var makeKeys = function( a ) {
477             var ret = {};
478             for( var i in a ) {
479                 ret[ a[ i ] ] = 1;
480             }
481             return ret;
482         }
483         
484         // some tests can't run in parallel with most others
485         var skipTests = makeKeys( [ "jstests/dbadmin.js",
486                                    "jstests/repair.js",
487                                    "jstests/cursor8.js",
488                                    "jstests/recstore.js",
489                                    "jstests/extent.js",
490                                    "jstests/indexb.js",
491                                    "jstests/profile1.js",
492                                    "jstests/mr3.js",
493                                    "jstests/apitest_db.js"] );
494         
495         // some tests can't be run in parallel with each other
496         var serialTestsArr = [ "jstests/fsync.js",
497                               "jstests/fsync2.js" ];
498         var serialTests = makeKeys( serialTestsArr );
499         
500         params[ 0 ] = serialTestsArr;
501         
502         var files = listFiles("jstests");
503         files = Array.shuffle( files );
504         
505         var i = 0;
506         files.forEach(
507                       function(x) {
508                       
509                       if ( /_runner/.test(x.name) ||
510                           /_lodeRunner/.test(x.name) ||
511                           ( x.name in skipTests ) ||
512                           ( x.name in serialTests ) ||
513                           ! /\.js$/.test(x.name ) ){ 
514                       print(" >>>>>>>>>>>>>>> skipping " + x.name);
515                       return;
516                       }
517                       
518                       params[ i % n ].push( x.name );
519                       ++i;
520                       }
521         );
522         
523         // randomize ordering of the serialTests
524         params[ 0 ] = Array.shuffle( params[ 0 ] );
525         
526         for( var i in params ) {
527             params[ i ].unshift( i );
528         }
529         
530         return params;
531     }
532     
533     // runs a set of test files
534     // first argument is an identifier for this tester, remaining arguments are file names
535     ParallelTester.fileTester = function() {
536         var args = argumentsToArray( arguments );
537         var suite = args.shift();
538         args.forEach(
539                      function( x ) {
540                      print("         S" + suite + " Test : " + x + " ...");
541                      var time = Date.timeFunc( function() { load(x); }, 1);
542                      print("         S" + suite + " Test : " + x + " " + time + "ms" );
543                      }
544                      );        
545     }
546     
547     // params: array of arrays, each element of which consists of a function followed
548     // by zero or more arguments to that function.  Each function and its arguments will
549     // be called in a separate thread.
550     // msg: failure message
551     // newScopes: if true, each thread starts in a fresh scope
552     assert.parallelTests = function( params, msg, newScopes ) {
553         newScopes = newScopes || false;
554         var wrapper = function( fun, argv ) {
555                    eval (
556                          "var z = function() {" +
557                          "var __parallelTests__fun = " + fun.toString() + ";" +
558                          "var __parallelTests__argv = " + tojson( argv ) + ";" +
559                          "var __parallelTests__passed = false;" +
560                          "try {" +
561                             "__parallelTests__fun.apply( 0, __parallelTests__argv );" +
562                             "__parallelTests__passed = true;" +
563                          "} catch ( e ) {" +
564                             "print( e );" +
565                          "}" +
566                          "return __parallelTests__passed;" +
567                          "}"
568                          );
569             return z;
570         }
571         var runners = new Array();
572         for( var i in params ) {
573             var param = params[ i ];
574             var test = param.shift();
575             var t;
576             if ( newScopes )
577                 t = new ScopedThread( wrapper( test, param ) );
578             else
579                 t = new Thread( wrapper( test, param ) );
580             runners.push( t );
581         }
582         
583         runners.forEach( function( x ) { x.start(); } );
584         var nFailed = 0;
585         // v8 doesn't like it if we exit before all threads are joined (SERVER-529)
586         runners.forEach( function( x ) { if( !x.returnData() ) { ++nFailed; } } );        
587         assert.eq( 0, nFailed, msg );
588     }
589 }
590 
591 tojson = function( x, indent , nolint ){
592     if ( x === null )
593         return "null";
594     
595     if ( x === undefined )
596         return "undefined";
597     
598     if (!indent) 
599         indent = "";
600 
601     switch ( typeof x ){
602         
603     case "string": {
604         var s = "\"";
605         for ( var i=0; i<x.length; i++ ){
606             if ( x[i] == '"' ){
607                 s += "\\\"";
608             }
609             else
610                 s += x[i];
611         }
612         return s + "\"";
613     }
614         
615     case "number": 
616     case "boolean":
617         return "" + x;
618             
619     case "object":{
620         var s = tojsonObject( x, indent , nolint );
621         if ( ( nolint == null || nolint == true ) && s.length < 80 && ( indent == null || indent.length == 0 ) ){
622             s = s.replace( /[\s\r\n ]+/gm , " " );
623         }
624         return s;
625     }
626         
627     case "function":
628         return x.toString();
629         
630 
631     default:
632         throw "tojson can't handle type " + ( typeof x );
633     }
634     
635 }
636 
637 tojsonObject = function( x, indent , nolint ){
638     var lineEnding = nolint ? " " : "\n";
639     var tabSpace = nolint ? "" : "\t";
640     
641     assert.eq( ( typeof x ) , "object" , "tojsonObject needs object, not [" + ( typeof x ) + "]" );
642 
643     if (!indent) 
644         indent = "";
645     
646     if ( typeof( x.tojson ) == "function" && x.tojson != tojson ) {
647         return x.tojson(indent,nolint);
648     }
649     
650     if ( typeof( x.constructor.tojson ) == "function" && x.constructor.tojson != tojson ) {
651         return x.constructor.tojson( x, indent , nolint );
652     }
653 
654     if ( x.toString() == "[object MaxKey]" )
655         return "{ $maxKey : 1 }";
656     if ( x.toString() == "[object MinKey]" )
657         return "{ $minKey : 1 }";
658     
659     var s = "{" + lineEnding;
660 
661     // push one level of indent
662     indent += tabSpace;
663     
664     var total = 0;
665     for ( var k in x ) total++;
666     if ( total == 0 ) {
667         s += indent + lineEnding;
668     }
669 
670     var keys = x;
671     if ( typeof( x._simpleKeys ) == "function" )
672         keys = x._simpleKeys();
673     var num = 1;
674     for ( var k in keys ){
675         
676         var val = x[k];
677         if ( val == DB.prototype || val == DBCollection.prototype )
678             continue;
679 
680         s += indent + "\"" + k + "\" : " + tojson( val, indent , nolint );
681         if (num != total) {
682             s += ",";
683             num++;
684         }
685         s += lineEnding;
686     }
687 
688     // pop one level of indent
689     indent = indent.substring(1);
690     return s + indent + "}";
691 }
692 
693 shellPrint = function( x ){
694     it = x;
695     if ( x != undefined )
696         shellPrintHelper( x );
697     
698     if ( db ){
699         var e = db.getPrevError();
700         if ( e.err ) {
701 	    if( e.nPrev <= 1 )
702 		print( "error on last call: " + tojson( e.err ) );
703 	    else
704 		print( "an error " + tojson(e.err) + " occurred " + e.nPrev + " operations back in the command invocation" );
705         }
706         db.resetError();
707     }
708 }
709 
710 printjson = function(x){
711     print( tojson( x ) );
712 }
713 
714 shellPrintHelper = function( x ){
715 
716     if ( typeof( x ) == "undefined" ){
717 
718         if ( typeof( db ) != "undefined" && db.getLastError ){
719             var e = db.getLastError();
720             if ( e != null )
721                 print( e );
722         }
723 
724         return;
725     }
726     
727     if ( x == __magicNoPrint )
728         return;
729 
730     if ( x == null ){
731         print( "null" );
732         return;
733     }
734 
735     if ( typeof x != "object" ) 
736         return print( x );
737     
738     var p = x.shellPrint;
739     if ( typeof p == "function" )
740         return x.shellPrint();
741 
742     var p = x.tojson;
743     if ( typeof p == "function" )
744         print( x.tojson() );
745     else
746         print( tojson( x ) );
747 }
748 
749 shellHelper = function( command , rest , shouldPrint ){
750     command = command.trim();
751     var args = rest.trim().replace(/;$/,"").split( "\s+" );
752     
753     if ( ! shellHelper[command] )
754         throw "no command [" + command + "]";
755     
756     var res = shellHelper[command].apply( null , args );
757     if ( shouldPrint ){
758         shellPrintHelper( res );
759     }
760     return res;
761 }
762 
763 help = shellHelper.help = function(){
764     print( "HELP" );
765     print( "\t" + "show dbs                     show database names");
766     print( "\t" + "show collections             show collections in current database");
767     print( "\t" + "show users                   show users in current database");
768     print( "\t" + "show profile                 show most recent system.profile entries with time >= 1ms");
769     print( "\t" + "use <db name>                set curent database to <db name>" );
770     print( "\t" + "db.help()                    help on DB methods");
771     print( "\t" + "db.foo.help()                help on collection methods");
772     print( "\t" + "db.foo.find()                list objects in collection foo" );
773     print( "\t" + "db.foo.find( { a : 1 } )     list objects in foo where a == 1" );
774     print( "\t" + "it                           result of the last line evaluated; use to further iterate");
775 }
776 
777 shellHelper.use = function( dbname ){
778     db = db.getMongo().getDB( dbname );
779     print( "switched to db " + db.getName() );
780 }
781 
782 shellHelper.it = function(){
783     if ( typeof( ___it___ ) == "undefined" || ___it___ == null ){
784         print( "no cursor" );
785         return;
786     }
787     shellPrintHelper( ___it___ );
788 }
789 
790 shellHelper.show = function( what ){
791     assert( typeof what == "string" );
792     
793     if( what == "profile" ) { 
794 	if( db.system.profile.count() == 0 ) { 
795 	    print("db.system.profile is empty");
796 	    print("Use db.setProfilingLevel(2) will enable profiling");
797 	    print("Use db.system.profile.find() to show raw profile entries");
798 	} 
799 	else { 
800 	    print(); 
801 	    db.system.profile.find({ millis : { $gt : 0 } }).sort({$natural:-1}).limit(5).forEach( function(x){print(""+x.millis+"ms " + String(x.ts).substring(0,24)); print(x.info); print("\n");} )
802         }
803 	return "";
804     }
805 
806     if ( what == "users" ){
807 	db.system.users.find().forEach( printjson );
808         return "";
809     }
810 
811     if ( what == "collections" || what == "tables" ) {
812         db.getCollectionNames().forEach( function(x){print(x)} );
813 	return "";
814     }
815     
816     if ( what == "dbs" ) {
817         db.getMongo().getDBNames().sort().forEach( function(x){print(x)} );
818 	return "";
819     }
820     
821     throw "don't know how to show [" + what + "]";
822 
823 }
824 
825 if ( typeof( Map ) == "undefined" ){
826     Map = function(){
827         this._data = {};
828     }
829 }
830 
831 Map.hash = function( val ){
832     if ( ! val )
833         return val;
834 
835     switch ( typeof( val ) ){
836     case 'string':
837     case 'number':
838     case 'date':
839         return val.toString();
840     case 'object':
841     case 'array':
842         var s = "";
843         for ( var k in val ){
844             s += k + val[k];
845         }
846         return s;
847     }
848 
849     throw "can't hash : " + typeof( val );
850 }
851 
852 Map.prototype.put = function( key , value ){
853     var o = this._get( key );
854     var old = o.value;
855     o.value = value;
856     return old;
857 }
858 
859 Map.prototype.get = function( key ){
860     return this._get( key ).value;
861 }
862 
863 Map.prototype._get = function( key ){
864     var h = Map.hash( key );
865     var a = this._data[h];
866     if ( ! a ){
867         a = [];
868         this._data[h] = a;
869     }
870     
871     for ( var i=0; i<a.length; i++ ){
872         if ( friendlyEqual( key , a[i].key ) ){
873             return a[i];
874         }
875     }
876     var o = { key : key , value : null };
877     a.push( o );
878     return o;
879 }
880 
881 Map.prototype.values = function(){
882     var all = [];
883     for ( var k in this._data ){
884         this._data[k].forEach( function(z){ all.push( z.value ); } );
885     }
886     return all;
887 }
888 
889 if ( typeof( gc ) == "undefined" ){
890     gc = function(){
891     }
892 }
893    
894 
895 Math.sigFig = function( x , N ){
896     if ( ! N ){
897         N = 3;
898     }
899     var p = Math.pow( 10, N - Math.ceil( Math.log( Math.abs(x) ) / Math.log( 10 )) );
900     return Math.round(x*p)/p;
901 }
902 
903 Random = function() {}
904 
905 // set random seed
906 Random.srand = function( s ) { _srand( s ); }
907 
908 // random number 0 <= r < 1
909 Random.rand = function() { return _rand(); }
910 
911 // random integer 0 <= r < n
912 Random.randInt = function( n ) { return Math.floor( Random.rand() * n ); }
913 
914 Random.setRandomSeed = function( s ) {
915     s = s || new Date().getTime();
916     print( "setting random seed: " + s );
917     Random.srand( s );
918 }
919 
920 // generate a random value from the exponential distribution with the specified mean
921 Random.genExp = function( mean ) {
922     return -Math.log( Random.rand() ) * mean;
923 }
924 
925 killWithUris = function( uris ) {
926     var inprog = db.currentOp().inprog;
927     for( var u in uris ) {
928         for ( var i in inprog ) {
929             if ( uris[ u ] == inprog[ i ].client ) {
930                 db.killOp( inprog[ i ].opid );
931             }
932         }
933     }
934 }
935 
936 Geo = {};
937 Geo.distance = function( a , b ){
938     var ax = null;
939     var ay = null;
940     var bx = null;
941     var by = null;
942     
943     for ( var key in a ){
944         if ( ax == null )
945             ax = a[key];
946         else if ( ay == null )
947             ay = a[key];
948     }
949     
950     for ( var key in b ){
951         if ( bx == null )
952             bx = b[key];
953         else if ( by == null )
954             by = b[key];
955     }
956 
957     return Math.sqrt( Math.pow( by - ay , 2 ) + 
958                       Math.pow( bx - ax , 2 ) );
959 }
960