Source: snet.maia

///
/// @license
/// Copyright 2020 Roberto Luiz Souza Monteiro,
///                Renata Souza Barreto,
///                Hernane Borges de Barros Pereira.
///
/// Licensed under the Apache License, Version 2.0 (the 'License');
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at;
///
///   http://www.apache.org/licenses/LICENSE-2.0;
///
/// Unless required by applicable law or agreed to in writing, software;
/// distributed under the License is distributed on an 'AS IS' BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, eitherMath.express or implied.
/// See the License for the specific language governing permissions and;
/// limitations under the License.
///

///
/// Library for building semantic networks.
/// @namespace snet
///
namespace snet {
    ///
    /// Create a JSON graph from a file in DLF format.
    /// @method createFromDlf
    /// @memberof snet
    /// @param {string}   fileContents - DLF file contents.
    /// @param {object}   properties - Network properties (n, m and directed).
    /// @param {string}   topology - Network topology. It can be:
    ///                              chain, circle or clique.
    /// @param {boolean}  weighted - The created network must be weighted based
    ///                              on the number of occurrences of the connections
    ///                              between the vertices.
    /// @param {boolean}  allowLoops - Allows the existence of loops.
    /// @param {object}   network - Network previously created to insert new data.
    /// @return {object}  A JSON containing the network.
    ///
    function createFromDlf(fileContents, properties, topology, weighted, allowLoops, network) {
        if (core.type(properties) == "undefined") {
            properties = {
                "n": 0,
                "m": 0,
                "directed": true
            }
        } else {
            properties.n = 0
            properties.m = 0
        }
        if (core.type(topology) == "undefined") {
            topology = "chain"
        }
        if (core.type(weighted) == "undefined") {
            weighted = true
        }
        if (core.type(network) == "undefined") {
            network = {
                "nodes": [],
                "edges": [],
                "numberOfSentences": 0,
                "vocabulary": 0,
                "density": []
            }
        }
        if (core.type(allowLoops) == "undefined") {
            allowLoops = false
        }

        if (properties.directed == true) {
            edgeType = "arrow"
        } else {
            edgeType = "line"
        }

        function findNode(list, label) {
            foreach (list; index; value) {
                if (value.label == label) {
                    return(value)
                }
            }
            return("undefined")
        }

        function findEdge(list, source, target, directed) {
            if (core.type(directed) == "undefined") {
                directed = false
            }

            foreach (list; index; value) {
                if (directed) {
                    if ((value.source == source) && (value.target == target)) {
                        return(value)
                    }
                } else {
                    if (((value.source == source) && (value.target == target)) || ((value.source == target) && (value.target == source))) {
                        return(value)
                    }
                }
            }
            return("undefined")
        }

        nodes = []
        endOfRecord = false

        t1 = core.date()

        fileLines = core.split(core.replace(fileContents, "\r\n", "\n"), "\n")

        system.println("Processing " + fileLines.length + " lines ...")

        for (l = 0; l < fileLines.length; l = l + 1) {
            if ((l != 0) || (l % 100 == 0)) {
                system.println("Processed " + l.toString() + " lines of " + fileLines.length.toString() + ".")
            }

            line = fileLines[l]
            line = core.replace(line, "\t", " ")
            record = core.splitCSV(line.trim(), " ", true)
            if (record[0] == "{S}") {
                network.numberOfSentences = network.numberOfSentences + 1
                endOfRecord = true
            } else {
                if (l == fileLines.length - 1) {
                    network.numberOfSentences = network.numberOfSentences + 1
                    endOfRecord = true
                }
            }

            if (core.length(record) >= 1) {
                if (!endOfRecord) {
                    label = core.toString(record[0])
                    x = ""
                    y = ""
                    size = ""
                    if (x == "") {
                        x = math.random()
                    }
                    if (y == "") {
                        y = math.random()
                    }
                    if (size == "") {
                        size = 1
                    }
                    node = {
                        "id": label,
                        "label": label,
                        "x": x,
                        "y": y,
                        "size": size
                    }
                    nodes.push(node)
                } else {
                    foreach (nodes; index; node) {
                        nodeFound = findNode(network.nodes, node.label)
                        if (nodeFound == "undefined") {
                            network.nodes.push(node)
                            network.vocabulary = network.vocabulary + 1
                        } else {
                            nodeFound.size = nodeFound.size + 1
                        }
                    }
                    
                    if (topology == "chain") {
                        for (i = 0; i < nodes.length - 1; i = i + 1) {
                            node1 = nodes[i]
                            node2 = nodes[i + 1]

                            if (!allowLoops) {
                                if (node1.id == node2.id) {
                                    continue
                                }
                            }

                            edgeFound = findEdge(network.edges, node1.id, node2.id, properties.directed)
                            if (edgeFound == "undefined") {
                                edge = {
                                    "id": "e" + network.edges.length,
                                    "label": "1",
                                    "source": node1.id,
                                    "target": node2.id,
                                    "size": 1,
                                    "type": edgeType
                                }
                                network.edges.push(edge)
                            } else {
                                if (weighted) {
                                    edgeFound.size = edgeFound.size + 1
                                    edgeFound.label = core.toString(edgeFound.size)
                                }
                            }
                        }
                    } elseif (topology == "clique") {
                        foreach (nodes; index1; node1) {
                            foreach (nodes; index2; node2) {
                                if (!allowLoops) {
                                    if (node1.id == node2.id) {
                                        continue
                                    }
                                }

                                if (index1 > index2) {
                                    continue
                                }

                                edgeFound = findEdge(network.edges, node1.id, node2.id, properties.directed)
                                if (edgeFound == "undefined") {
                                    edge = {
                                        "id": "e" + network.edges.length,
                                        "label": "1",
                                        "source": node1.id,
                                        "target": node2.id,
                                        "size": 1,
                                        "type": edgeType
                                    }
                                    network.edges.push(edge)
                                } else {
                                    if (weighted) {
                                        edgeFound.size = edgeFound.size + 1
                                        edgeFound.label = core.toString(edgeFound.size)
                                    }
                                }
                            }
                        }
                    } elseif (topology == "circle") {
                        for (i = 0; i < nodes.length - 1; i = i + 1) {
                            node1 = nodes[i]
                            node2 = nodes[i + 1]

                            if (!allowLoops) {
                                if (node1.id == node2.id) {
                                    continue
                                }
                            }

                            edgeFound = findEdge(network.edges, node1.id, node2.id, properties.directed)
                            if (edgeFound == "undefined") {
                                edge = {
                                    "id": "e" + network.edges.length,
                                    "label": "1",
                                    "source": node1.id,
                                    "target": node2.id,
                                    "size": 1,
                                    "type": edgeType
                                }
                                network.edges.push(edge)
                            } else {
                                if (weighted) {
                                    edgeFound.size = edgeFound.size + 1
                                    edgeFound.label = core.toString(edgeFound.size)
                                }
                            }

                            if (i + 1 == nodes.length - 1) {
                                node1 = nodes[i + 1]
                                node2 = nodes[0]

                                edgeFound = findEdge(network.edges, node1.id, node2.id, properties.directed)
                                if (edgeFound == "undefined") {
                                    edge = {
                                        "id": "e" + network.edges.length,
                                        "label": "1",
                                        "source": node1.id,
                                        "target": node2.id,
                                        "size": 1,
                                        "type": edgeType
                                    }
                                    network.edges.push(edge)
                                } else {
                                    if (weighted) {
                                        edgeFound.size = edgeFound.size + 1
                                        edgeFound.label = core.toString(edgeFound.size)
                                    }
                                }
                            }
                        }
                    }

                    properties.n = network.nodes.length
                    properties.m = network.edges.length
                    if (properties.directed) {
                        density = (properties.m / (properties.n * (properties.n - 1)))
                    } else {
                        density = (2 * properties.m / (properties.n * (properties.n - 1)))
                    }
                    network.density.push(density)

                    nodes = []
                    endOfRecord = false
                }
            }
        }

        properties.n = network.nodes.length
        properties.m = network.edges.length

        t2 = core.date()
        t = t2 - t1

        system.println("Processed " + l.toString() + " lines of " + fileLines.length.toString() + ".")
        system.println("Elapsed time: " + t + " ms.")

        return (network)
    }

    ///
    /// Create a JSON graph from a file in JSON data.
    /// @method createFromJson
    /// @memberof snet
    /// @param {object}   jsonData - JSON data.
    /// @param {object}   properties - Network properties (n, m and directed).
    /// @param {string}   topology - Network topology. It can be:
    ///                              chain, circle or clique.
    /// @param {boolean}  weighted - The created network must be weighted based
    ///                              on the number of occurrences of the connections
    ///                              between the vertices.
    /// @param {boolean}  allowLoops - Allows the existence of loops.
    /// @param {object}   network - Network previously created to insert new data.
    /// @return {object}  A JSON containing the network.
    ///
    function createFromJson(jsonData, properties, topology, weighted, allowLoops, network) {
        if (core.type(properties) == "undefined") {
            properties = {
                "n": 0,
                "m": 0,
                "directed": true
            }
        } else {
            properties.n = 0
            properties.m = 0
        }
        if (core.type(topology) == "undefined") {
            topology = "chain"
        }
        if (core.type(weighted) == "undefined") {
            weighted = true
        }
        if (core.type(network) == "undefined") {
            network = {
                "nodes": [],
                "edges": [],
                "numberOfSentences": 0,
                "vocabulary": 0,
                "density": []
            }
        }
        if (core.type(allowLoops) == "undefined") {
            allowLoops = false
        }

        if (properties.directed == true) {
            edgeType = "arrow"
        } else {
            edgeType = "line"
        }

        function findNode(list, label) {
            foreach (list; index; value) {
                if (value.label == label) {
                    return(value)
                }
            }
            return("undefined")
        }

        function findEdge(list, source, target, directed) {
            if (core.type(directed) == "undefined") {
                directed = false
            }

            foreach (list; index; value) {
                if (directed) {
                    if ((value.source == source) && (value.target == target)) {
                        return(value)
                    }
                } else {
                    if (((value.source == source) && (value.target == target)) || ((value.source == target) && (value.target == source))) {
                        return(value)
                    }
                }
            }
            return("undefined")
        }

        nodes = []
        
        t1 = core.date()

        system.println("Processing " + jsonData.length + " records ...")

        network.numberOfSentences = jsonData.length

        for (r = 0; r < jsonData.length; r = r + 1) {
            if ((r != 0) || (r % 100 == 0)) {
                system.println("Processed " + r.toString() + " records of " + jsonData.length.toString() + ".")
            }

            record = jsonData[r]

            if (record.length == 0) {
                continue
            }
            
            for (w = 0; w < record.length; w = w + 1) {
                word = record[w]

                label = word.object
                x = math.random()
                y = math.random()
                size = 1
                
                node = {
                    "id": label,
                    "label": label,
                    "x": x,
                    "y": y,
                    "size": size,
                    "class": word.class,
                    "subClass": word.subClass,
                    "subSubClass": word.subSubClass
                }
                
                nodes.push(node)
            }

            foreach (nodes; index; node) {
                nodeFound = findNode(network.nodes, node.label)
                if (nodeFound == "undefined") {
                    network.nodes.push(node)
                    network.vocabulary = network.vocabulary + 1
                } else {
                    nodeFound.size = nodeFound.size + 1
                }
            }

            if (topology == "chain") {
                for (i = 0; i < nodes.length - 1; i = i + 1) {
                    node1 = nodes[i]
                    node2 = nodes[i + 1]

                    if (!allowLoops) {
                        if (node1.id == node2.id) {
                            continue
                        }
                    }

                    edgeFound = findEdge(network.edges, node1.id, node2.id, properties.directed)
                    if (edgeFound == "undefined") {
                        edge = {
                            "id": "e" + network.edges.length,
                            "label": "1",
                            "source": node1.id,
                            "target": node2.id,
                            "size": 1,
                            "type": edgeType
                        }
                        network.edges.push(edge)
                    } else {
                        if (weighted) {
                            edgeFound.size = edgeFound.size + 1
                            edgeFound.label = core.toString(edgeFound.size)
                        }
                    }
                }
            } elseif (topology == "clique") {
                foreach (nodes; index1; node1) {
                    foreach (nodes; index2; node2) {
                        if (!allowLoops) {
                            if (node1.id == node2.id) {
                                continue
                            }
                        }

                        if (index1 > index2) {
                            continue
                        }

                        edgeFound = findEdge(network.edges, node1.id, node2.id, properties.directed)
                        if (edgeFound == "undefined") {
                            edge = {
                                "id": "e" + network.edges.length,
                                "label": "1",
                                "source": node1.id,
                                "target": node2.id,
                                "size": 1,
                                "type": edgeType
                            }
                            network.edges.push(edge)
                        } else {
                            if (weighted) {
                                edgeFound.size = edgeFound.size + 1
                                edgeFound.label = core.toString(edgeFound.size)
                            }
                        }
                    }
                }
            } elseif (topology == "circle") {
                for (i = 0; i < nodes.length - 1; i = i + 1) {
                    node1 = nodes[i]
                    node2 = nodes[i + 1]

                    if (!allowLoops) {
                        if (node1.id == node2.id) {
                            continue
                        }
                    }

                    edgeFound = findEdge(network.edges, node1.id, node2.id, properties.directed)
                    if (edgeFound == "undefined") {
                        edge = {
                            "id": "e" + network.edges.length,
                            "label": "1",
                            "source": node1.id,
                            "target": node2.id,
                            "size": 1,
                            "type": edgeType
                        }
                        network.edges.push(edge)
                    } else {
                        if (weighted) {
                            edgeFound.size = edgeFound.size + 1
                            edgeFound.label = core.toString(edgeFound.size)
                        }
                    }

                    if (i + 1 == nodes.length - 1) {
                        node1 = nodes[i + 1]
                        node2 = nodes[0]

                        edgeFound = findEdge(network.edges, node1.id, node2.id, properties.directed)
                        if (edgeFound == "undefined") {
                            edge = {
                                "id": "e" + network.edges.length,
                                "label": "1",
                                "source": node1.id,
                                "target": node2.id,
                                "size": 1,
                                "type": edgeType
                            }
                            network.edges.push(edge)
                        } else {
                            if (weighted) {
                                edgeFound.size = edgeFound.size + 1
                                edgeFound.label = core.toString(edgeFound.size)
                            }
                        }
                    }
                }
            }

            properties.n = network.nodes.length
            properties.m = network.edges.length
            if (properties.directed) {
                density = (properties.m / (properties.n * (properties.n - 1)))
            } else {
                density = (2 * properties.m / (properties.n * (properties.n - 1)))
            }
            network.density.push(density)

            nodes = []
        }

        properties.n = network.nodes.length
        properties.m = network.edges.length

        t2 = core.date()
        t = t2 - t1

        system.println("Processed " + r.toString() + " records of " + jsonData.length.toString() + ".")
        system.println("Elapsed time: " + t + " ms.")

        return (network)
    }

    ///
    /// Calculate de incidence fidelity index from JSON network data.
    /// @method calculateIncidenceFidelity
    /// @memberof snet
    /// @param {object}   jsonData - JSON network data.
    /// @param {object}   updateWeights - Update weights on the network.
    /// @return {object}  Incidence fidelity table.
    ///
    function calculateIncidenceFidelity(jsonData, updateWeights) {
        if (core.type(jsonData) == "undefined") {
            return("undefined")
        }
        if (core.type(updateWeights) == "undefined") {
            updateWeights = false
        }

        nodes = jsonData.nodes
        edges = jsonData.edges
        numberOfSentences = jsonData.numberOfSentences
        vocabulary = jsonData.vocabulary

        ifData = {
            "numberOfSentences": numberOfSentences,
            "vocabulary": vocabulary,
            "vocabularyByNumberOfSentences": vocabulary / numberOfSentences
        }

        function findNode(list, id) {
            foreach (list; index; value) {
                if (value.id == id) {
                    return(value)
                }
            }
            return("undefined")
        }

        rows = []

        foreach (edges; index; value) {
            source = findNode(nodes, value.source)
            target = findNode(nodes, value.target)

            row = []
            row.push(value.id)
            row.push(source.label + "-" + target.label)
            row.push(source.size)
            row.push(target.size)
            row.push(value.size)
            incidence = value.size / numberOfSentences
            fidelity = value.size / (source.size + target.size - value.size)
            incidenceFidelity = incidence * fidelity
            row.push(incidence)
            row.push(fidelity)
            row.push(incidenceFidelity)
            rows.push(row)
            if (updateWeights) {
                value.size = incidenceFidelity
            }
        }
        
        ifData.rows = rows
        
        return(ifData)
    }
}