# 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
                }
            }
            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 % 1000 == 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)
                                        }
                                    }
                                }
                            }
                        }
                        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
                }
            }
            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 % 1000 == 0)) {
                    system.println("Processed " + r.toString() + " records of " + jsonData.length.toString() + ".")
                }

                record = jsonData[r]

                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)
                                }
                            }
                        }
                    }
                }
                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)
        }
    }