mulmoclaude 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -0
- package/bin/mulmoclaude.js +202 -0
- package/bin/prepare-dist.js +93 -0
- package/client/assets/chunk-vKJrgz-R-C_I3GbVV.js +1 -0
- package/client/assets/html2canvas-Cx501zZr-BF5dYYkY.js +5 -0
- package/client/assets/index-D8rhwXLq.js +4906 -0
- package/client/assets/index-KNLBjwuh.css +1 -0
- package/client/assets/index.es-D4YyL_Dg-BfRHLTZV.js +5 -0
- package/client/assets/material-icons-Dr0goTwe.woff +0 -0
- package/client/assets/material-icons-kAwBdRge.woff2 +0 -0
- package/client/assets/material-icons-outlined-BpWbwl2n.woff +0 -0
- package/client/assets/material-icons-outlined-DZhiGvEA.woff2 +0 -0
- package/client/assets/material-icons-round-BDlwx-sv.woff +0 -0
- package/client/assets/material-icons-round-DrirKXBx.woff2 +0 -0
- package/client/assets/material-icons-sharp-CH1KkVu7.woff +0 -0
- package/client/assets/material-icons-sharp-gidztirS.woff2 +0 -0
- package/client/assets/material-icons-two-tone-B7wz7mED.woff +0 -0
- package/client/assets/material-icons-two-tone-DuNIpaEj.woff2 +0 -0
- package/client/assets/mulmo_bw-ERmkSv0a.png +0 -0
- package/client/assets/purify.es-Fx1Nqyry-PeS5RUhs.js +2 -0
- package/client/assets/typeof-DBp4T-Ny-BC0P-2DM.js +1 -0
- package/client/index.html +28 -0
- package/package.json +66 -0
- package/server/agent/attachmentConverter.ts +270 -0
- package/server/agent/config.ts +414 -0
- package/server/agent/index.ts +260 -0
- package/server/agent/mcp-server.ts +412 -0
- package/server/agent/mcp-tools/index.ts +63 -0
- package/server/agent/mcp-tools/x.ts +188 -0
- package/server/agent/plugin-names.ts +75 -0
- package/server/agent/prompt.ts +349 -0
- package/server/agent/resumeFailover.ts +129 -0
- package/server/agent/sandboxMounts.ts +329 -0
- package/server/agent/stream.ts +194 -0
- package/server/api/auth/bearerAuth.ts +61 -0
- package/server/api/auth/token.ts +98 -0
- package/server/api/csrfGuard.ts +85 -0
- package/server/api/routes/agent.ts +478 -0
- package/server/api/routes/chart.ts +98 -0
- package/server/api/routes/chat-index.ts +46 -0
- package/server/api/routes/config.ts +258 -0
- package/server/api/routes/dispatchResponse.ts +79 -0
- package/server/api/routes/files.ts +812 -0
- package/server/api/routes/html.ts +101 -0
- package/server/api/routes/image.ts +169 -0
- package/server/api/routes/mulmo-script.ts +712 -0
- package/server/api/routes/mulmoScriptValidate.ts +101 -0
- package/server/api/routes/notifications.ts +69 -0
- package/server/api/routes/pdf.ts +163 -0
- package/server/api/routes/plugins.ts +276 -0
- package/server/api/routes/presentHtml.ts +48 -0
- package/server/api/routes/roles.ts +125 -0
- package/server/api/routes/scheduler.ts +153 -0
- package/server/api/routes/schedulerHandlers.ts +151 -0
- package/server/api/routes/schedulerTasks.ts +163 -0
- package/server/api/routes/sessions.ts +294 -0
- package/server/api/routes/sessionsCursor.ts +59 -0
- package/server/api/routes/skills.ts +195 -0
- package/server/api/routes/sources.ts +540 -0
- package/server/api/routes/todos.ts +263 -0
- package/server/api/routes/todosColumnsHandlers.ts +347 -0
- package/server/api/routes/todosHandlers.ts +274 -0
- package/server/api/routes/todosItemsHandlers.ts +386 -0
- package/server/api/routes/wiki/pageIndex.ts +53 -0
- package/server/api/routes/wiki.ts +363 -0
- package/server/api/sandboxStatus.ts +64 -0
- package/server/events/notifications.ts +160 -0
- package/server/events/pub-sub/index.ts +45 -0
- package/server/events/relay-client.ts +288 -0
- package/server/events/scheduler-adapter.ts +302 -0
- package/server/events/session-store/index.ts +492 -0
- package/server/events/task-manager/index.ts +181 -0
- package/server/index.ts +572 -0
- package/server/system/config.ts +243 -0
- package/server/system/credentials.ts +220 -0
- package/server/system/docker.ts +97 -0
- package/server/system/env.ts +109 -0
- package/server/system/logger/config.ts +112 -0
- package/server/system/logger/formatters.ts +40 -0
- package/server/system/logger/index.ts +53 -0
- package/server/system/logger/rotation.ts +37 -0
- package/server/system/logger/sinks.ts +101 -0
- package/server/system/logger/types.ts +29 -0
- package/server/utils/date.ts +57 -0
- package/server/utils/errors.ts +7 -0
- package/server/utils/fetch.ts +27 -0
- package/server/utils/files/atomic.ts +125 -0
- package/server/utils/files/html-io.ts +20 -0
- package/server/utils/files/image-store.ts +66 -0
- package/server/utils/files/index.ts +45 -0
- package/server/utils/files/journal-io.ts +213 -0
- package/server/utils/files/json.ts +69 -0
- package/server/utils/files/markdown-store.ts +33 -0
- package/server/utils/files/naming.ts +50 -0
- package/server/utils/files/reference-dirs-io.ts +45 -0
- package/server/utils/files/roles-io.ts +45 -0
- package/server/utils/files/safe.ts +106 -0
- package/server/utils/files/scheduler-io.ts +20 -0
- package/server/utils/files/scheduler-overrides-io.ts +64 -0
- package/server/utils/files/session-io.ts +136 -0
- package/server/utils/files/spreadsheet-store.ts +63 -0
- package/server/utils/files/todos-io.ts +29 -0
- package/server/utils/files/user-tasks-io.ts +25 -0
- package/server/utils/files/workspace-io.ts +221 -0
- package/server/utils/gemini.ts +59 -0
- package/server/utils/gitignore.ts +69 -0
- package/server/utils/http.ts +15 -0
- package/server/utils/httpError.ts +61 -0
- package/server/utils/id.ts +16 -0
- package/server/utils/json.ts +83 -0
- package/server/utils/logBackgroundError.ts +22 -0
- package/server/utils/markdown.ts +82 -0
- package/server/utils/request.ts +29 -0
- package/server/utils/slug.ts +50 -0
- package/server/utils/spawn.ts +62 -0
- package/server/utils/time.ts +34 -0
- package/server/utils/types.ts +47 -0
- package/server/workspace/chat-index/index.ts +153 -0
- package/server/workspace/chat-index/indexer.ts +209 -0
- package/server/workspace/chat-index/paths.ts +34 -0
- package/server/workspace/chat-index/summarizer.ts +247 -0
- package/server/workspace/chat-index/types.ts +38 -0
- package/server/workspace/custom-dirs.ts +220 -0
- package/server/workspace/helps/business.md +104 -0
- package/server/workspace/helps/github.md +23 -0
- package/server/workspace/helps/index.md +60 -0
- package/server/workspace/helps/mulmoscript.md +249 -0
- package/server/workspace/helps/sandbox.md +90 -0
- package/server/workspace/helps/spreadsheet.md +43 -0
- package/server/workspace/helps/telegram.md +135 -0
- package/server/workspace/helps/wiki.md +131 -0
- package/server/workspace/journal/archivist.ts +386 -0
- package/server/workspace/journal/dailyPass.ts +743 -0
- package/server/workspace/journal/diff.ts +71 -0
- package/server/workspace/journal/index.ts +185 -0
- package/server/workspace/journal/indexFile.ts +136 -0
- package/server/workspace/journal/linkRewrite.ts +4 -0
- package/server/workspace/journal/memoryExtractor.ts +130 -0
- package/server/workspace/journal/optimizationPass.ts +160 -0
- package/server/workspace/journal/paths.ts +76 -0
- package/server/workspace/journal/state.ts +125 -0
- package/server/workspace/paths.ts +158 -0
- package/server/workspace/reference-dirs.ts +252 -0
- package/server/workspace/roles.ts +37 -0
- package/server/workspace/skills/discovery.ts +125 -0
- package/server/workspace/skills/index.ts +10 -0
- package/server/workspace/skills/parser.ts +144 -0
- package/server/workspace/skills/paths.ts +41 -0
- package/server/workspace/skills/scheduler.ts +149 -0
- package/server/workspace/skills/types.ts +30 -0
- package/server/workspace/skills/user-tasks.ts +257 -0
- package/server/workspace/skills/writer.ts +189 -0
- package/server/workspace/sources/arxivDiscovery.ts +182 -0
- package/server/workspace/sources/classifier.ts +268 -0
- package/server/workspace/sources/fetchers/arxiv.ts +170 -0
- package/server/workspace/sources/fetchers/github.ts +106 -0
- package/server/workspace/sources/fetchers/githubIssues.ts +208 -0
- package/server/workspace/sources/fetchers/githubReleases.ts +186 -0
- package/server/workspace/sources/fetchers/index.ts +71 -0
- package/server/workspace/sources/fetchers/registerAll.ts +15 -0
- package/server/workspace/sources/fetchers/rss.ts +141 -0
- package/server/workspace/sources/fetchers/rssParser.ts +295 -0
- package/server/workspace/sources/httpFetcher.ts +230 -0
- package/server/workspace/sources/interests.ts +120 -0
- package/server/workspace/sources/paths.ts +110 -0
- package/server/workspace/sources/pipeline/dedup.ts +60 -0
- package/server/workspace/sources/pipeline/fetch.ts +136 -0
- package/server/workspace/sources/pipeline/index.ts +249 -0
- package/server/workspace/sources/pipeline/notify.ts +72 -0
- package/server/workspace/sources/pipeline/plan.ts +66 -0
- package/server/workspace/sources/pipeline/summarize.ts +189 -0
- package/server/workspace/sources/pipeline/write.ts +185 -0
- package/server/workspace/sources/rateLimiter.ts +148 -0
- package/server/workspace/sources/registry.ts +326 -0
- package/server/workspace/sources/robots.ts +271 -0
- package/server/workspace/sources/sourceState.ts +135 -0
- package/server/workspace/sources/taxonomy.ts +74 -0
- package/server/workspace/sources/types.ts +144 -0
- package/server/workspace/sources/urls.ts +112 -0
- package/server/workspace/tool-trace/classify.ts +114 -0
- package/server/workspace/tool-trace/index.ts +250 -0
- package/server/workspace/tool-trace/writeSearch.ts +98 -0
- package/server/workspace/wiki-backlinks/index.ts +107 -0
- package/server/workspace/wiki-backlinks/sessionBacklinks.ts +144 -0
- package/server/workspace/workspace.ts +66 -0
- package/src/App.vue +720 -0
- package/src/assets/mulmo_bw.png +0 -0
- package/src/components/CanvasViewToggle.vue +27 -0
- package/src/components/ChatAttachmentPreview.vue +45 -0
- package/src/components/ChatImagePreview.vue +17 -0
- package/src/components/ChatInput.vue +208 -0
- package/src/components/FileContentHeader.vue +49 -0
- package/src/components/FileContentRenderer.vue +162 -0
- package/src/components/FileTree.vue +115 -0
- package/src/components/FileTreePane.vue +85 -0
- package/src/components/FilesView.vue +206 -0
- package/src/components/LockStatusPopup.vue +111 -0
- package/src/components/NotificationBell.vue +131 -0
- package/src/components/NotificationToast.vue +72 -0
- package/src/components/PluginLauncher.vue +138 -0
- package/src/components/RightSidebar.vue +113 -0
- package/src/components/RoleSelector.vue +64 -0
- package/src/components/SessionHistoryPanel.vue +176 -0
- package/src/components/SessionTabBar.vue +81 -0
- package/src/components/SettingsMcpTab.vue +350 -0
- package/src/components/SettingsModal.vue +275 -0
- package/src/components/SettingsReferenceDirsTab.vue +173 -0
- package/src/components/SettingsWorkspaceDirsTab.vue +174 -0
- package/src/components/SidebarHeader.vue +69 -0
- package/src/components/StackView.vue +360 -0
- package/src/components/SuggestionsPanel.vue +65 -0
- package/src/components/TodoExplorer.vue +358 -0
- package/src/components/ToolResultsPanel.vue +77 -0
- package/src/components/todo/TodoAddDialog.vue +131 -0
- package/src/components/todo/TodoEditDialog.vue +47 -0
- package/src/components/todo/TodoEditPanel.vue +113 -0
- package/src/components/todo/TodoKanbanView.vue +249 -0
- package/src/components/todo/TodoListView.vue +79 -0
- package/src/components/todo/TodoTableView.vue +177 -0
- package/src/composables/useActiveSession.ts +40 -0
- package/src/composables/useAppApi.ts +45 -0
- package/src/composables/useCanvasViewMode.ts +121 -0
- package/src/composables/useChatScroll.ts +47 -0
- package/src/composables/useClickOutside.ts +26 -0
- package/src/composables/useClipboardCopy.ts +44 -0
- package/src/composables/useContentDisplay.ts +52 -0
- package/src/composables/useDebugBeat.ts +23 -0
- package/src/composables/useDynamicFavicon.ts +115 -0
- package/src/composables/useEventListeners.ts +42 -0
- package/src/composables/useExpandedDirs.ts +64 -0
- package/src/composables/useFaviconState.ts +30 -0
- package/src/composables/useFileSelection.ts +115 -0
- package/src/composables/useFileSortMode.ts +24 -0
- package/src/composables/useFileTree.ts +85 -0
- package/src/composables/useFreshPluginData.ts +89 -0
- package/src/composables/useHealth.ts +38 -0
- package/src/composables/useImeAwareEnter.ts +57 -0
- package/src/composables/useKeyNavigation.ts +60 -0
- package/src/composables/useMarkdownLinkHandler.ts +46 -0
- package/src/composables/useMarkdownMode.ts +17 -0
- package/src/composables/useMcpTools.ts +71 -0
- package/src/composables/useMergedSessions.ts +27 -0
- package/src/composables/useNotifications.ts +90 -0
- package/src/composables/usePdfDownload.ts +60 -0
- package/src/composables/usePendingCalls.ts +77 -0
- package/src/composables/usePubSub.ts +85 -0
- package/src/composables/useRightSidebar.ts +23 -0
- package/src/composables/useRoles.ts +34 -0
- package/src/composables/useSandboxStatus.ts +67 -0
- package/src/composables/useSelectedResult.ts +49 -0
- package/src/composables/useSessionDerived.ts +51 -0
- package/src/composables/useSessionHistory.ts +81 -0
- package/src/composables/useSessionSync.ts +57 -0
- package/src/composables/useViewLayout.ts +55 -0
- package/src/config/apiRoutes.ts +173 -0
- package/src/config/pubsubChannels.ts +45 -0
- package/src/config/roles.ts +335 -0
- package/src/config/schedulerActions.ts +25 -0
- package/src/config/toolNames.ts +71 -0
- package/src/config/workspacePaths.ts +24 -0
- package/src/index.css +107 -0
- package/src/main.ts +25 -0
- package/src/plugins/canvas/Preview.vue +13 -0
- package/src/plugins/canvas/View.vue +333 -0
- package/src/plugins/canvas/definition.ts +38 -0
- package/src/plugins/canvas/index.ts +36 -0
- package/src/plugins/chart/Preview.vue +49 -0
- package/src/plugins/chart/View.vue +143 -0
- package/src/plugins/chart/definition.ts +58 -0
- package/src/plugins/chart/index.ts +52 -0
- package/src/plugins/editImage/Preview.vue +13 -0
- package/src/plugins/editImage/View.vue +13 -0
- package/src/plugins/editImage/definition.ts +27 -0
- package/src/plugins/editImage/index.ts +36 -0
- package/src/plugins/generateImage/Preview.vue +13 -0
- package/src/plugins/generateImage/View.vue +33 -0
- package/src/plugins/generateImage/definition.ts +32 -0
- package/src/plugins/generateImage/index.ts +56 -0
- package/src/plugins/manageRoles/Preview.vue +49 -0
- package/src/plugins/manageRoles/View.vue +525 -0
- package/src/plugins/manageRoles/definition.ts +43 -0
- package/src/plugins/manageRoles/index.ts +47 -0
- package/src/plugins/manageSkills/Preview.vue +21 -0
- package/src/plugins/manageSkills/View.vue +321 -0
- package/src/plugins/manageSkills/definition.ts +49 -0
- package/src/plugins/manageSkills/index.ts +49 -0
- package/src/plugins/manageSource/Preview.vue +33 -0
- package/src/plugins/manageSource/View.vue +697 -0
- package/src/plugins/manageSource/definition.ts +63 -0
- package/src/plugins/manageSource/index.ts +66 -0
- package/src/plugins/markdown/Preview.vue +77 -0
- package/src/plugins/markdown/View.vue +476 -0
- package/src/plugins/markdown/definition.ts +50 -0
- package/src/plugins/markdown/index.ts +36 -0
- package/src/plugins/presentHtml/Preview.vue +25 -0
- package/src/plugins/presentHtml/View.vue +52 -0
- package/src/plugins/presentHtml/definition.ts +27 -0
- package/src/plugins/presentHtml/helpers.ts +72 -0
- package/src/plugins/presentHtml/index.ts +41 -0
- package/src/plugins/presentMulmoScript/Preview.vue +23 -0
- package/src/plugins/presentMulmoScript/View.vue +1166 -0
- package/src/plugins/presentMulmoScript/definition.ts +95 -0
- package/src/plugins/presentMulmoScript/helpers.ts +162 -0
- package/src/plugins/presentMulmoScript/index.ts +40 -0
- package/src/plugins/scheduler/Preview.vue +67 -0
- package/src/plugins/scheduler/TasksTab.vue +205 -0
- package/src/plugins/scheduler/View.vue +565 -0
- package/src/plugins/scheduler/definition.ts +57 -0
- package/src/plugins/scheduler/index.ts +45 -0
- package/src/plugins/scheduler/viewModes.ts +26 -0
- package/src/plugins/spreadsheet/Preview.vue +29 -0
- package/src/plugins/spreadsheet/View.vue +997 -0
- package/src/plugins/spreadsheet/cellHighlights.ts +79 -0
- package/src/plugins/spreadsheet/definition.ts +121 -0
- package/src/plugins/spreadsheet/engine/calculator.ts +459 -0
- package/src/plugins/spreadsheet/engine/cellBuilder.ts +81 -0
- package/src/plugins/spreadsheet/engine/date-parser.ts +220 -0
- package/src/plugins/spreadsheet/engine/date-utils.ts +56 -0
- package/src/plugins/spreadsheet/engine/engine.ts +176 -0
- package/src/plugins/spreadsheet/engine/evaluator.ts +390 -0
- package/src/plugins/spreadsheet/engine/formatter.ts +172 -0
- package/src/plugins/spreadsheet/engine/formulaRefs.ts +101 -0
- package/src/plugins/spreadsheet/engine/functions/date.ts +299 -0
- package/src/plugins/spreadsheet/engine/functions/financial.ts +387 -0
- package/src/plugins/spreadsheet/engine/functions/index.ts +16 -0
- package/src/plugins/spreadsheet/engine/functions/logical.ts +262 -0
- package/src/plugins/spreadsheet/engine/functions/lookup.ts +400 -0
- package/src/plugins/spreadsheet/engine/functions/mathematical.ts +297 -0
- package/src/plugins/spreadsheet/engine/functions/statistical.ts +338 -0
- package/src/plugins/spreadsheet/engine/functions/text.ts +389 -0
- package/src/plugins/spreadsheet/engine/index.ts +27 -0
- package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +111 -0
- package/src/plugins/spreadsheet/engine/parser.ts +143 -0
- package/src/plugins/spreadsheet/engine/registry.ts +150 -0
- package/src/plugins/spreadsheet/engine/responseDecoder.ts +67 -0
- package/src/plugins/spreadsheet/engine/types.ts +64 -0
- package/src/plugins/spreadsheet/index.ts +36 -0
- package/src/plugins/textResponse/Preview.vue +94 -0
- package/src/plugins/textResponse/View.vue +503 -0
- package/src/plugins/textResponse/definition.ts +34 -0
- package/src/plugins/textResponse/index.ts +27 -0
- package/src/plugins/textResponse/plugin.ts +29 -0
- package/src/plugins/textResponse/samples.ts +97 -0
- package/src/plugins/textResponse/types.ts +11 -0
- package/src/plugins/todo/Preview.vue +63 -0
- package/src/plugins/todo/View.vue +364 -0
- package/src/plugins/todo/composables/useTodos.ts +177 -0
- package/src/plugins/todo/definition.ts +45 -0
- package/src/plugins/todo/index.ts +61 -0
- package/src/plugins/todo/labels.ts +163 -0
- package/src/plugins/todo/priority.ts +98 -0
- package/src/plugins/todo/viewModes.ts +19 -0
- package/src/plugins/ui-image/ImagePreview.vue +23 -0
- package/src/plugins/ui-image/ImageView.vue +34 -0
- package/src/plugins/ui-image/index.ts +3 -0
- package/src/plugins/ui-image/types.ts +4 -0
- package/src/plugins/wiki/Preview.vue +65 -0
- package/src/plugins/wiki/View.vue +342 -0
- package/src/plugins/wiki/definition.ts +25 -0
- package/src/plugins/wiki/helpers.ts +59 -0
- package/src/plugins/wiki/index.ts +52 -0
- package/src/router/guards.ts +61 -0
- package/src/router/index.ts +50 -0
- package/src/tools/index.ts +52 -0
- package/src/tools/types.ts +27 -0
- package/src/types/events.ts +16 -0
- package/src/types/fileTree.ts +13 -0
- package/src/types/notification.ts +67 -0
- package/src/types/session.ts +116 -0
- package/src/types/sse.ts +90 -0
- package/src/types/toolCallHistory.ts +13 -0
- package/src/utils/agent/eventDispatch.ts +74 -0
- package/src/utils/agent/request.ts +55 -0
- package/src/utils/agent/toolCalls.ts +62 -0
- package/src/utils/api.ts +218 -0
- package/src/utils/canvas/viewMode.ts +46 -0
- package/src/utils/dom/authTokenMeta.ts +20 -0
- package/src/utils/dom/clickOutside.ts +11 -0
- package/src/utils/dom/externalLink.ts +57 -0
- package/src/utils/dom/scrollable.ts +24 -0
- package/src/utils/errors.ts +11 -0
- package/src/utils/files/expandedDirs.ts +25 -0
- package/src/utils/files/filename.ts +12 -0
- package/src/utils/files/sortChildren.ts +20 -0
- package/src/utils/filesPreview/schedulerPreview.ts +38 -0
- package/src/utils/filesPreview/todoPreview.ts +40 -0
- package/src/utils/format/date.ts +85 -0
- package/src/utils/format/frontmatter.ts +80 -0
- package/src/utils/format/jsonSyntax.ts +109 -0
- package/src/utils/html/previewCsp.ts +65 -0
- package/src/utils/image/resolve.ts +8 -0
- package/src/utils/image/rewriteMarkdownImageRefs.ts +182 -0
- package/src/utils/markdown/extractFirstH1.ts +39 -0
- package/src/utils/notification/dispatch.ts +22 -0
- package/src/utils/path/relativeLink.ts +130 -0
- package/src/utils/role/icon.ts +20 -0
- package/src/utils/role/merge.ts +10 -0
- package/src/utils/role/plugins.ts +12 -0
- package/src/utils/session/mergeSessions.ts +103 -0
- package/src/utils/session/seedRoleDefault.ts +35 -0
- package/src/utils/session/sessionEntries.ts +121 -0
- package/src/utils/session/sessionFactory.ts +22 -0
- package/src/utils/session/sessionHelpers.ts +99 -0
- package/src/utils/tools/dedup.ts +17 -0
- package/src/utils/tools/mcp.ts +33 -0
- package/src/utils/tools/pendingCalls.ts +16 -0
- package/src/utils/tools/result.ts +40 -0
- package/src/utils/types.ts +44 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mathematical Functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { functionRegistry, toNumber, type FunctionHandler } from "../registry";
|
|
6
|
+
|
|
7
|
+
const roundHandler: FunctionHandler = (args, context) => {
|
|
8
|
+
if (args.length !== 2) throw new Error("ROUND requires 2 arguments");
|
|
9
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
10
|
+
const digits = toNumber(context.evaluateFormula(args[1]));
|
|
11
|
+
const multiplier = Math.pow(10, digits);
|
|
12
|
+
return Math.round(number * multiplier) / multiplier;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const roundupHandler: FunctionHandler = (args, context) => {
|
|
16
|
+
if (args.length !== 2) throw new Error("ROUNDUP requires 2 arguments");
|
|
17
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
18
|
+
const digits = toNumber(context.evaluateFormula(args[1]));
|
|
19
|
+
const multiplier = Math.pow(10, digits);
|
|
20
|
+
return Math.ceil(number * multiplier) / multiplier;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const rounddownHandler: FunctionHandler = (args, context) => {
|
|
24
|
+
if (args.length !== 2) throw new Error("ROUNDDOWN requires 2 arguments");
|
|
25
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
26
|
+
const digits = toNumber(context.evaluateFormula(args[1]));
|
|
27
|
+
const multiplier = Math.pow(10, digits);
|
|
28
|
+
return Math.floor(number * multiplier) / multiplier;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const floorHandler: FunctionHandler = (args, context) => {
|
|
32
|
+
if (args.length !== 2) throw new Error("FLOOR requires 2 arguments");
|
|
33
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
34
|
+
const significance = toNumber(context.evaluateFormula(args[1]));
|
|
35
|
+
if (significance === 0) return 0;
|
|
36
|
+
return Math.floor(number / significance) * significance;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const ceilingHandler: FunctionHandler = (args, context) => {
|
|
40
|
+
if (args.length !== 2) throw new Error("CEILING requires 2 arguments");
|
|
41
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
42
|
+
const significance = toNumber(context.evaluateFormula(args[1]));
|
|
43
|
+
if (significance === 0) return 0;
|
|
44
|
+
return Math.ceil(number / significance) * significance;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const absHandler: FunctionHandler = (args, context) => {
|
|
48
|
+
if (args.length !== 1) throw new Error("ABS requires 1 argument");
|
|
49
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
50
|
+
return Math.abs(number);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const powerHandler: FunctionHandler = (args, context) => {
|
|
54
|
+
if (args.length !== 2) throw new Error("POWER requires 2 arguments");
|
|
55
|
+
const base = toNumber(context.evaluateFormula(args[0]));
|
|
56
|
+
const exponent = toNumber(context.evaluateFormula(args[1]));
|
|
57
|
+
return Math.pow(base, exponent);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const sqrtHandler: FunctionHandler = (args, context) => {
|
|
61
|
+
if (args.length !== 1) throw new Error("SQRT requires 1 argument");
|
|
62
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
63
|
+
return Math.sqrt(number);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const modHandler: FunctionHandler = (args, context) => {
|
|
67
|
+
if (args.length !== 2) throw new Error("MOD requires 2 arguments");
|
|
68
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
69
|
+
const divisor = toNumber(context.evaluateFormula(args[1]));
|
|
70
|
+
if (divisor === 0) return 0;
|
|
71
|
+
return number % divisor;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const intHandler: FunctionHandler = (args, context) => {
|
|
75
|
+
if (args.length !== 1) throw new Error("INT requires 1 argument");
|
|
76
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
77
|
+
return Math.floor(number);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const truncHandler: FunctionHandler = (args, context) => {
|
|
81
|
+
if (args.length < 1 || args.length > 2) {
|
|
82
|
+
throw new Error("TRUNC requires 1 or 2 arguments");
|
|
83
|
+
}
|
|
84
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
85
|
+
const digits = args.length === 2 ? toNumber(context.evaluateFormula(args[1])) : 0;
|
|
86
|
+
const multiplier = Math.pow(10, digits);
|
|
87
|
+
return Math.trunc(number * multiplier) / multiplier;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const signHandler: FunctionHandler = (args, context) => {
|
|
91
|
+
if (args.length !== 1) throw new Error("SIGN requires 1 argument");
|
|
92
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
93
|
+
return Math.sign(number);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const piHandler: FunctionHandler = (args) => {
|
|
97
|
+
if (args.length !== 0) throw new Error("PI requires 0 arguments");
|
|
98
|
+
return Math.PI;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const expHandler: FunctionHandler = (args, context) => {
|
|
102
|
+
if (args.length !== 1) throw new Error("EXP requires 1 argument");
|
|
103
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
104
|
+
return Math.exp(number);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const lnHandler: FunctionHandler = (args, context) => {
|
|
108
|
+
if (args.length !== 1) throw new Error("LN requires 1 argument");
|
|
109
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
110
|
+
return Math.log(number);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const logHandler: FunctionHandler = (args, context) => {
|
|
114
|
+
if (args.length < 1 || args.length > 2) {
|
|
115
|
+
throw new Error("LOG requires 1 or 2 arguments");
|
|
116
|
+
}
|
|
117
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
118
|
+
const base = args.length === 2 ? toNumber(context.evaluateFormula(args[1])) : 10;
|
|
119
|
+
return Math.log(number) / Math.log(base);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const log10Handler: FunctionHandler = (args, context) => {
|
|
123
|
+
if (args.length !== 1) throw new Error("LOG10 requires 1 argument");
|
|
124
|
+
const number = toNumber(context.evaluateFormula(args[0]));
|
|
125
|
+
return Math.log10(number);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Register all mathematical functions
|
|
129
|
+
functionRegistry.register({
|
|
130
|
+
name: "ROUND",
|
|
131
|
+
handler: roundHandler,
|
|
132
|
+
minArgs: 2,
|
|
133
|
+
maxArgs: 2,
|
|
134
|
+
description: "Rounds a number to a specified number of digits",
|
|
135
|
+
examples: ["ROUND(3.14159, 2)", "ROUND(A1, 0)"],
|
|
136
|
+
category: "Mathematical",
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
functionRegistry.register({
|
|
140
|
+
name: "ROUNDUP",
|
|
141
|
+
handler: roundupHandler,
|
|
142
|
+
minArgs: 2,
|
|
143
|
+
maxArgs: 2,
|
|
144
|
+
description: "Rounds a number up, away from zero",
|
|
145
|
+
examples: ["ROUNDUP(3.14159, 2)", "ROUNDUP(A1, 0)"],
|
|
146
|
+
category: "Mathematical",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
functionRegistry.register({
|
|
150
|
+
name: "ROUNDDOWN",
|
|
151
|
+
handler: rounddownHandler,
|
|
152
|
+
minArgs: 2,
|
|
153
|
+
maxArgs: 2,
|
|
154
|
+
description: "Rounds a number down, toward zero",
|
|
155
|
+
examples: ["ROUNDDOWN(3.14159, 2)", "ROUNDDOWN(A1, 0)"],
|
|
156
|
+
category: "Mathematical",
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
functionRegistry.register({
|
|
160
|
+
name: "FLOOR",
|
|
161
|
+
handler: floorHandler,
|
|
162
|
+
minArgs: 2,
|
|
163
|
+
maxArgs: 2,
|
|
164
|
+
description: "Rounds a number down to the nearest multiple of significance",
|
|
165
|
+
examples: ["FLOOR(3.7, 1)", "FLOOR(24, 5)"],
|
|
166
|
+
category: "Mathematical",
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
functionRegistry.register({
|
|
170
|
+
name: "CEILING",
|
|
171
|
+
handler: ceilingHandler,
|
|
172
|
+
minArgs: 2,
|
|
173
|
+
maxArgs: 2,
|
|
174
|
+
description: "Rounds a number up to the nearest multiple of significance",
|
|
175
|
+
examples: ["CEILING(3.2, 1)", "CEILING(24, 5)"],
|
|
176
|
+
category: "Mathematical",
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
functionRegistry.register({
|
|
180
|
+
name: "ABS",
|
|
181
|
+
handler: absHandler,
|
|
182
|
+
minArgs: 1,
|
|
183
|
+
maxArgs: 1,
|
|
184
|
+
description: "Returns the absolute value of a number",
|
|
185
|
+
examples: ["ABS(-5)", "ABS(A1)"],
|
|
186
|
+
category: "Mathematical",
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
functionRegistry.register({
|
|
190
|
+
name: "POWER",
|
|
191
|
+
handler: powerHandler,
|
|
192
|
+
minArgs: 2,
|
|
193
|
+
maxArgs: 2,
|
|
194
|
+
description: "Returns the result of a number raised to a power",
|
|
195
|
+
examples: ["POWER(2, 3)", "POWER(A1, 2)"],
|
|
196
|
+
category: "Mathematical",
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
functionRegistry.register({
|
|
200
|
+
name: "SQRT",
|
|
201
|
+
handler: sqrtHandler,
|
|
202
|
+
minArgs: 1,
|
|
203
|
+
maxArgs: 1,
|
|
204
|
+
description: "Returns the square root of a number",
|
|
205
|
+
examples: ["SQRT(16)", "SQRT(A1)"],
|
|
206
|
+
category: "Mathematical",
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
functionRegistry.register({
|
|
210
|
+
name: "MOD",
|
|
211
|
+
handler: modHandler,
|
|
212
|
+
minArgs: 2,
|
|
213
|
+
maxArgs: 2,
|
|
214
|
+
description: "Returns the remainder after division",
|
|
215
|
+
examples: ["MOD(10, 3)", "MOD(A1, 2)"],
|
|
216
|
+
category: "Mathematical",
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
functionRegistry.register({
|
|
220
|
+
name: "INT",
|
|
221
|
+
handler: intHandler,
|
|
222
|
+
minArgs: 1,
|
|
223
|
+
maxArgs: 1,
|
|
224
|
+
description: "Rounds a number down to the nearest integer",
|
|
225
|
+
examples: ["INT(3.7)", "INT(A1)"],
|
|
226
|
+
category: "Mathematical",
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
functionRegistry.register({
|
|
230
|
+
name: "TRUNC",
|
|
231
|
+
handler: truncHandler,
|
|
232
|
+
minArgs: 1,
|
|
233
|
+
maxArgs: 2,
|
|
234
|
+
description: "Truncates a number to a specified number of decimal places",
|
|
235
|
+
examples: ["TRUNC(3.14159, 2)", "TRUNC(A1)"],
|
|
236
|
+
category: "Mathematical",
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
functionRegistry.register({
|
|
240
|
+
name: "SIGN",
|
|
241
|
+
handler: signHandler,
|
|
242
|
+
minArgs: 1,
|
|
243
|
+
maxArgs: 1,
|
|
244
|
+
description: "Returns the sign of a number (1, 0, or -1)",
|
|
245
|
+
examples: ["SIGN(-5)", "SIGN(A1)"],
|
|
246
|
+
category: "Mathematical",
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
functionRegistry.register({
|
|
250
|
+
name: "PI",
|
|
251
|
+
handler: piHandler,
|
|
252
|
+
minArgs: 0,
|
|
253
|
+
maxArgs: 0,
|
|
254
|
+
description: "Returns the value of pi (3.14159...)",
|
|
255
|
+
examples: ["PI()", "PI()*2"],
|
|
256
|
+
category: "Mathematical",
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
functionRegistry.register({
|
|
260
|
+
name: "EXP",
|
|
261
|
+
handler: expHandler,
|
|
262
|
+
minArgs: 1,
|
|
263
|
+
maxArgs: 1,
|
|
264
|
+
description: "Returns e raised to the power of a number",
|
|
265
|
+
examples: ["EXP(1)", "EXP(A1)"],
|
|
266
|
+
category: "Mathematical",
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
functionRegistry.register({
|
|
270
|
+
name: "LN",
|
|
271
|
+
handler: lnHandler,
|
|
272
|
+
minArgs: 1,
|
|
273
|
+
maxArgs: 1,
|
|
274
|
+
description: "Returns the natural logarithm of a number",
|
|
275
|
+
examples: ["LN(10)", "LN(A1)"],
|
|
276
|
+
category: "Mathematical",
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
functionRegistry.register({
|
|
280
|
+
name: "LOG",
|
|
281
|
+
handler: logHandler,
|
|
282
|
+
minArgs: 1,
|
|
283
|
+
maxArgs: 2,
|
|
284
|
+
description: "Returns the logarithm of a number to a specified base",
|
|
285
|
+
examples: ["LOG(100)", "LOG(8, 2)"],
|
|
286
|
+
category: "Mathematical",
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
functionRegistry.register({
|
|
290
|
+
name: "LOG10",
|
|
291
|
+
handler: log10Handler,
|
|
292
|
+
minArgs: 1,
|
|
293
|
+
maxArgs: 1,
|
|
294
|
+
description: "Returns the base-10 logarithm of a number",
|
|
295
|
+
examples: ["LOG10(100)", "LOG10(A1)"],
|
|
296
|
+
category: "Mathematical",
|
|
297
|
+
});
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistical Functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { functionRegistry, toNumber, parseCriteria, type FunctionContext, type FunctionHandler } from "../registry";
|
|
6
|
+
|
|
7
|
+
const isLetter = (char: string): boolean => /[A-Z]/i.test(char);
|
|
8
|
+
|
|
9
|
+
const isCellReference = (segment: string): boolean => {
|
|
10
|
+
if (!segment) return false;
|
|
11
|
+
let index = 0;
|
|
12
|
+
if (segment[index] === "$") index++;
|
|
13
|
+
const colStart = index;
|
|
14
|
+
while (index < segment.length && isLetter(segment[index])) {
|
|
15
|
+
index++;
|
|
16
|
+
}
|
|
17
|
+
if (index === colStart) return false; // Require at least one column letter
|
|
18
|
+
if (segment[index] === "$") index++;
|
|
19
|
+
if (index >= segment.length) return false; // Require row digits
|
|
20
|
+
for (; index < segment.length; index++) {
|
|
21
|
+
const char = segment[index];
|
|
22
|
+
if (char < "0" || char > "9") {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const isRangeReference = (value: string): boolean => {
|
|
30
|
+
if (!value) return false;
|
|
31
|
+
const rangePart = value.includes("!") ? value.split("!").slice(-1)[0] : value;
|
|
32
|
+
const [start, end] = rangePart.split(":");
|
|
33
|
+
if (!start || !end) return false;
|
|
34
|
+
return isCellReference(start) && isCellReference(end);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const collectNumericValues = (args: string[], context: FunctionContext): number[] => {
|
|
38
|
+
const values: number[] = [];
|
|
39
|
+
|
|
40
|
+
for (const rawArg of args) {
|
|
41
|
+
const arg = rawArg?.trim();
|
|
42
|
+
if (!arg) continue;
|
|
43
|
+
|
|
44
|
+
if (isRangeReference(arg)) {
|
|
45
|
+
const rangeValues = context.getRangeValues(arg).map(toNumber);
|
|
46
|
+
values.push(...rangeValues);
|
|
47
|
+
} else {
|
|
48
|
+
const evaluated = context.evaluateFormula(arg);
|
|
49
|
+
values.push(toNumber(evaluated));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return values;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const sumHandler: FunctionHandler = (args, context) => {
|
|
57
|
+
if (args.length !== 1) throw new Error("SUM requires 1 argument");
|
|
58
|
+
const values = context.getRangeValues(args[0]);
|
|
59
|
+
return values.reduce((sum: number, val) => sum + toNumber(val), 0);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const averageHandler: FunctionHandler = (args, context) => {
|
|
63
|
+
if (args.length !== 1) throw new Error("AVERAGE requires 1 argument");
|
|
64
|
+
const values = context.getRangeValues(args[0]);
|
|
65
|
+
if (values.length === 0) return 0;
|
|
66
|
+
const sum = values.reduce((acc: number, val) => acc + toNumber(val), 0);
|
|
67
|
+
return sum / values.length;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const maxHandler: FunctionHandler = (args, context) => {
|
|
71
|
+
if (args.length === 0) {
|
|
72
|
+
throw new Error("MAX requires at least 1 argument");
|
|
73
|
+
}
|
|
74
|
+
const values = collectNumericValues(args, context);
|
|
75
|
+
return values.length > 0 ? Math.max(...values) : 0;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const minHandler: FunctionHandler = (args, context) => {
|
|
79
|
+
if (args.length === 0) {
|
|
80
|
+
throw new Error("MIN requires at least 1 argument");
|
|
81
|
+
}
|
|
82
|
+
const values = collectNumericValues(args, context);
|
|
83
|
+
return values.length > 0 ? Math.min(...values) : 0;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const countHandler: FunctionHandler = (args, context) => {
|
|
87
|
+
if (args.length !== 1) throw new Error("COUNT requires 1 argument");
|
|
88
|
+
const values = context.getRangeValues(args[0]);
|
|
89
|
+
return values.length;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const medianHandler: FunctionHandler = (args, context) => {
|
|
93
|
+
if (args.length !== 1) throw new Error("MEDIAN requires 1 argument");
|
|
94
|
+
const values = context
|
|
95
|
+
.getRangeValues(args[0])
|
|
96
|
+
.map(toNumber)
|
|
97
|
+
.sort((a, b) => a - b);
|
|
98
|
+
|
|
99
|
+
if (values.length === 0) return 0;
|
|
100
|
+
const mid = Math.floor(values.length / 2);
|
|
101
|
+
return values.length % 2 === 0 ? (values[mid - 1] + values[mid]) / 2 : values[mid];
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const modeHandler: FunctionHandler = (args, context) => {
|
|
105
|
+
if (args.length !== 1) throw new Error("MODE requires 1 argument");
|
|
106
|
+
const values = context.getRangeValues(args[0]).map(toNumber);
|
|
107
|
+
|
|
108
|
+
if (values.length === 0) return 0;
|
|
109
|
+
|
|
110
|
+
// Count frequency of each value
|
|
111
|
+
const frequency = new Map<number, number>();
|
|
112
|
+
for (const val of values) {
|
|
113
|
+
frequency.set(val, (frequency.get(val) || 0) + 1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Find the value with highest frequency
|
|
117
|
+
let maxFreq = 0;
|
|
118
|
+
let mode = values[0];
|
|
119
|
+
for (const [val, freq] of frequency.entries()) {
|
|
120
|
+
if (freq > maxFreq) {
|
|
121
|
+
maxFreq = freq;
|
|
122
|
+
mode = val;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return mode;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const stdevHandler: FunctionHandler = (args, context) => {
|
|
130
|
+
if (args.length !== 1) throw new Error("STDEV requires 1 argument");
|
|
131
|
+
const values = context.getRangeValues(args[0]).map(toNumber);
|
|
132
|
+
|
|
133
|
+
if (values.length === 0) return 0;
|
|
134
|
+
|
|
135
|
+
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
|
|
136
|
+
const squaredDiffs = values.map((val) => Math.pow(val - mean, 2));
|
|
137
|
+
const variance = squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length;
|
|
138
|
+
return Math.sqrt(variance);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const varHandler: FunctionHandler = (args, context) => {
|
|
142
|
+
if (args.length !== 1) throw new Error("VAR requires 1 argument");
|
|
143
|
+
const values = context.getRangeValues(args[0]).map(toNumber);
|
|
144
|
+
|
|
145
|
+
if (values.length === 0) return 0;
|
|
146
|
+
|
|
147
|
+
const mean = values.reduce((sum, val) => sum + val, 0) / values.length;
|
|
148
|
+
const squaredDiffs = values.map((val) => Math.pow(val - mean, 2));
|
|
149
|
+
return squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const countaHandler: FunctionHandler = (args, context) => {
|
|
153
|
+
if (args.length !== 1) throw new Error("COUNTA requires 1 argument");
|
|
154
|
+
const values = context.getRangeValuesRaw?.(args[0]) ?? context.getRangeValues(args[0]);
|
|
155
|
+
// Count non-empty cells
|
|
156
|
+
return values.filter((v) => v !== null && v !== undefined && v !== "").length;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const countifHandler: FunctionHandler = (args, context) => {
|
|
160
|
+
if (args.length !== 2) throw new Error("COUNTIF requires 2 arguments");
|
|
161
|
+
const values = context.getRangeValuesRaw?.(args[0]) ?? context.getRangeValues(args[0]);
|
|
162
|
+
const criteria = args[1].trim();
|
|
163
|
+
const compareFn = parseCriteria(criteria);
|
|
164
|
+
return values.filter(compareFn).length;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const sumifHandler: FunctionHandler = (args, context) => {
|
|
168
|
+
if (args.length < 2 || args.length > 3) {
|
|
169
|
+
throw new Error("SUMIF requires 2 or 3 arguments");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const criteriaRange = context.getRangeValuesRaw?.(args[0]) ?? context.getRangeValues(args[0]);
|
|
173
|
+
const criteria = args[1].trim();
|
|
174
|
+
const sumRange = args.length === 3 ? context.getRangeValues(args[2]) : context.getRangeValues(args[0]);
|
|
175
|
+
|
|
176
|
+
const compareFn = parseCriteria(criteria);
|
|
177
|
+
|
|
178
|
+
let sum = 0;
|
|
179
|
+
for (let i = 0; i < criteriaRange.length; i++) {
|
|
180
|
+
if (compareFn(criteriaRange[i])) {
|
|
181
|
+
sum += toNumber(sumRange[i] ?? 0);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return sum;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const averageifHandler: FunctionHandler = (args, context) => {
|
|
189
|
+
if (args.length < 2 || args.length > 3) {
|
|
190
|
+
throw new Error("AVERAGEIF requires 2 or 3 arguments");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const criteriaRange = context.getRangeValuesRaw?.(args[0]) ?? context.getRangeValues(args[0]);
|
|
194
|
+
const criteria = args[1].trim();
|
|
195
|
+
const avgRange = args.length === 3 ? context.getRangeValues(args[2]) : context.getRangeValues(args[0]);
|
|
196
|
+
|
|
197
|
+
const compareFn = parseCriteria(criteria);
|
|
198
|
+
|
|
199
|
+
let sum = 0;
|
|
200
|
+
let count = 0;
|
|
201
|
+
for (let i = 0; i < criteriaRange.length; i++) {
|
|
202
|
+
if (compareFn(criteriaRange[i])) {
|
|
203
|
+
sum += toNumber(avgRange[i] ?? 0);
|
|
204
|
+
count++;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return count > 0 ? sum / count : 0;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Register all statistical functions
|
|
212
|
+
functionRegistry.register({
|
|
213
|
+
name: "SUM",
|
|
214
|
+
handler: sumHandler,
|
|
215
|
+
minArgs: 1,
|
|
216
|
+
maxArgs: 1,
|
|
217
|
+
description: "Returns the sum of all numbers in a range",
|
|
218
|
+
examples: ["SUM(A1:A10)", "SUM(B2:B20)"],
|
|
219
|
+
category: "Statistical",
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
functionRegistry.register({
|
|
223
|
+
name: "AVERAGE",
|
|
224
|
+
handler: averageHandler,
|
|
225
|
+
minArgs: 1,
|
|
226
|
+
maxArgs: 1,
|
|
227
|
+
description: "Returns the average (arithmetic mean) of numbers in a range",
|
|
228
|
+
examples: ["AVERAGE(A1:A10)", "AVERAGE(B2:B20)"],
|
|
229
|
+
category: "Statistical",
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
functionRegistry.register({
|
|
233
|
+
name: "MAX",
|
|
234
|
+
handler: maxHandler,
|
|
235
|
+
minArgs: 1,
|
|
236
|
+
description: "Returns the largest value in a range",
|
|
237
|
+
examples: ["MAX(A1:A10)", "MAX(B2:B20)"],
|
|
238
|
+
category: "Statistical",
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
functionRegistry.register({
|
|
242
|
+
name: "MIN",
|
|
243
|
+
handler: minHandler,
|
|
244
|
+
minArgs: 1,
|
|
245
|
+
description: "Returns the smallest value in a range",
|
|
246
|
+
examples: ["MIN(A1:A10)", "MIN(B2:B20)"],
|
|
247
|
+
category: "Statistical",
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
functionRegistry.register({
|
|
251
|
+
name: "COUNT",
|
|
252
|
+
handler: countHandler,
|
|
253
|
+
minArgs: 1,
|
|
254
|
+
maxArgs: 1,
|
|
255
|
+
description: "Counts the number of cells in a range",
|
|
256
|
+
examples: ["COUNT(A1:A10)", "COUNT(B2:B20)"],
|
|
257
|
+
category: "Statistical",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
functionRegistry.register({
|
|
261
|
+
name: "MEDIAN",
|
|
262
|
+
handler: medianHandler,
|
|
263
|
+
minArgs: 1,
|
|
264
|
+
maxArgs: 1,
|
|
265
|
+
description: "Returns the median (middle) value in a range",
|
|
266
|
+
examples: ["MEDIAN(A1:A10)", "MEDIAN(B2:B20)"],
|
|
267
|
+
category: "Statistical",
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
functionRegistry.register({
|
|
271
|
+
name: "MODE",
|
|
272
|
+
handler: modeHandler,
|
|
273
|
+
minArgs: 1,
|
|
274
|
+
maxArgs: 1,
|
|
275
|
+
description: "Returns the most frequently occurring value in a range",
|
|
276
|
+
examples: ["MODE(A1:A10)", "MODE(B2:B20)"],
|
|
277
|
+
category: "Statistical",
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
functionRegistry.register({
|
|
281
|
+
name: "STDEV",
|
|
282
|
+
handler: stdevHandler,
|
|
283
|
+
minArgs: 1,
|
|
284
|
+
maxArgs: 1,
|
|
285
|
+
description: "Returns the standard deviation of numbers in a range",
|
|
286
|
+
examples: ["STDEV(A1:A10)", "STDEV(B2:B20)"],
|
|
287
|
+
category: "Statistical",
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
functionRegistry.register({
|
|
291
|
+
name: "VAR",
|
|
292
|
+
handler: varHandler,
|
|
293
|
+
minArgs: 1,
|
|
294
|
+
maxArgs: 1,
|
|
295
|
+
description: "Returns the variance of numbers in a range",
|
|
296
|
+
examples: ["VAR(A1:A10)", "VAR(B2:B20)"],
|
|
297
|
+
category: "Statistical",
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
functionRegistry.register({
|
|
301
|
+
name: "COUNTA",
|
|
302
|
+
handler: countaHandler,
|
|
303
|
+
minArgs: 1,
|
|
304
|
+
maxArgs: 1,
|
|
305
|
+
description: "Counts the number of non-empty cells in a range",
|
|
306
|
+
examples: ["COUNTA(A1:A10)", "COUNTA(B2:B20)"],
|
|
307
|
+
category: "Statistical",
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
functionRegistry.register({
|
|
311
|
+
name: "COUNTIF",
|
|
312
|
+
handler: countifHandler,
|
|
313
|
+
minArgs: 2,
|
|
314
|
+
maxArgs: 2,
|
|
315
|
+
description: "Counts cells in a range that match a criteria",
|
|
316
|
+
examples: ['COUNTIF(A1:A10, ">5")', 'COUNTIF(B1:B10, "Yes")'],
|
|
317
|
+
category: "Statistical",
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
functionRegistry.register({
|
|
321
|
+
name: "SUMIF",
|
|
322
|
+
handler: sumifHandler,
|
|
323
|
+
minArgs: 2,
|
|
324
|
+
maxArgs: 3,
|
|
325
|
+
description: "Sums cells in a range that match a criteria",
|
|
326
|
+
examples: ['SUMIF(A1:A10, ">5")', 'SUMIF(A1:A10, ">5", B1:B10)'],
|
|
327
|
+
category: "Statistical",
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
functionRegistry.register({
|
|
331
|
+
name: "AVERAGEIF",
|
|
332
|
+
handler: averageifHandler,
|
|
333
|
+
minArgs: 2,
|
|
334
|
+
maxArgs: 3,
|
|
335
|
+
description: "Averages cells in a range that match a criteria",
|
|
336
|
+
examples: ['AVERAGEIF(A1:A10, ">5")', 'AVERAGEIF(A1:A10, ">5", B1:B10)'],
|
|
337
|
+
category: "Statistical",
|
|
338
|
+
});
|