String.prototype.toTitleCase = function() {
    var i, str, lowers, uppers;
    str = this.replace(/\w\S*/g, function(txt) {
        return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
    });

    // Certain minor words should be left lowercase unless
    // they are the first or last words in the string
    lowers = ['A', 'An', 'The', 'And', 'But', 'Or', 'For', 'Nor', 'As', 'At',
    'By', 'For', 'From', 'In', 'Into', 'Near', 'Of', 'On', 'Onto', 'To', 'With'];
    for (i = 0; i < lowers.length; i++)
        str = str.replace(new RegExp('\\s' + lowers[i] + '\\s', 'g'),
            function(txt) {
                return txt.toLowerCase();
            });

    // Certain words such as initialisms or acronyms should be left uppercase
    uppers = ['Id'];
    for (i = 0; i < uppers.length; i++)
        str = str.replace(new RegExp('\\b' + uppers[i] + '\\b', 'g'),
            uppers[i].toUpperCase());

    return str;
};

 //
//     Gene and GeneList contructors and methods



function Gene(index, lr, pv, fc, id, symbol, name, desc, location, proFamily, evidenceType) {
    this.logRatio = parseFloat(lr);
    this.pValue = parseFloat(pv);
    this.foldChange = Math.round(parseFloat(fc) * 1000) / 1000; // three signficant digits
    this.id = id;
    this.rank = 0;
    this.symbol = symbol;
    this.name = name.toTitleCase();
    this.name = name;    
    this.location = location;
    this.topic = "genes";
    this.topicLabel = "gene";
    // Commented out becasue of name conflict
    // this.topicArray = genes;
    this.desc = this.name + ": " + desc;
    this.proFamily = proFamily;
    this.parentId = null;
    this.pathways = [];
    this.bioProcesses = [];
    this.diseases = [];
    this.biomarkers = [];
    this.graphics = {wheel: null,matrix: null};
    this.interactions = [];
    this.interactionTypes = [];
    this.citations = [];
    this.inContext = true;
    this.index = index;
    this.genes = [index]; // to be compatible for focusOn, add, etc
    this.upstreamInx = 0;
    this.downstreamInx = 0;
    this.bindingInx = 0;
    this.neighborCount = 0;
    this.neighborMap = null;
    this.evidenceType = evidenceType;
    locationGroups.addGene(this);
    functionGroups.addGene(this);
    interactionTypeGroups.addGene(this);
    
    this.getRank = function() {
        this.rank = (this.foldChange - genes.fcRange.min) * genes.fcRange.inv;
        return this.rank;
    };
    
    // this.getDiseaseList = function() {
    //     for (var j = 0; j < diseases.list.length; j++) { // over all diseases
    //         for (var k = 0; k < diseases.list[j].genes.length; k++) { // over all genes in this (j) disease
    //             if (diseases.list[j].genes[k].toLowerCase().indexOf(this.symbol.toLowerCase()) >= 0) {
    //                 this.diseases.push(j); // index into diseaseList array
    //             }
    //         }
    //     }
    // };
    
    this.select = function() {
        selectionMgr.set(this);
    };
    
    this.removeFromContext = function() {
        this.inContext = false;
    };
    
    this.addToContext = function() {
        this.inContext = true;
    };
    
    this.getGeneList = function() {
        return [this.index]; // every object returns an array
    };
    this.getGenesInContext = function() {
        var l = [this.index];
        if (!this.inContext)
            l = [];
        return l;
    };
    
    this.getInteractionsInContext = function() {
        var l = [];
        for (var i = 0; i < this.interactions.length; i++) {
            if (genes.list[this.interactions[i].neighbor].inContext)
                l.push(this.interactions[i]);
        }
        return l;
    };
    
    this.calcNeighborCount = function() { // probably should combine this and neighborMap???
        
        var nei = [];
        var neiCount = 0;
        for (var i = 0; i < this.interactions.length; i++) {
            var alreadyCounted = false;
            for (var j = 0; j < nei.length; j++) {
                if (this.interactions[i].neighbor == nei[j]) {
                    alreadyCounted = true;
                }
            }
            if (!alreadyCounted) {
                nei.push(this.interactions[i].neighbor);
                neiCount++;
            }
        }
        return neiCount;
    };
    
    this.getBBox = function() { // bounding box of the object .. eithr geneIcon(matrix) or circle(wheel)
        if (parent.view.pageType == "context") {
            return parent.view.wheel.getGeneScreenLoc(this.index);
        
        }
        if (parent.view.isMatrixPage) {
            return parent.view.geneMatrix.getGeneScreenLoc(this.index);
        }
        return null;
    };
    
    this.getExpColor = function() { // based on the logRatio	
        var frac = (this.logRatio - genes.lrRange.min) * genes.lrRecip;
        //return getColorByFraction(frac, true);
        return getColorByFraction(this.foldChange, true);
    };
    this.getName = function() {
        return this.name;
    };
    
    this.getTopic = function() {
        return this.topic;
    };
    this.getTopicLabel = function() {
        return this.topicLabel;
    };
    this.getTopicArray = function() {
        return this.topicArray;
    };
    this.getIndex = function() {
        return this.index;
    };
    this.getDesc = function() {
        return this.desc;
    };
    this.getId = function() {
        return this.id;
    };
    this.getSymbol = function() {
        return this.symbol;
    };
    this.getNeighborMap = function() {
        return this.neighborMap;
    };
    
    this.updateInxCount = function() {
        this.neighborMap = new Object();
        var map = new Object();
        var neighborList = new Array();
        for (var i = 0; i < this.interactions.length; i++) {
            //go over inx and populate the count
            var inx = this.interactions[i];
            var key = inx.neighbor + "," + inx.neighborDirection;
            var val = map[key];
            if (val == null)
                map[key] = inx.neighborDirection;
            
            var obj = {index: inx.neighbor,type: inx.type,
                direction: inx.neighborDirection};
            if (!containsNeighbor(neighborList, obj)) { // contains = same gene index, interaction type and interaction direction
                neighborList.push(obj);
            }
        }
        //calculate neighbor counts
        for (var key in map) {
            var value = map[key];
            if (value == "upstream")
                this.upstreamInx++;
            else if (value == "downstream")
                this.downstreamInx++;
            else if (value == "both")
                this.bindingInx++;
        }
        
        for (var i = 0; i < neighborList.length; i++) {
            var neighbor = neighborList[i];
            this.neighborMap[neighbor.index] = neighbor;

            if (x$.inArray(neighbor.type, this.interactionTypes) == -1)
                this.interactionTypes.push(neighbor.type);
        }
        this.neighborCount = this.calcNeighborCount(); // store this for later usage
    };
    
    function containsNeighbor(array, obj) {
        var i = array.length;
        while (i--) {
            var curr = array[i];
            if (curr.index == obj.index && curr.type == obj.type && curr.direction == obj.direction)
                return true;
        }
        return false;
    }

}


function AttributeGroups(attribute) {
    this.list = [];
    this.attribute = attribute;
    
    switch (this.attribute) {
        case "location":
            this.type = "Cellular Location";
            break;
        case "proFamily":
            this.type = "Molecular Function";
            break;
        case "interactionTypes":
            this.type = "Interaction Types";
            break;
        default:
            alert("error creating an attribute group.. ie. location or proFamily or ..")
    }
    
    this.addGroup = function(name) {
        this.list.push(new AttributeGroup(name, "", this.type, this.attribute, this.list.length)); // no desc for now
    };
    
    this.addGene = function(g) {
        if ("interactionTypes" != this.attribute) {
            this.addGeneToGroup(g, g[this.attribute]);
        } else {
            for (var i in g.interactionTypes) {
                this.addGeneToGroup(g, g.interactionTypes[i]);
            }
        }
    };
    
    this.addGeneToGroup = function(g, grpName) {
        var grp = this.getGroupByName(grpName);
        if (grp == null) {
            this.addGroup(grpName);
            grp = this.getGroupByName(grpName);
        }
        grp.addGene(g);
    };
    
    this.getGroupByName = function(name) {
        for (var i = 0; i < this.list.length; i++) {
            if (this.list[i].name == name)
                return this.list[i];
        }
        return null;
    };
}
function AttributeGroup(name, desc, type, topic, index) {
    this.name = name;
    this.genes = [];
    this.index = index;
    this.desc = desc;
    this.type = type;
    this.topic = topic;
    this.hierarchyNodes = [];
    this.id = topic + index;
    this.inContext = false;
    this.topicLabel = "attribute group";
    switch (topic) {
        case "location":
            this.topicLabel = "cellular location";
            break;
        case "proFamily":
            this.topicLabel = "molecular function";
            break;
        case "interactionTypes":
            this.topicLabel = "interaction types";
            break;
        default:
    //do nothing
    //alert("error creating an attribute group.. ie. location or proFamily or ..");
    }
    
    this.addGene = function(g) {
        this.genes.push(g.index);
    };
    
    this.getGeneList = function() {
        return this.genes; // note that inContext is not considered at this point
    };
    this.getGenesInContext = function() {
        var lst = [];
        for (var i = 0; i < this.genes.length; i++) {
            if (genes.list[this.genes[i]].inContext)
                lst.push(this.genes[i]);
        }
        return lst;
    };
    
    this.getChildObjects = function() {
        return [];
    };
    
    this.getTopic = function() {
        return this.topic;
    };
    this.getTopicLabel = function() {
        return this.topicLabel;
    };
    this.getName = function() {
        return this.name;
    };
    this.getIndex = function() {
        return this.index;
    };
    this.getDesc = function() {
        return this.desc;
    };
    this.getId = function() {
        return this.id;
    };
    
    this.getType = function() {
        return this.type;
    };
}

function initGenes(updateGeneList, geneList, callback) {
    updateGeneList(geneData[reportId], geneList, callback);
}

function updateGeneList(gJson, geneList, callback) {
    var pValueMin = 99999999;
    var pValueMax = -99999999;
    var lrMin = 99999999;
    var lrMax = -99999999;
    var fcMin = 99999999;
    var fcMax = -99999999;
    
    var mndpg = 0;
    var gl = new Array();
    
    for (var i = 0; i < gJson.length; i++) {
        gl.push(new Gene(i, gJson[i].logRatio, gJson[i].pvalue, gJson[i].foldChange, 
        gJson[i].did, gJson[i].symbol, gJson[i].name, "Temp Description", 
        gJson[i].location, gJson[i].proteinFamily, gJson[i].evidenceType));
        pValueMin = Math.min(pValueMin, gJson[i].pvalue);
        pValueMax = Math.max(pValueMax, gJson[i].pvalue);
        lrMin = Math.min(lrMin, gJson[i].logRatio);
        lrMax = Math.max(lrMax, gJson[i].logRatio);
        fcMin = Math.min(fcMin, gJson[i].foldChange);
        fcMax = Math.max(fcMax, gJson[i].foldChange);
        mndpg = Math.max(gl[i].diseases.length, mndpg);
    }
    
    geneList.list = gl;
    //	geneList.maxNumMembers = new Array();
    // geneList.maxNumMembers["diseases"] = mndpg;
    // geneList.maxNumMembers["pathways"] = 0; // not yet defined
    // geneList.maxNumMembers["processes"] = 0;
    geneList.maxNumMembers["interactions"] = {total: 0,upstream: 0,downstream: 0};
    
    
    var pvRecip = (pValueMax == pValueMin) ? 0 : 1.0 / (pValueMax - pValueMin);
    var pValueRange = {min: pValueMin,max: pValueMax,inv: pvRecip};
    var fcRecip = (fcMax == fcMin) ? 0 : 1.0 / (fcMax - fcMin);
    var fcRange = {min: fcMin,max: fcMax,inv: fcRecip};
    var lrRecip = (lrMax == lrMin) ? 0 : 1.0 / (lrMax - lrMin);
    var lrRange = {min: lrMin,max: lrMax,inv: lrRecip};
    
    geneList.updateValues(pValueRange, lrRange, fcRange, pvRecip, lrRecip);
    geneList.indexList = [];
    geneList.idHash = {}; // hash to look up the object by id
    var geneLen = geneList.list.length;
    var g, id;
    var re = /ing:/;
    for (g = 0; g < geneLen; g++) {
        geneList.indexList[g] = g;
        id = geneList.list[g].id.toLowerCase().replace(re, "");
        geneList.idHash[id] = {}
        geneList.idHash[id].idx = g;
        geneList.idHash[id].o = geneList.list[g];
    }
    
    callback();
    
}

function GeneList() {
    this.list = new Array();
    this.maxNumMembers = new Array();
    this.memberships = new Array();
    
    this.updateValues = function(pValueRange, lrRange, fcRange, pvRecip, lrRecip) {
        this.pValueRange = pValueRange;
        this.lrRange = lrRange;
        this.fcRange = fcRange;
        this.pvRecip = pvRecip;
        this.lrRecip = lrRecip;
    };
    
    this.calcMembershipCounts = function() {
        var geneInDiseaseCount = 0;
        var geneInProcessCount = 0;
        var geneInPathwayCount = 0;
        
        for (var i = 0; i < this.list.length; i++) {
            if (this.list[i].diseases.length > 0) {
                geneInDiseaseCount++;
            }
            if (this.list[i].bioProcesses.length > 0) {
                geneInProcessCount++;
            }
            if (this.list[i].pathways.length > 0) {
                geneInPathwayCount++;
            }
        }
        
        this.memberships["diseases"] = geneInDiseaseCount;
        this.memberships["processes"] = geneInProcessCount;
        this.memberships["pathways"] = geneInPathwayCount;
    
    };
    
    this.calcMemberCount = function(opt) {
        var cnt = 0;
        if (opt == "bioProcesses") {
            for (var i = 0; i < this.list.length; i++) {
                cnt = Math.max(cnt, this.list[i].bioProcesses.length);
            }
            this.maxNumMembers["processes"] = cnt;
        }
        if (opt == "diseases") {
            for (var i = 0; i < this.list.length; i++) {
                cnt = Math.max(cnt, this.list[i].diseases.length);
            }
            this.maxNumMembers["diseases"] = cnt;
        }
        if (opt == "pathways") {
            for (var i = 0; i < this.list.length; i++) {
                cnt = Math.max(cnt, this.list[i].pathways.length);
            }
            this.maxNumMembers["pathways"] = cnt;
        }
        if (opt == "interactions") {
            var nin = 0;
            var nout = 0;
            var nboth = 0;
            for (var i = 0; i < this.list.length; i++) {
                cnt = Math.max(cnt, this.list[i].interactions.length);
                var thisIn = 0;
                var thisOut = 0;
                var thisBoth = 0;
                for (var j = 0; j < this.list[i].interactions.length; j++) {
                    if (this.list[i].interactions[j].neighborDirection == "upstream")
                        thisIn++;
                    if (this.list[i].interactions[j].neighborDirection == "downstream")
                        thisOut++;
                    if (this.list[i].interactions[j].neighborDirection == "both") {
                        thisBoth++;
                        thisOut++;
                        thisIn++
                    }
                    ;
                }
                nin = Math.max(nin, thisIn);
                nout = Math.max(nout, thisOut);
                nboth = Math.max(nboth, thisBoth);
            }
            this.maxNumMembers["interactions"].total = cnt;
            this.maxNumMembers["interactions"].upstream = nin;
            this.maxNumMembers["interactions"].downstream = nout;
            this.maxNumMembers["interactions"].both = nboth;
        }
    };
    
    this.getNeighborCount = function() {
        var maxNei = 0;
        for (var i = 0; i < this.list.length; i++) {
            var nei = this.list[i].neighborCount;
            //this.list[i].neighborCount = nei;  // this is set in genes.list[i].updateInxCount set in populateInteractions
            maxNei = Math.max(nei, maxNei);
        }
        this.maxNumMembers["neighbors"] = maxNei;
    };
    
    this.sortBy = function(opt, inContext) {
        var l = [];
        if (inContext) {
            l = this.listInContext();
        } else {
            for (var i = 0; i < this.list.length; i++) {
                l[i] = i;
            }
        }
        if (opt == "foldChange") {
            l.sort(function(a, b) {
                return genes.list[a].foldChange - genes.list[b].foldChange;
            });
        }
        if (opt == "neighborCount") {
            l.sort(function(a, b) {
                return genes.list[a].neighborCount - genes.list[b].neighborCount;
            });
        }
        if (opt == "processCount") {
            l.sort(function(a, b) {
                return genes.list[a].bioProcesses.length - genes.list[b].bioProcesses.length;
            });
        }
        if (opt == "diseaseCount") {
            l.sort(function(a, b) {
                return genes.list[a].diseases.length - genes.list[b].diseases.length;
            });
        }
        if (opt == "pathwayCount") {
            l.sort(function(a, b) {
                return genes.list[a].pathways.length - genes.list[b].pathways.length;
            });
        }
        if (opt == "pValue") {
            l.sort(function(a, b) {
                return genes.list[a].pValue - genes.list[b].pValue;
            });
        }
        return l;
    };
    
    this.byIndex = function(i) {
        return this.list[i];
    };
    
    this.byName = function(s) {
        var notFound = null;
        for (var i = 0; i < this.list.length; i++) {
            if (this.list[i].name.toLowerCase() == s.toLowerCase())
                return this.list[i];
        }
        ;
        return null;
    };
    
    this.byId = function(idToFind) {
        /*var notFound = null;
		for (var i=0; i<this.list.length; i++ ){
			if(this.list[i].id == idToFind) return this.list[i];
		};
		return null;*/
        var obj = null;
        var re = /ing:/;
        var id = idToFind.toLowerCase().replace(re, "");
        if (this.idHash.hasOwnProperty(id))
            obj = this.idHash[id].o;
        return obj
    };
    
    this.getByID = function(idToFind) { // for compatibility with other structures (diseases, pathways, processes etc)
        return this.byId(idToFind);
    };
    
    this.indexBySymbol = function(s) {
        var notFound = -1;
        for (var i = 0; i < this.list.length; i++) {
            if (this.list[i].symbol.toLowerCase() == s.toLowerCase())
                return i;
        }
        return notFound;
    };
    
    this.indexById = function(s) {
        var re = /ing:/;
        var id = s.toLowerCase().replace(re, "");
        var idx = -1;
        if (this.idHash.hasOwnProperty(id))
            idx = this.idHash[id].idx;
        return idx;
    };
    
    this.getIds = function(indexList) {
        var idList = new Array();
        for (var i = 0; i < indexList.length; i++) {
            idList.push(this.list[indexList[i]].getId());
        }
        return idList;
    };
    
    this.numberInContext = function() {
        var n = 0;
        for (var i = 0; i < this.list.length; i++) {
            if (this.list[i].inContext)
                n++;
        }
        return n;
    };
    
    this.listInContext = function() {
        var l = new Array();
        for (var i = 0; i < this.list.length; i++) {
            if (this.list[i].inContext)
                l.push(i);
        }
        return l;
    };
    
    this.getGenesInContext = function() { // for rim compatibility
        return this.listInContext();
    };
    
    this.populateInteractions = function() {
        
        
        var mxi = 0;
        
        var count = interactions.list.length;
        for (var j = 0; j < count; j++) {
            var inx = interactions.list[j];
            var toindex = this.indexById(inx.to);
            var fromindex = this.indexById(inx.from);
            
            var from_dir = "both";
            var to_dir = "both";
            if (inx.isDirected) {
                from_dir = "downstream";
                to_dir = "upstream";
            }
            var fromGene = this.byId(inx.from);
            var toGene = this.byId(inx.to);
            
            
            fromGene.interactions.push({type: inx.type,findingId: inx.findingId,xid: inx.id,
                neighborDirection: from_dir,neighbor: toindex});
            mxi = Math.max(mxi, fromGene.interactions.length);
            this.maxNumMembers["interactions"].total = mxi;
            
            toGene.interactions.push({type: inx.type,findingId: inx.findingId,xid: inx.id,
                neighborDirection: to_dir,neighbor: fromindex});
            mxi = Math.max(mxi, toGene.interactions.length);
            this.maxNumMembers["interactions"].total = mxi;
        }

        //update upstream,downstream and binding inx counts for every gene
        for (var i = 0; i < this.list.length; i++) {
            this.list[i].updateInxCount();
            interactionTypeGroups.addGene(this.list[i]); //update the interaction type group
        }
    };
    this.getName = function() {
        return "genes";
    };
}
;

// Ad hoc collection of genes. If it is a copy of a specific object  (disease/process/pathway etc)
// then the parentObj can be set to the original object
function AdHocGeneSet(name, list, parentObj, id, topic, topicLabel) {
    this.id = id ? id : 0;
    this.name = name;
    this.topic = topic ? topic : "adhocGenes";
    this.topicLabel = topicLabel ? topicLabel : "Gene Set";
    this.desc = "";
    this.genes = list;
    this.parentObj = parentObj;
    this.inContext = true;
    this.index = -1;
    this.type = "adhocGenes";
    
    var removeDuplicates = function() {
    // TODO
    };
    removeDuplicates();
    
    this.getGenesInContext = function() {
        var l = [];
        for (var i = 0; i < this.genes.length; i++) {
            if (genes.list[this.genes[i]].inContext)
                l.push(this.genes[i]);
        }
        return l;
    };
    
    this.isInContext = function() {
        var gl = this.getGenesInContext();
        this.inContext = (gl.length > 0) ? true : false;
        return this.inContext;
    };
    
    this.getMemberById = function(memId) {
        var mem = null;
        for (var i = 0; i < this.genes.length; i++) {
            if (genes.list[this.genes[i]].id == memId) {
                mem = genes.list[this.genes[i]];
                break;
            }
        }
        
        return mem;
    
    };
    
    this.getGeneList = function() {
        return this.genes;
    };
    this.getName = function() {
        return this.name;
    };
    this.getParentObj = function() {
        return this.parentObj;
    };
    this.getTopic = function() {
        return this.topic;
    };
    this.getTopicLabel = function() {
        return this.topicLabel;
    };
    this.getIndex = function() {
        return this.index;
    };
    this.getDesc = function() {
        return this.desc;
    };
    this.getId = function() {
        return this.id;
    };
    
    function Commonality() {
        
        function CommonGroup() {
        // Map of ObjectID to {[genes], %calc}
        }
        ;
        
        this.pathways = new CommonGroup();
        this.bioProcesses = new CommonGroup();
        this.diseases = new CommonGroup();
        this.interactionTypeGroups = new CommonGroup();
        this.locationGroups = new CommonGroup();
        this.molFunctionGroups = new CommonGroup();
        this.interactions = new Object(); // // Map of GeneID to {count, %calc}
        
        this.sortedPathways = [];
        this.sortedBioProcesses = [];
        this.sortedDiseases = [];
        this.sortedInteractionTypeGroups = [];
        this.sortedLocationGroups = [];
        this.sortedMolFunctionGroups = [];
        this.sortedInteractions = []; // // Map of GeneID to {count, %calc}
        
        this.addGene = function(gene) {
            // if (true) {
            //     var allPathways = pathways; // from parent.comm
            //     for (var i = 0; i < gene.pathways.length; i++) {
            //         var pwy = allPathways.byIndex(gene.pathways[i]);
            //         if (this.pathways.hasOwnProperty(pwy.id)) {
            //             this.pathways[pwy.id].genes.push(gene);
            //         } else {
            //             this.pathways[pwy.id] = new Object();
            //             this.pathways[pwy.id].genes = [gene];
            //             this.pathways[pwy.id].obj = pwy;
            //             this.pathways[pwy.id].percentage = 0; // initialized to 0; 
            //         }
            //     }
            // }
            
            if (true) {
                var allDiseases = diseases; // from parent.comm
                for (var i = 0; i < gene.diseases.length; i++) {
                    var disease = allDiseases.byIndex(gene.diseases[i]);
                    if (this.diseases.hasOwnProperty(disease.id)) {
                        this.diseases[disease.id].genes.push(gene);
                    } else {
                        this.diseases[disease.id] = new Object();
                        this.diseases[disease.id].genes = [gene];
                        this.diseases[disease.id].obj = disease;
                        this.diseases[disease.id].percentage = 0; // initialized to 0; 
                    }
                }
            }
            
            if (true) {
                var allProcesses = bioProcesses; // from parent.comm
                for (var i = 0; i < gene.bioProcesses.length; i++) {
                    var bioPro = allProcesses.byIndex(gene.bioProcesses[i]);
                    if (this.bioProcesses.hasOwnProperty(bioPro.id)) {
                        this.bioProcesses[bioPro.id].genes.push(gene);
                    } else {
                        this.bioProcesses[bioPro.id] = new Object();
                        this.bioProcesses[bioPro.id].genes = [gene];
                        this.bioProcesses[bioPro.id].obj = bioPro;
                        this.bioProcesses[bioPro.id].percentage = 0; // initialized to 0; 
                    }
                }
            }
            
            if (true) {
                var allInteractionTypeGroups = interactionTypeGroups; // from parent.comm
                for (var i = 0; i < gene.interactionTypes.length; i++) {
                    var inxTypeGroup = allInteractionTypeGroups.getGroupByName(gene.interactionTypes[i]);
                    if (this.interactionTypeGroups.hasOwnProperty(inxTypeGroup.id)) {
                        this.interactionTypeGroups[inxTypeGroup.id].genes.push(gene);
                    } else {
                        this.interactionTypeGroups[inxTypeGroup.id] = new Object();
                        this.interactionTypeGroups[inxTypeGroup.id].genes = [gene];
                        this.interactionTypeGroups[inxTypeGroup.id].obj = inxTypeGroup;
                        this.interactionTypeGroups[inxTypeGroup.id].percentage = 0; // initialized to 0; 
                    }
                }
                
                this.interactions[gene.id] = new Object();
                this.interactions[gene.id].neighborCount = 0;
                this.interactions[gene.id].obj = gene;
                this.interactions[gene.id].percentage = 0;
            }
            
            if (true) {
                var allMolFunctionGroups = functionGroups; // from parent.comm
                var molFunctionGroup = allMolFunctionGroups.getGroupByName(gene.proFamily);
                if (this.molFunctionGroups.hasOwnProperty(molFunctionGroup.id)) {
                    this.molFunctionGroups[molFunctionGroup.id].genes.push(gene);
                } else {
                    this.molFunctionGroups[molFunctionGroup.id] = new Object();
                    this.molFunctionGroups[molFunctionGroup.id].genes = [gene];
                    this.molFunctionGroups[molFunctionGroup.id].obj = molFunctionGroup;
                    this.molFunctionGroups[molFunctionGroup.id].percentage = 0; // initialized to 0; 
                }
            }
            
            if (true) {
                var allLocationGroups = locationGroups; // from parent.comm
                var locGroup = allLocationGroups.getGroupByName(gene.location);
                if (this.locationGroups.hasOwnProperty(locGroup.id)) {
                    this.locationGroups[locGroup.id].genes.push(gene);
                } else {
                    this.locationGroups[locGroup.id] = new Object();
                    this.locationGroups[locGroup.id].genes = [gene];
                    this.locationGroups[locGroup.id].obj = locGroup;
                    this.locationGroups[locGroup.id].percentage = 0; // initialized to 0; 
                }
            }
        };
    }
    ;
    
    this.commons = null;
    this.calculateCommonality = function() {
        if (this.commons == null) { // dont recalculate
            
            var fnPercentageSort = function(a, b) {
                return a.percentage < b.percentage ? 1 : -1;
            };
            
            this.commons = new Commonality();
            var allGenes = genes; // from parent.comm
            
            for (var i = 0; i < this.genes.length; i++) {
                this.commons.addGene(allGenes.byIndex(this.genes[i]));
            }
            
            var gll = this.genes.length;
            // for (var i in this.commons.pathways) {
            //     var pcu = (this.commons.pathways[i].genes.length / gll) * 100;
            //     this.commons.pathways[i].percentage = roundNumber(pcu, 1);
            //     this.commons.sortedPathways.push(this.commons.pathways[i]);
            // }
            // this.commons.sortedPathways = merge_sort(this.commons.sortedPathways, fnPercentageSort);
            
            for (var i in this.commons.bioProcesses) {
                var pcu = (this.commons.bioProcesses[i].genes.length / gll) * 100;
                this.commons.bioProcesses[i].percentage = roundNumber(pcu, 1);
                this.commons.sortedBioProcesses.push(this.commons.bioProcesses[i]);
            }
            this.commons.sortedBioProcesses = merge_sort(this.commons.sortedBioProcesses, fnPercentageSort);
            
            for (var i in this.commons.diseases) {
                var pcu = (this.commons.diseases[i].genes.length / gll) * 100;
                this.commons.diseases[i].percentage = roundNumber(pcu, 1);
                this.commons.sortedDiseases.push(this.commons.diseases[i]);
            }
            this.commons.sortedDiseases = merge_sort(this.commons.sortedDiseases, fnPercentageSort);

            //			for (var i in this.commons.interactionTypeGroups) {
            //				var pcu  =  (this.commons.interactionTypeGroups[i].genes.length / gll) * 100;
            //				this.commons.interactionTypeGroups[i].percentage = roundNumber(pcu, 1);
            //				this.commons.sortedInteractionTypeGroups.push(this.commons.interactionTypeGroups[i]);
            //			}
            //			this.commons.sortedInteractionTypeGroups = merge_sort(this.commons.sortedInteractionTypeGroups, fnPercentageSort);

            //calculate neighbor counts
            for (var i in this.commons.interactions) {
                var g = this.commons.interactions[i].obj;
                for (var inx in g.neighborMap) {
                    if (this.getMemberById(genes.byIndex(inx).id) != null) {
                        this.commons.interactions[i].neighborCount++;
                    }
                }
            }
            //then calculate %s
            for (var i in this.commons.interactions) {
                var pcu = (this.commons.interactions[i].neighborCount / gll) * 100;
                this.commons.interactions[i].percentage = roundNumber(pcu, 1);
                this.commons.sortedInteractions.push(this.commons.interactions[i]);
            }
            this.commons.sortedInteractions = merge_sort(this.commons.sortedInteractions, fnPercentageSort);

        //			for (var i in this.commons.molFunctionGroups) {
        //				var pcu  =  (this.commons.molFunctionGroups[i].genes.length / gll) * 100;
        //				this.commons.molFunctionGroups[i].percentage = roundNumber(pcu, 1);
        //				this.commons.sortedMolFunctionGroups.push(this.commons.molFunctionGroups[i]);
        //			}
        //			this.commons.sortedMolFunctionGroups = merge_sort(this.commons.sortedMolFunctionGroups, fnPercentageSort);
        //			
        //			for (var i in this.commons.locationGroups) {
        //				var pcu  =  (this.commons.locationGroups[i].genes.length / gll) * 100;
        //				this.commons.locationGroups[i].percentage = roundNumber(pcu, 1);
        //				this.commons.sortedLocationGroups.push(this.commons.locationGroups[i]);
        //			}
        //			this.commons.sortedLocationGroups = merge_sort(this.commons.sortedLocationGroups, fnPercentageSort);
        }
    };
}


GroupComplexGenes.prototype = new AdHocGeneSet();
function GroupComplexGenes(name, list, parentId) {
    AdHocGeneSet.call(this, name, list);
    this.topic = "groupComplex";
    this.topicLabel = "Group/Complex";
    this.parentId = parentId;
    this.id = parentId; // setting id to parentId since this represents the set of genes in this G/C
    this.symbol = '';
    this.desc = '';
    this.type = '';
}

function NonDatasetConcept(id) {
    this.id = id;
    this.name = '';
    this.symbol = '';
    this.desc = '';
    this.type = '';
    this.gl = [];
    this.topic = "nondatasetconcept";
    this.topicLabel = "NonDatasetConcept";
    
    this.getGenesInContext = function() {
        return this.gl;
    };
    this.getGeneList = function() {
        return this.gl;
    };
    this.select = function() {
        selectionMgr.set(this);
    };
    this.getTopic = function() {
        return this.topic;
    };
    this.getTopicLabel = function() {
        return this.topicLabel;
    };
    this.getIndex = function() {
        return -1;
    };
    this.getDesc = function() {
        return this.desc;
    };
    this.getId = function() {
        return this.id;
    };
    this.getName = function() {
        return this.name;
    };
    this.getSymbol = function() {
        return this.symbol;
    };
}



