{"id":24314905,"url":"https://github.com/karrick/gorpn","last_synced_at":"2025-09-26T21:30:47.048Z","repository":{"id":57487008,"uuid":"44262342","full_name":"karrick/gorpn","owner":"karrick","description":"RPN expression evaluator for Go","archived":false,"fork":false,"pushed_at":"2019-05-15T18:47:00.000Z","size":96,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-11T05:24:26.532Z","etag":null,"topics":["evaluator","golang","golang-library","rpn-expression"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karrick.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-10-14T16:48:39.000Z","updated_at":"2020-08-24T12:37:58.000Z","dependencies_parsed_at":"2022-09-01T22:51:15.142Z","dependency_job_id":null,"html_url":"https://github.com/karrick/gorpn","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karrick%2Fgorpn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karrick%2Fgorpn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karrick%2Fgorpn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karrick%2Fgorpn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karrick","download_url":"https://codeload.github.com/karrick/gorpn/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234345365,"owners_count":18817558,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["evaluator","golang","golang-library","rpn-expression"],"created_at":"2025-01-17T10:16:09.973Z","updated_at":"2025-09-26T21:30:41.801Z","avatar_url":"https://github.com/karrick.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gorpn\n\nRPN expression evaluator for Go\n\n## References\n\n- http://oss.oetiker.ch/rrdtool/doc/rrdgraph_rpn.en.html\n\n- http://linux.die.net/man/1/cdeftutorial\n\n## Usage Examples\n\n### Simple\n\nFor RPN expressions that contain all the required data to calculate the result, simply create an\nexpression and evaluate it.\n\n```Go\n    expression, err := gorpn.New(\"60,24,*\")\n    if err != nil {\n        panic(err)\n    }\n    result, err := expression.Evaluate(nil)\n    if err != nil {\n        panic(err)\n    }\n```\n\n### With Variable Bindings\n\nFor RPN expressions that do _not_ contain all the required data, and require variable bindings,\nprovide them in the form of a map of string variable names to their respective numerical values.\n\n```Go\n    expression, err := gorpn.New(\"12,age,*\")\n    if err != nil {\n        panic(err)\n    }\n    bindings := map[string]interface{} {\n        \"age\": 21,\n    }\n    result, err := expression.Evaluate(bindings)\n    if err != nil {\n        panic(err)\n    }\n```\n\n## Supported Features\n\n### Algebraic Functions\n\n * +, -, *, /, %\n * ABS\n * ADDNAN (add, but if one num is NaN/UNK, treat it as zero. if both NaN/UNK, then return NaN/UNK)\n * ATAN (output in radians)\n * ATAN2 (output in radians)\n * CEIL\n * COS (input in radians)\n * DEG2RAD\n * EXP: a,EXP -\u003e a^_e_, where _e_ is the natural number\n * FLOOR\n * LOG: a,LOG -\u003e log base _e_ of a, where _e_ is the natural number\n * POW: a,b,POW -\u003e a^b\n * RAD2DEG\n * SIN (input in radians)\n * SMAX: a,b,c,3,SMAX -\u003e max(a,b,c)\n * SMIN: a,b,c,3,SMIN -\u003e min(a,b,c)\n * SQRT\n\n### Boolean Functions\n\nEach logical function pushes 1 for 0, and 0 for false.\n\n * EQ (=)\n * GE (\u003e=)\n * GT (\u003e)\n * IF (treats 0, UNK, and ±Inf as false)\n * ISINF (is top ±Inf)\n * LE (\u003c=)\n * LT (\u003c)\n * NE (!=)\n * UN (is top of stack UNK?)\n\n### Comparing Values\n\nPop two elements from the stack and pushes back the larger or smaller\nelement, depending on the name.  Infinite is larger than every other\nnumber.  If either of the numbers are UNK, then pushes UNK back on the\nstack.\n\n * MAX\n * MIN\n\nThese versions work similar to above, with the exception that if\neither of the two numbers popped off the stack are UNK, then it pushes\nthe other number.\n\n * MAXNAN\n * MINNAN \n\n### Set Operations\n\n * count,SORT: Pop count of items, then pop that many items. Sort, then push all items back.\n * count,REV: Pop count of items, then pop that many items. Reverse, then push all items back.\n * count,AVG: Pop count of items, then compute mean, ignoring all UNK. Push mean back.\n * count,MAD: a,b,c,3,MAD -\u003e median absolute deviation of [a, b, c]\n * count,MEDIAN: a,b,c,3,MEDIAN -\u003e median of [a, b, c]\n * percentile,count,PERCENT: a,b,c,95,3,PERCENT -\u003e find 95percentile of a,b,c using the nearest rank method (https://en.wikipedia.org/wiki/Percentile)\n * count,STDEV: a,b,c,3,STDEV -\u003e stdev(a,b,c), ignoring all UNK\n * count,TREND: create a \"sliding window\" average of another data series\n * count,TRENDNAN: create a \"sliding window\" average of another data series\n\n### Other Supported Constants and Functions\n\n * DAY: number of seconds in a day\n * HOUR: number of seconds in an hour\n * INF: push +Inf on stack\n * LIMIT: pop 2 and define inclusive range. pop third. if third in range, push it back, otherwise push UNK. if any of 3 numbers is UNK or ±Inf, push UNK\n * MINUTE: number of seconds in a minute\n * NEGINF: push -Inf on stack\n * NOW: push number of seconds since epoch\n * STEPWIDTH: current step measured in seconds\n * UNKN: push UNK\n * WEEK: number of seconds in a week\n\n### Features Supported with Variable Binding\n\nThe following features are supported, however they only make sense while evaluating in the context\nof a set of bindings. See below for more information.\n\n * COUNT\n * LTIME\n * NEWDAY: push 1 if datum is first datum for day\n * NEWMONTH: push 1 if datum is first datum for month\n * NEWWEEK: push 1 if datum is first datum for week\n * NEWYEAR: push 1 if datum is first datum for year\n * TIME\n\n### Stack Manipulation\n\n * n,COPY: push a copy of the top _n_ elements onto the stack\n * DEPTH: pushes the current depth of the stack onto the stack\n * DUP: duplicate value on top of stack\n * EXC: exchange top two items on stack\n * n,INDEX: push the _nth_ element onto the stack\n * POP: discard top element of stack\n * n,m,ROLL: rotate the top _n_ elements of the stack by _m_\n\n## Unsupported Features\n\nThe following features have yet to be implemented in this library.\n\n * PREDICT\n * PREDICTSIGMA\n * PREV\n * PREV(vname)\n\n## Variable Binding\n\nIt is useful to send an RPN expression, along with a map of variable names to their respective\nvalues, to the Evaluate method to calculate the expression value in the context of the provided\nbindings.\n\nRecall the simple example above, where the RPN expression `60,24,*` does not need any bindings\nbecause all the information required to calculate the result is in the expression. Likewise, the\nexpression `1,2,3,4,5,6,7,8,9,10,AVG` contains all the information needed to calculate the result.\n\n```Go\n    expression, err := gorpn.New(\"60,24,*\")\n    if err != nil {\n        panic(err)\n    }\n    result, err := expression.Evaluate(nil)\n    if err != nil {\n        panic(err)\n    }\n```\n\nHowever, the RPN expression `12,age,/` requires a binding of the term `age` to its numerical value\nin order to evaluate the final result.\n\nVariable bindings are supported by providing a map of string names to their numerical values to the\nEvaluate method. Recall that when no bindings are needed, the `nil` value may be sent to Evaluate to\nfind the RPN result. In the example below,\n\n```Go\n    type Datum struct {\n        when time.Time\n        what float64\n    }\n\n    type Series []Datum\n\n    func getValueAtTime(when time.Time, series Series) float64 {\n        // magic, returns math.NaN() when value not available for time\n    }\n\n    func example(start, end time.Time, interval time.Duration, data map[string]Series) {\n        for when := start; end.Before(when); when.Add(interval) {\n            bindings := make(map[string]interface{})\n\n            for label, series := range data {\n                bindings[label] = getValueAtTime(when, series)\n            }\n\n            value, err := exp.Evaluate(bindings)\n            // handle error...\n        }\n    }\n```\n\n## Features Supported with Variable Binding\n\n### COUNT\n\nWhen evaluating an RPN expression, the evaluator for a single expression does not know how many\nother expressions have been evaluated. The program that is requesting the evaluation must provide\nthat information in the form of a binding variable.\n\n### LTIME and TIME, contrasted against NOW\n\nThe NOW pseudo-variable is _always_ available during evaulation because it's the number of seconds\nsince the UNIX epoch at the moment of evaluation. In contrast, TIME in RRD parlance, refers to the\ntime associated with a particular datum. As a program loops through a bunch of time+value datum\ntuples, it will need to bind the time to the TIME symbol in the bindings map provided to Evaluate.\n\nThe RPN evaluator does not know the time a particular datum was obtained; that must be provided at\nthe time of evaluation. But once TIME is provided, other pseudo-variables are available for\nevaluation, including LTIME, NEWDAY, NEWWEEK, NEWMONTH, and NEWYEAR.\n\nLTIME, like TIME, corresponds to the time associated with a particular datum. It is calculated from\nthe bound TIME value provided in the bindings to Evaluate.\n\n```Go\n    // as before...\n\n    func example(start, end time.Time, interval time.Duration, data map[string]Series) {\n        var count int\n        for when := start; end.Before(when); when.Add(interval) {\n            count++ // according to the RRD spec, count starts at 1 for the first item in the series\n\n            bindings := make(map[string]interface{})\n            bindings[\"COUNT\"] = count\n            bindings[\"TIME\"] = when.Unix()\n\n            for label, series := range data {\n                bindings[label] = getValueAtTime(when, series)\n            }\n\n            value, err := exp.Evaluate(bindings)\n            ...\n        }\n    }\n```\n\n# Implementation Notes\n\n## UNKN implemented as NaN.\n\nPerhaps this ought to change. I'm not sure. It seems like it is a decent compromise for now. Let me\nknow if you experience problems using this library because of this assumption. It may change in the\nfuture. If it does, I hope the library API will remain constant.\n\n```Go\n    for _, tm := range times {\n        bindings := map[string]interface{}{\n            \"TIME\": tm,\n        }\n        value, err := exp.Evaluate(bindings)\n        ...\n    }\n```\n\n## PREV\n\nPushes an unknown value if this is the first value of a data set or otherwise the result of this\nCDEF at the previous time step. This allows you to do calculations across the data. This function\ncannot be used in VDEF instructions.\n\n## PREV(vname)\n\nRequires COUNT binding.\n\nPushes an unknown value if this is the first value of a data set or otherwise the result of the\nvname variable at the previous time step. This allows you to do calculations across the data. This\nfunction cannot be used in VDEF instructions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarrick%2Fgorpn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarrick%2Fgorpn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarrick%2Fgorpn/lists"}