// extramath.js // Copyright 2019-2025, Namhyeon Go and the WelsonJS contributors. // SPDX-License-Identifier: GPL-3.0-or-later // https://github.com/gnh1201/welsonjs // // Documentation: https://app.rdbl.io/T0AB411UHDW9/S0VLKP4HKVGC/P0TL9IQZOUFT // // DTM(Document-term Matrix): https://en.wikipedia.org/wiki/Document-term_matrix function DTM() { this.data = []; this.terms = []; this.add = function(s) { var w = s.trim().split(/\s+/); for (var i = 0; i < w.length; i++) { if (this.terms.indexOf(w[i]) < 0) this.terms.push(w[i]); } this.data.push(w); }; this.toArray = function() { var dtm = []; for (var i = 0; i < this.data.length; i++) { var dt = []; for (var k = 0; k < this.terms.length; k++) { dt.push(this.data[i].indexOf(this.terms[k]) < 0 ? 0 : 1); } dtm.push(dt); } return dtm; }; } // Cosine similarity: https://en.wikipedia.org/wiki/Cosine_similarity function arrayCos(A, B) { var dotproduct = 0; var mA = 0; var mB = 0; for (i = 0; i < A.length; i++) { dotproduct += (A[i] * B[i]); mA += (A[i] * A[i]); mB += (B[i] * B[i]); } mA = Math.sqrt(mA); mB = Math.sqrt(mB); var similarity = (dotproduct) / ((mA) * (mB)) return similarity; } function measureSimilarity(s1, s2) { var dtm = new DTM(); dtm.add(s1); dtm.add(s2); var mat = dtm.toArray(); return arrayCos(mat[0], mat[1]); } function export_measureSimilarity() { return "var ExtraMath=function(){};ExtraMath.DTM=function(){this.data=[],this.terms=[],this.add=function(t){for(var r=t.trim().split(/\s+/),a=0;athis.terms.indexOf(r[a])&&this.terms.push(r[a]);this.data.push(r)},this.toArray=function(){for(var t=[],r=0;rthis.data[r].indexOf(this.terms[s])?0:1);t.push(a)}return t}},ExtraMath.arrayCos=function(t,r){var a=0,s=0,h=0;for(i=0;i maxIndex) { return false; // invalid index -> immediately return false } var idx = n - 1; // shift to 0-based coords.push({ x: idx % size, y: Math.floor(idx / size) }); } // --- Additional rule: if more than 1/3 of the entire grid is selected, // treat it as an abnormal case and return false immediately. var globalRatio = coords.length / (size * size); if (globalRatio > (1/3)) { return false; } // --- Normal density calculation based on bounding box --- var xs = coords.map(function (c) { return c.x; }); var ys = coords.map(function (c) { return c.y; }); // 2. Compute bounding box of selected cells var minX = Math.min.apply(Math, xs), maxX = Math.max.apply(Math, xs); var minY = Math.min.apply(Math, ys), maxY = Math.max.apply(Math, ys); // 3. Compute area and density var w = maxX - minX + 1; var h = maxY - minY + 1; var boxArea = w * h; var density = coords.length / boxArea; return density >= minDensity; } function estimateTileStartPosition(index, tiles, spreadSize, gap) { if (typeof tiles !== 'number' || tiles <= 0) return null; if (typeof spreadSize !== 'number' || !isFinite(spreadSize)) return null; if (typeof gap !== 'number' || !isFinite(gap) || gap < 0) gap = 0; if (typeof index !== 'number' || !isFinite(index) || index % 1 !== 0 || index < 1) return null; var totalSlots = tiles * tiles; if (index > totalSlots) return null; var tileSize = (spreadSize - gap * (tiles - 1)) / tiles; if (!isFinite(tileSize)) return null; var i = index - 1; var col = i % tiles; var row = Math.floor(i / tiles); return { x: Math.max(0, col) * (tileSize + gap), y: Math.max(0, row) * (tileSize + gap) }; } exports.DTM = DTM; exports.arrayCos = arrayCos; exports.measureSimilarity = measureSimilarity; exports.export_measureSimilarity = export_measureSimilarity; exports.cartesianProduct = cartesianProduct; exports.clusteredCellsDensity = clusteredCellsDensity; exports.estimateTileStartPosition = estimateTileStartPosition; exports.VERSIONINFO = "ExtraMath module (extramath.js) version 1.0.4"; exports.AUTHOR = "gnh1201@catswords.re.kr"; exports.global = global; exports.require = global.require;