How to improve Javascript coding

Advantages

  1. Modern browsers are way faster to process JS.
  2. Trend – clients are doing what servers used to do
  3. Server is used only to generate data like json
  4. Client is used only to generate/render html
  5. Speed/efficient (no redundant data transfer between server client)

Disadvantage

  1. No compile time error checking
  2. Once a project becomes big, it is hard to upgrade libraries/framework
    without breaking something (at runtime)
  3. Not properly organized (global methods, big script file)

Solution

  1. Enclose functions in namespace
  2. Modularize code (this way big file can be split)
  3. “use strict” (reason)
  4. Write unit test (to solve compile time checking)
(function (nsr, $, undefined) {
“use strict”
     nsr.Module1 = nsr.Module1 || {};
     nsr.Module1.variable1 = 'test';
     nsr.Module1.function1 = function(){}
} (window.nsr = window.nsr || {}, jQuery));

Using and working with IndexedDB

Below is a sample example code for IndexedDB

//nsr is namespace
(function (nsr, $, undefined) {
    //private variables
    nsr.myTvQ.indexedDB = nsr.myTvQ.indexedDB || {};
    nsr.myTvQ.indexedDB.db = null;
    nsr.myTvQ.indexedDB.version = "1.0";
    
    //saving webkitIndexedDB in global namespace
    if ('webkitIndexedDB' in window) {
        window.indexedDB = window.webkitIndexedDB;
        window.IDBTransaction = window.webkitIDBTransaction;
        window.IDBKeyRange = window.webkitIDBKeyRange;
        window.IDBCursor = window.webkitIDBCursor;
    }
    //global error handler
    nsr.myTvQ.indexedDB.onerror = function (e) {
        console.log("myError: ", e);        
    };
    // callback param in integer, -1 error, 0 exiting, 1 new
    nsr.myTvQ.indexedDB.Open = function (callback) {
        if (nsr.myTvQ.indexedDB.db) {
            if (callback) {
                callback(0);//opened but present
            }
            return;
        }
        var request = indexedDB.open("myTvQDB", "comments bla blah database v1.0");

        request.onsuccess = function (e) {
            var v = nsr.myTvQ.indexedDB.version;
            nsr.myTvQ.indexedDB.db = e.target.result;
            var db = nsr.myTvQ.indexedDB.db;
            console.log('opened indexedDB myTvQDB v:' + db.version);            

            // We can only create Object stores in a setVersion transaction;
            // so if version not found update or create new db
            if (v != db.version) {
                console.log('setting new v ',v, db.version);
                var setVrequest = db.setVersion(v);

                // onsuccess is the only place we can create Object Stores
                setVrequest.onfailure = request.onfailure =  function (e) 
                {        
                    console.log("myError: ", e); 
                    if (callback) {                        
                        callback(-1);
                    }
                };
                setVrequest.onsuccess = function (e) {
                    //create this at last, since update_time is stored in end
                    nsr.myTvQ.indexedDB.DropObjectStore("settings");
                    nsr.myTvQ.indexedDB.CreateSettingsStore();

                    nsr.myTvQ.indexedDB.DropObjectStore("listings");
                    nsr.myTvQ.indexedDB.listing.CreateListingsStore();         
                    if (callback) {
                        callback(1);//created                        
                    }
                };
            } else {
                if (callback) {
                    callback(0);//opened but present
                }
                console.log('object stores are present');
            }
        };

        request.onfailure =  function (e) 
        {        
            console.log("myError: ", e); 
            if (callback) {
                callback(-1);//error
            }
        };
    }
    
    //helper methods
    nsr.myTvQ.indexedDB.Close = function(){
        try {
            var db = nsr.myTvQ.indexedDB.db;
            if (db) {
                db.close();
                nsr.myTvQ.indexedDB.db = null;
                if (callback) {
                    callback(1);
                }
            }   
        } catch (ex) {
            if (callback) {
                callback(-1);
            }
            console.log("Exception in Close() - " + ex.message);
        }
    } 
    nsr.myTvQ.indexedDB.DeleteDatabase = function(callback){
        try {
            var db = nsr.myTvQ.indexedDB.db;
            if (db) {
                db.close();
            }   
            var request =  window.indexedDB.deleteDatabase('myTvQDB');
            request.onsuccess = function (e) {
                nsr.myTvQ.indexedDB.db = null;
                if (callback) {
                    callback(1);
                }
                console.log('Database successfully deleted');
            };
            request.onfailure = function (e) 
            {        
                console.log("myError: ", e); 
                if (callback) {
                    callback(-1);
                }
            };    
        } catch (ex) {
            if (callback) {
                callback(-1);
            }
            console.log("Exception in DeleteDatabase() - " + ex.message);
        }
    }
    
    nsr.myTvQ.indexedDB.DropObjectStore = function (storeName) {
        var db = nsr.myTvQ.indexedDB.db;
        if (db.objectStoreNames.contains(storeName)) {
            db.deleteObjectStore(storeName);
            console.log('objectstore ' + storeName + ' dropped');
        }
    }

    nsr.myTvQ.indexedDB.ForceDropObjectStore = function (storeName) {
        var db = nsr.myTvQ.indexedDB.db;
        var setVrequest = db.setVersion(Math.random());
        setVrequest.onfailure = nsr.myTvQ.indexedDB.onerror;
        setVrequest.onsuccess = function (e) {
            console.log('db.version ', db.version);
            nsr.myTvQ.indexedDB.DropObjectStore(storeName);
        }
    };
    
    //can use to re-create empty ObjectStore
    nsr.myTvQ.indexedDB.ForceCreateObjectStore = function (storeName) {
        console.log('ForceCreateObjectStore', storeName);
        var db = nsr.myTvQ.indexedDB.db;
        var setVrequest = db.setVersion(Math.random());
        setVrequest.onfailure = nsr.myTvQ.indexedDB.onerror;
        setVrequest.onsuccess = function (e) {
            console.log('db.version ', db.version);
            nsr.myTvQ.indexedDB.DropObjectStore(storeName);
            switch(storeName){
                case "listings":
                    nsr.myTvQ.indexedDB.listing.CreateListingsStore();
                    break;	
                case "settings":
                    nsr.myTvQ.indexedDB.listing.CreateSettingsStore();
                    break;	   
                default:
                    break;
                }
            nsr.myTvQ.indexedDB.ResetVersion();
        }
    };
    
    //set version back to old version
    nsr.myTvQ.indexedDB.ResetVersion = function () {
        var db = nsr.myTvQ.indexedDB.db;
        var setVrequest = db.setVersion(nsr.myTvQ.indexedDB.version);
        setVrequest.onfailure = nsr.myTvQ.indexedDB.onerror;
        setVrequest.onsuccess = function (e) {
            console.log('reset db.version ', db.version);
        }
    };
    
    //empty or truncate table
    nsr.myTvQ.indexedDB.ClearAll = function(storeName, callback){
        var db = nsr.myTvQ.indexedDB.db;
        if (db.objectStoreNames.contains(storeName)) {
            var clearTransaction = db.transaction([storeName], IDBTransaction.READ_WRITE);
            var clearRequest = clearTransaction.objectStore(storeName).clear();
            clearRequest.onsuccess = function(event){
                 //event.target.result contains undefined
                 if (callback) {
                    callback(true);
                 }                 
            };
            clearRequest.onerror = function(event){            
                 console.log("myError: ", event);
                 if (callback) {
                    callback(false);
                 }                                  
            };
        }else {
            console.log(storeName+ " doesnot exist ");
            if (callback) {
                    callback(false);
            }                 
        }
    }
    //nsr.myTvQ.indexedDB.ListAll('','store_id',function(l1){console.log(l1);});
    nsr.myTvQ.indexedDB.ListAll = function(storeName, objOrArray, callback){
        var db = nsr.myTvQ.indexedDB.db;
        if (db.objectStoreNames.contains(storeName)) {
            var transaction = db.transaction(storeName, IDBTransaction.READ_ONLY);
            var list = objOrArray ? {} : [] ;
            transaction.oncomplete = function(event) {
                if(callback){
                    //console.log(results);
                    callback(list);
                }
            };
            transaction.onerror = nsr.myTvQ.indexedDB.onerror;
            var cursorRequest = transaction.objectStore(storeName).openCursor();
            
            cursorRequest.onsuccess = function(ev) {
                var cursor = cursorRequest.result || ev.target.result;
                if(!!cursor == false)
                    return;
                if(objOrArray){
                    list[cursor.value[objOrArray]] = cursor.value;
                }
                else{
                    list.push(cursor.value);                
                }
                cursor.continue();
            };      
        }else {
            console.log(storeName+ " doesnot exist ");
            if (callback) {
                    callback(false);
            }                 
        }            
    }
    
    nsr.myTvQ.indexedDB.Delete = function(storeName, key, callback){
        var db = nsr.myTvQ.indexedDB.db;
        var request = db.transaction(storeName, IDBTransaction.READ_WRITE)  
                .objectStore(storeName)  
                .delete(key);  
        request.onsuccess = function(event) {  
          if(callback){
                //console.log(results);
                callback(true);
            }  
        };
        request.onerror = function(event) {  
          if(callback){
                callback(false);
            }  
        };
    }
    
    nsr.myTvQ.indexedDB.CreateSettingsStore = function(callback){
        var db = nsr.myTvQ.indexedDB.db;
        if(!db.objectStoreNames.contains("settings")) {
            //will be used by background to get all past dates and run operations on them
            db.createObjectStore("settings",{keyPath:"Name"});
            console.log('objectstore settings created');
                        
            nsr.myTvQ.indexedDB.SetAllSettings(undefined,callback);
        }
    };
    
    
    
    nsr.myTvQ.indexedDB.GetAllSettings = function(callback){
        var db = nsr.myTvQ.indexedDB.db;        
        var settings = {};
        var transaction = db.transaction(["settings"], IDBTransaction.READ_ONLY);
        transaction.oncomplete = function(event) {  
          if(callback){
            callback(settings);
          }
        }; 
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("settings");
        var cursorRequest = store.openCursor(IDBKeyRange.lowerBound(0));
        cursorRequest.onsuccess = function(ev) {
            var cursor = cursorRequest.result || ev.target.result;
            if(!!cursor == false)
                return;
            //console.log(cursor.value);
            settings[cursor.value.Name] = cursor.value.Value;
            cursor.continue();
        };          
    }

    nsr.myTvQ.indexedDB.SetAllSettings = function(settings, callback){
         var db = nsr.myTvQ.indexedDB.db; 
         var transaction = db.transaction(["settings"], IDBTransaction.READ_WRITE);
         var store = transaction.objectStore("settings");
         transaction.oncomplete = function(event) {  
            if(callback){
                console.log('SetAllSettings Initialized');
                callback();
            }
         }; 
         transaction.onerror = nsr.myTvQ.indexedDB.onerror;
         settings = settings || {};
         var init_list = [{"Name":"update_time","Value":settings.update_time || (new Date()).getTime() }                          
                            ,{"Name":"enable_notification","Value":settings.enable_notification || 1}
                            ,{"Name":"default_country","Value":settings.default_country || 'US'}];

         $.each(init_list, function (index0, item0) {
                var request = store.put(item0);
                request.onsuccess = function(event) {};
                request.onerror = function(event) {
                    console.log(event);
                }; 
         });
    }

    nsr.myTvQ.indexedDB.GetSettingsFor = function(name, callback){
        var db = nsr.myTvQ.indexedDB.db;
        var resultset = null;
        var transaction = db.transaction(["settings"], IDBTransaction.READ_ONLY);
        transaction.oncomplete = function(event) {  
          if(callback){
            callback(resultset);
          }
        }; 
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("settings");
        var request = store.get(name);  
        request.onerror = function(event) {  
          console.log(event);
        };  
        request.onsuccess = function(event) {  
          resultset = request.result ? request.result.Value : undefined ;  
        };
    }

    nsr.myTvQ.indexedDB.SetSettingsFor = function(name, value, callback){
        var db = nsr.myTvQ.indexedDB.db;
        var transaction = db.transaction(["settings"], IDBTransaction.READ_WRITE);
        transaction.oncomplete = function(event) {  
          if(callback){
            callback(true);
          }
        }; 
        transaction.onerror = function(event) {  
          if(callback){
            callback(false);
          }
        }; 
        var store = transaction.objectStore("settings");
        var request = store.put({"Name":name,"Value":value});  
        request.onerror = function(event) {  
          console.log(event);
        };  
        request.onsuccess = function(event) {};
    }    
    
    ///Show Listings
    nsr.myTvQ.indexedDB.listing.CreateListingsStore = function(callback){
        var db = nsr.myTvQ.indexedDB.db;
        if(!db.objectStoreNames.contains("listings")) {
            var store = db.createObjectStore("listings", { keyPath: "show_id" });
            store.createIndex("countryIndex", "country", { unique: false });
            store.createIndex("country_statusIndex", "country_status", { unique: false });
            store.createIndex("show_name_Index", "show_name", { unique: false });
            console.log('objectstore listing created');  
            if(callback){
                callback(true);
            }          
        }else {
            if(callback){
                console.log('CreateListingsStore already exits');
                callback(true);
            }
        }
    };
    
    nsr.myTvQ.indexedDB.listing.AddListings = function (x2j_list_new, index, callback) {
        //no need to clear, since its add or update.        
        var db = nsr.myTvQ.indexedDB.db;
        var transaction = db.transaction(["listings"], IDBTransaction.READ_WRITE);
        var count = 0;
        transaction.oncomplete = function (event) {
            if (callback) {
                console.log('x2jShowListing Added ' + count + '/' + x2j_list_new.length);
                callback([index, count, x2j_list_new.length]);
            }
        };
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("listings");
        console.log('AddListings looping'+index);
        $.each(x2j_list_new, function (index0, item0) {
            var request = store.put(item0);
            request.onsuccess = function (event) {
                count++;
                // event.target.result == show_id  
            };
        });        
    };

    nsr.myTvQ.listing.AddListingsInChunks = function (progressElemId, callback) {
        $(progressElemId).html('(Listing is older than 14 days, fetching latest from server)');
        nsr.myTvQ.listing.GetListingsFromTvRage(function (x2j_list_new) {
            if (!x2j_list_new) {
                if (callback) {
                    $(progressElemId).html('(Encountered Error while fetching, please try again later.)');
                    callback(false);
                    return;
                }
            }
            console.log('x2j_list_new now calling AddListings');
            
            //http://stackoverflow.com/questions/8495687/split-array-into-chunks      
            var i = 0, j = x2j_list_new.length, temp_array, chunk = 500;
            $(progressElemId).html('(Got a total of '+j+' Shows)');
            //http://www.kryogenix.org/days/2009/07/03/not-blocking-the-ui-in-tight-javascript-loops
            function doWork() {
                temp_array = x2j_list_new.slice(i, i + chunk);
                nsr.myTvQ.indexedDB.listing.AddListings(temp_array, i, function (cnt) {
                    if (cnt[0] >= j - chunk) {
                        console.log('AddListings ALL DONE', cnt);
                        if (callback) {
                            $(progressElemId).html('(Saving Shows locally 100% Comlepete. Now generating screen. Almost there... )');
                            callback(true);
                            return;
                        }
                    }
                    $(progressElemId).html('(Saving '+j+' Shows locally, '+(((i + chunk >=j ? j: i + chunk)/j)*100).toFixed(2) +'% Done.)');
                    console.log(cnt);
                });
                i += chunk;
                if (i < j) {
                    setTimeout(doWork, 100);
                }
            };
            setTimeout(doWork, 100);            
        });
    } 
    
    nsr.myTvQ.indexedDB.listing.GetAllCountries = function (callback) {
        var db = nsr.myTvQ.indexedDB.db;
        var resultset = [];
        var transaction = db.transaction(["listings"], IDBTransaction.READ_ONLY);
        transaction.oncomplete = function(event) {  
          if(callback){
            callback(resultset);
          }
        }; 
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("listings");

        var countryIndex = store.index("countryIndex");
        
        var cursorRequest = countryIndex.openKeyCursor(null, IDBCursor.NEXT_NO_DUPLICATE);
        cursorRequest.onsuccess = function(ev) {
          var cursor = cursorRequest.result;
          if (cursor) {
            resultset.push(cursor.key);
            //console.log(cursor.value);//undefined
            cursor.continue();
          }
        };
    };    

    nsr.myTvQ.indexedDB.listing.GetListingByCountry = function (country, callback) {
        console.log("GetListingBy country");
        var resultset = [];
        var db = nsr.myTvQ.indexedDB.db;
        var transaction = db.transaction(["listings"], IDBTransaction.READ_ONLY);
        transaction.oncomplete = function(event) {  
            if(callback){
            callback(resultset);
            }
        }; 
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("listings");

        var countryIndex = store.index("countryIndex");
            
        var cursorRequest = countryIndex.openCursor(new IDBKeyRange.only(country));
        cursorRequest.onsuccess = function(ev) {
            var cursor = cursorRequest.result;
            if (cursor) {
            resultset.push(cursor.value);
                
            cursor.continue();
            }
        };
    };

    nsr.myTvQ.indexedDB.listing.GetListingByCountryStatus = function (country, status, callback) {
        var resultset = [];
        var db = nsr.myTvQ.indexedDB.db;
        var transaction = db.transaction(["listings"], IDBTransaction.READ_ONLY);
        transaction.oncomplete = function(event) {  
          if(callback){
            callback(resultset);
          }
        }; 
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("listings");

        var country_statusIndex = store.index("country_statusIndex");
        var cursorRequest = country_statusIndex.openCursor(new IDBKeyRange.only(country +'_'+ status));
        cursorRequest.onsuccess = function(ev) {
          var cursor = cursorRequest.result;
          if (cursor) {
            resultset.push(cursor.value);
            //console.log(cursor.value);
            cursor.continue();
          }          
        };
    };
    
    //from ="A", to="B"
    nsr.myTvQ.indexedDB.listing.GetCountryByRange = function (from, to) {
        var db = nsr.myTvQ.indexedDB.db;
        var resultset = [];
        var transaction = db.transaction(["listings"], IDBTransaction.READ_ONLY);
        transaction.oncomplete = function(event) {  
            console.log(resultset);
        }; 
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("listings");

        var countryIndex = store.index("countryIndex");
        
        var cursorRequest = countryIndex.openKeyCursor(IDBKeyRange.bound(from,to,false,true), IDBCursor.NEXT_NO_DUPLICATE);
        cursorRequest.onsuccess = function(ev) {
          var cursor = cursorRequest.result;
          if (cursor) {
            resultset.push(cursor.key);
            //console.log(cursor.value);//undefined
            cursor.continue();
          }
        };
    };
    
    //chrome doesnot support count yet.
    nsr.myTvQ.indexedDB.listing.CountListing = function (callback) {        
        var db = nsr.myTvQ.indexedDB.db;
        var transaction = db.transaction(["listings"], IDBTransaction.READ_ONLY);
        var count = 0;
        transaction.oncomplete = function(event) {  
          if(callback){
            callback(count);
          }
        };
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("listings");

        var keyRange = IDBKeyRange.lowerBound(0);
        var showidRequest  = store.openCursor(keyRange);
        showidRequest.onsuccess = function(ev) {
          var result = ev.target.result;
          if(!!result == false)
            return;
                      
            count++;
            result.continue();          
        };
    }

    nsr.myTvQ.indexedDB.listing.CountListingBy = function (country, callback) {        
        var db = nsr.myTvQ.indexedDB.db;
        var transaction = db.transaction(["listings"], IDBTransaction.READ_ONLY);
        var count=0;
        transaction.oncomplete = function(event) {  
          if(callback){
            //console.log(count);
            callback(count);
          }
        };
        transaction.onerror = nsr.myTvQ.indexedDB.onerror;
        var store = transaction.objectStore("listings");

        var countryIndex = store.index("countryIndex");
        
        var cursorRequest = countryIndex.openCursor(new IDBKeyRange.only(country));
        cursorRequest.onsuccess = function(ev) {
          var cursor = cursorRequest.result;
          if(!!cursor == false)
            return;

          count++;
          cursor.continue();
        };        
    }                           

} (window.nsr = window.nsr || {}, jQuery));

Convert xml to json or JavaScript object

For my Google chrome extension TV WatchList, I needed to save TVRage.com’s xml feed in indexedDB. Since it only takes json object,I decided to make a small library which can convert xml to JavaScript xml2json. Existing solutions did not work for me due various reasons like node with url was giving me blank values, console.log would throw circular exeptions during debugging, and couple of other things i dont remember right now.

This library is 100% javascript with no third party library dependency.
Also This is only tested in chrome browser.This should work in other browsers too (maybe with minor tweaks)
Anyway lets start with some code

Sample XML – shows.xml

<?xml version="1.0" encoding="UTF-8" ?>
<Results>
    <show>
        <name>One Piece (JP)</name>
        <link>http://www.tvrage.com/One_Piece_JP</link>
        <country>JP</country>
        <started>Oct/20/1999</started>
        <ended></ended>
        <genres><genre>Anime</genre><genre>Action</genre><genre>Adventure</genre><genre>Comedy</genre><genre>Fantasy</genre></genres>
        <network country="JP">Fuji TV</network>
        <akas><aka country="JP">&#12527;&#12531;&#12500;&#12540;&#12473;</aka></akas>
    </show>
    <show>
        <name>One Piece (US)</name>
        <link>http://www.tvrage.com/One_Piece_US</link>
        <country>AJ</country>
        <started>Sep/18/2004</started>
        <ended>Mar/15/2008</ended>
        <genres><genre>Anime</genre><genre>Children</genre></genres>
        <network country="US">Cartoon Network</network>
    </show>
</Results>

Usage:

    var x2jObj = null;
    $.get('shows.xml', function (xmlDocument) {
        x2jObj = X2J.parseXml(xmlDocument); //X2J.parseXml(xmlDocument, '/');
        //x2jObj is called jNode
        console.log('x2jObj populated', x2jObj);
        //...
    });

Read More

To make a calculator from scratch

Near May 2011, as a personal challenge, I made a very basic calculator from scratch using C#.NET. My goals were to avoid any CLR’s arithmetic operations and integers, And to only use memory.

I first came up with a very basic counter, this was the building block for Addition, Which in turn was the building block for Multiplication. I actually have two version of addition and multiplication. The second version has better performance as it caches intermediary results.

One of the cool side effect of using this technique is the results can be bigger than Integer’s Max value.
For example it can easily calculate
(5492012742638447789741521173787972956681294295296 * 22867376956627844231881057173787972956681294295296)
and return full result without the E Notation :)

partial class FastCalculator
{
    static LinkedList<Char> numbers = new LinkedList<Char>(new Char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' });
    static Dictionary<Char, LinkedListNode<Char>> fastNumbers = new Dictionary<Char,LinkedListNode<Char>>();
    static Dictionary<string, LinkedList<char>> operationCache = new Dictionary<string, LinkedList<char>>();
 
    static FastCalculator()
    {
        LinkedListNode<Char> zero = numbers.First;
        while (zero != null)
        {
            fastNumbers[zero.Value] = zero;
            zero = zero.Next;
        }
    }

    static void Main(string[] args)
    {
        try
        {
            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();
    //        System.Diagnostics.Debug.WriteLine(0 + " PlusOne is " + PlusOne("0"));
    //        System.Diagnostics.Debug.WriteLine(5 + " PlusOne is " + PlusOne("5"));
    //        System.Diagnostics.Debug.WriteLine(9 + " PlusOne is " + PlusOne("9"));
    //        System.Diagnostics.Debug.WriteLine(10 + " PlusOne is " + PlusOne("10"));
    //        System.Diagnostics.Debug.WriteLine(999 + " PlusOne is " + PlusOne("999"));
    //        System.Diagnostics.Debug.WriteLine(256 + " PlusOne is " + PlusOne("256"));
    //        System.Diagnostics.Debug.WriteLine("Multiply 999*256 =" + Multiply("999", "256"));
            System.Diagnostics.Debug.WriteLine(MultiplyVer2("5492012742638447789741521173787972956681294295296", "22867376956627844231881057173787972956681294295296"));
            watch.Stop();
            TimeSpan ts = watch.Elapsed;
            string elapsedTime = String.Format("{0:00}h:{1:00}m:{2:00}s.{3:00}ms",
                                    ts.Hours, ts.Minutes, ts.Seconds,
                                    ts.Milliseconds / 10);
            System.Diagnostics.Debug.WriteLine("elapsedTime " + elapsedTime);
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
        }
    }

    static string PlusOne(string num)
    {
        if (!string.IsNullOrEmpty(num))
        {
            LinkedList<Char> input = new LinkedList<Char>(num.ToCharArray());
            if (input.Last.Value != numbers.Last.Value)// not 9
            {
                input.Last.Value = fastNumbers[input.Last.Value].Next.Value;
                return LinkedListToString(input);
            }
            else // if 9
            {
                LinkedListNode<Char> in_last = input.Last;
                bool doCarryOver = true;
                while (in_last != null)
                {
                    if (in_last.Value == numbers.Last.Value)
                    {
                        if (doCarryOver)
                        {
                            in_last.Value = numbers.First.Value;
                            doCarryOver = true;
                        }
                    }
                    else
                    {
                        if (doCarryOver)
                        {
                            in_last.Value = fastNumbers[in_last.Value].Next.Value;
                            doCarryOver = false;
                        }                            
                    }
                    in_last = in_last.Previous;
                }
                if (doCarryOver)
                {
                    input.AddFirst(numbers.First.Next.Value);//1
                }
                return LinkedListToString(input);
            }            
        }

        return "0";
    }

    static string Add(string left, string right)
    {
        string zero = numbers.First.Value.ToString();
        if (string.IsNullOrEmpty(left) && string.IsNullOrEmpty(right))
        {
            return zero;
        }

        while (zero != right)
        {
            left = PlusOne(left);
            zero = PlusOne(zero);
        }
        return left;
    }

    static string AddVer2(string left, string right)
    {
        string zero = numbers.First.Value.ToString();
        if (string.IsNullOrEmpty(left))
        {
            left = zero;
        }

        if (string.IsNullOrEmpty(right))
        {
            right = zero;
        }

        if (LengthLessThan(left, right)) //(left.Length < right.Length),make sure left is always greater
        {
            string tmp = left;
            left = right;
            right = tmp;
        }

        LinkedList<Char> rightIn = new LinkedList<Char>(right.ToCharArray());
        LinkedList<Char> leftIn = new LinkedList<Char>(left.ToCharArray());
        var leftInLast = leftIn.Last;
        var rightInLast = rightIn.Last;
        bool doCarryOver = false;

        while (leftInLast != null)
        {
            if (rightInLast != null)
            {
                LinkedList<Char> add = AddSymbols(leftInLast.Value, rightInLast.Value);
                if (doCarryOver)
                {   //since carry is always 1, we will do PlusOne
                    add = new LinkedList<Char>(PlusOne(LinkedListToString(add)).ToCharArray());
                }
                leftInLast.Value = add.Last.Value;
                if (add.Last.Previous != null)
                {
                    doCarryOver = true;
                }
                else
                {
                    doCarryOver = false;
                }
                rightInLast = rightInLast.Previous;
            }
            else
            {
                LinkedList<Char> add = AddSymbols(leftInLast.Value, numbers.First.Value);
                if (doCarryOver)
                {   //since carry is always 1, we will do PlusOne
                    add = new LinkedList<Char>(PlusOne(LinkedListToString(add)).ToCharArray());
                }
                leftInLast.Value = add.Last.Value;
                if (add.Last.Previous != null)
                {
                    doCarryOver = true;
                }
                else
                {
                    doCarryOver = false;
                }
            }

            leftInLast = leftInLast.Previous;
        }
        if (doCarryOver)
        {
            leftIn.AddFirst(numbers.First.Next.Value);//1
        }
        return LinkedListToString(leftIn);
    }
    
    static string Multiply(string left, string right)
    {
        string zero = numbers.First.Value.ToString();
        if (string.IsNullOrEmpty(left) && string.IsNullOrEmpty(right))
        {
            return zero;
        }

        string rtn = zero;

        while (zero != right)
        {
            rtn = Add(rtn, left);
            zero = PlusOne(zero);
        }

        return rtn;
    }
    
    static string MultiplyVer2(string left, string right)
    {
        string zero = numbers.First.Value.ToString();
        if (string.IsNullOrEmpty(left) || string.IsNullOrEmpty(right))
        {
            return zero;
        }

        if (LengthLessThan(left, right))//(left.length < right.length) make sure left is always greater
        {
            var tmp = left;
            left = right;
            right = tmp;
            System.Diagnostics.Debug.WriteLine("swapped");
        }

        string rtn = zero;
        LinkedList<Char> rightIn = new LinkedList<Char>(right.ToCharArray());
        LinkedList<Char> leftIn = new LinkedList<Char>(left.ToCharArray());
        LinkedList<string> result2sum = new LinkedList<string>();
        var rightInLast = rightIn.Last;
        while (rightInLast != null)
        {
            var leftInLast = leftIn.Last;
            System.Diagnostics.Debug.WriteLine("right symbol " + rightInLast.Value);
            LinkedList<Char> temp = new LinkedList<char>();
            String carry = string.Empty;
            while (leftInLast != null)
            {
                System.Diagnostics.Debug.WriteLine("left symbol " + leftInLast.Value);
                LinkedList<Char> mult = MultiplySymbols(leftInLast.Value, rightInLast.Value);
                if (!string.IsNullOrEmpty(carry))
                {
                    mult = new LinkedList<Char>(AddVer2(LinkedListToString(mult), carry).ToCharArray());
                    carry = string.Empty;
                }
                temp.AddFirst(mult.Last.Value);
                if (mult.Last.Previous != null)
                {
                    carry = mult.Last.Previous.Value.ToString();
                }

                leftInLast = leftInLast.Previous;
            }
            if (!string.IsNullOrEmpty(carry))
            {
                temp.AddFirst(Convert.ToChar(carry));
            }
            result2sum.AddFirst(LinkedListToString(temp));
            rightInLast = rightInLast.Previous;
        }
        var result = result2sum.Last;
        string sum = numbers.First.Value.ToString();//0
        string sumShift = string.Empty;
        while (result != null)
        {
            //string r = result.Value;
            System.Diagnostics.Debug.WriteLine("sum " + sum + " with " + result.Value + sumShift);
            sum = AddVer2(sum, result.Value + sumShift);
            sumShift = sumShift + numbers.First.Value.ToString();
            result = result.Previous;
        }
        return sum;
    }

    static LinkedList<char> AddSymbols(char left, char right)
    {
        string inputStr = left + "+" + right;
        if (operationCache.ContainsKey(inputStr))
        {
            return operationCache[inputStr];
        }

        string zero = numbers.First.Value.ToString();
        string rightStr = right.ToString();
        string leftStr = left.ToString();
        while (zero != rightStr)
        {
            leftStr = PlusOne(leftStr);
            zero = PlusOne(zero);
        }
        operationCache[inputStr] = new LinkedList<Char>(leftStr.ToCharArray());
        return operationCache[inputStr];
    }
    
    static LinkedList<char> MultiplySymbols(char left, char right)
    {
        string inputStr = left + "*" + right;
        if (operationCache.ContainsKey(inputStr))
        {
            return operationCache[inputStr];
        }
        string zero = numbers.First.Value.ToString();
        string rightStr = right.ToString();
        string leftStr = left.ToString();
        string rtn = zero;
        while (zero != rightStr)
        {
            rtn = AddVer2(rtn, leftStr);
            zero = PlusOne(zero);
        }

        operationCache[inputStr] = new LinkedList<Char>(rtn.ToCharArray());
        return operationCache[inputStr];
    }

    static bool LengthLessThan(string left, string right)
    {
        LinkedList<Char> rightIn = new LinkedList<Char>(right.ToCharArray());
        LinkedList<Char> leftIn = new LinkedList<Char>(left.ToCharArray());
        var leftInLast = leftIn.Last;
        var rightInLast = rightIn.Last;
        while (leftInLast != null && rightInLast != null)
        {
            leftInLast = leftInLast.Previous;
            rightInLast = rightInLast.Previous;
        }
        if (leftInLast == null && rightInLast == null)
        {
            return false;
        }

        if (leftInLast != null && rightInLast == null)
        {
            return false;
        }

        if (leftInLast == null && rightInLast != null)
        {
            return true;
        }

        return false;
    }

    private static string LinkedListToString(LinkedList<Char> num)
    {
        StringBuilder sb = new StringBuilder();
        if (num != null)
        {
            LinkedListNode<Char> first = num.First;
            while (first != null)
            {
                sb.Append(first.Value);
                first = first.Next;
            }
        }

        return sb.ToString();
    }  
}

http://codereview.stackexchange.com/questions/2334/to-make-a-calculator-from-scratch

VisualSVN pre-commit hook

1.Start VisualSVN Server Manager
2.Under Repositories, Select your repository and then Open Properties
3.Select Hooks tab
4.double click the pre-commit hook

Edit: As of VisualSVN version 2.5 use below code

setlocal enabledelayedexpansion

set REPOS=%1
set TXN=%2

set SVNLOOK="%VISUALSVN_SERVER%\bin\svnlook.exe"

SET M=

REM Concatenate all the lines in the commit message
FOR /F "usebackq delims==" %%g IN (`%SVNLOOK% log -t %TXN% %REPOS%`) DO SET M=!M!%%g

REM Make sure M is defined
SET M=0%M%

REM Here the 6 is the length we require
IF NOT "%M:~6,1%"=="" goto NORMAL_EXIT

:ERROR_TOO_SHORT
echo "Commit note must be at least 6 letters" >&2
goto ERROR_EXIT

:ERROR_EXIT
exit /b 1

REM All checks passed, so allow the commit.
:NORMAL_EXIT
exit 0

If using VisualSVN < 2.5

setlocal

set REPOS=%1
set TXN=%2

set SVNLOOK=”C:\Program Files\VisualSVN Server\bin\svnlook.exe”

REM Make sure that the log message contains some text.
FOR /F “usebackq delims==” %%g IN (`%SVNLOOK% log -t %TXN% %REPOS% FINDSTR /R /C:……`) DO goto NORMAL_EXIT

:ERROR_TOO_SHORT
echo “Comment must be at least 6 letters” >&2
goto ERROR_EXIT

:ERROR_EXIT
exit /b 1

REM All checks passed, so allow the commit.
:NORMAL_EXIT
exit 0

The above code will make the comment of at least 6 letters mandatory.