{"id":22131186,"url":"https://github.com/clinaresl/table","last_synced_at":"2025-07-04T05:07:56.957Z","repository":{"id":165553820,"uuid":"291336480","full_name":"clinaresl/table","owner":"clinaresl","description":"A Go package for showing data in tabular form. It honours UTF-8 characters, ANSI color escape sequences, fixed- and variable-width columns, full/partial horizontal rules, various vertical and horizontal allignment options, and multicolumns","archived":false,"fork":false,"pushed_at":"2024-05-02T10:29:07.000Z","size":916,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-30T14:43:39.344Z","etag":null,"topics":["go","table","tabular"],"latest_commit_sha":null,"homepage":"","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/clinaresl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-08-29T19:33:00.000Z","updated_at":"2024-09-12T02:41:22.000Z","dependencies_parsed_at":"2023-06-11T01:15:43.658Z","dependency_job_id":"9c9fce60-0e6b-42e9-995d-db70bd675b11","html_url":"https://github.com/clinaresl/table","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clinaresl%2Ftable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clinaresl%2Ftable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clinaresl%2Ftable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clinaresl%2Ftable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clinaresl","download_url":"https://codeload.github.com/clinaresl/table/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227611277,"owners_count":17793536,"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":["go","table","tabular"],"created_at":"2024-12-01T18:31:11.288Z","updated_at":"2024-12-01T18:31:53.670Z","avatar_url":"https://github.com/clinaresl.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# table\n\nThis package implements means for drawing data in tabular form and it is\nintended as a substitution of the Go standard package `tabwriter`. Its design is\nbased on the functionality of tables in LaTeX but extends its functionality in\nvarious ways through a very simple interface\n\nIt honours [UTF-8 characters](https://www.utf8-chartable.de/), [ANSI color escape sequences](https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences), fixed- and variable-width columns, full/partial\nhorizontal rules, various vertical and horizontal alignment options, and\nmulticolumns.\n\nRemarkably, it prints any *stringer* and as tables are stringers, tables can be\nnested to any degree.\n\nWhile I do not want to delve into the [controversy between tabs and\nspaces](https://www.youtube.com/watch?v=SsoOG6ZeyUI) this is my position: \"*tabs\nshould be used for indentation while spaces should be preferred for alignment*\".\nThus, this package uses spaces for aligning contents and it should then be used\nwith fixed-size fonts.\n\n\n# Installation \n\nClone and install the `table` package with the following command:\n\n    $ go get github.com/clinaresl/table\n    \nTo try the different examples given in the package change dir to\n`$GOPATH/github.com/clinaresl/table` and type:\n\n    $ go test\n   \n# Usage #\n\nThis section provides various examples of usage with the hope of providing a\nflavour of the different capabilities of the package. For a full description of\nthe package check out the technical documentation.\n\n## First step: Create a table ##\n\nBefore inserting data to a new table it is necessary to create it first:\n\n```Go\n\tt, err := NewTable(\"l   p{25}\")\n\tif err != nil {\n\t\tlog.Fatalln(\" NewTable: Fatal error!\")\n\t}\n```\n\nThis snippet creates a table with two columns. The first one displays its\ncontents ragged left, whereas the second one takes a fixed width of 25\ncharacters to display the contents of each cell and, in case a cell exceeds the\navailable width, its contents are shown left ragged in as many lines as needed\n---i.e., `table` supports multi-line cells. In case it was not possible to\nsuccessfully process the *column specification*, an error is immediately\nreturned.\n\nThe following table shows all the different options for specifying the format of\na single column:\n\n| Syntax | Purpose |\n|:------:|:-------:|\n|  `l`   | the contents of the column are ragged left |\n|  `c`   | the contents of the column are horizontally aligned |\n|  `r`   | the contents of the column are ragged right |\n|  `p{NUMBER}` | the cell takes a fixed with equal to *NUMBER* characters and the contents are split across various lines if needed |\n|  `L{NUMBER}` | the width of the column does not exceed *NUMBER* characters and the contents are ragged left |\n|  `C{NUMBER}` | the width of the column does not exceed *NUMBER* characters and the contents are centered |\n|  `R{NUMBER}` | the width of the column does not exceed *NUMBER* characters and the contents are ragged right |\n\nThe *column specification* allows the usage of `|`, e.g.:\n\n``` Go\n\tt, _ := NewTable(\"|c|c|c|c|c|\")\n```\n\ncreates a table with five different columns all separated by a single vertical\nseparator. It is possible to create also *double* and *thick* vertical\nseparators using `||` and `|||` respectively. As a matter of fact, these are\njust shortcuts and the UTF-8 characters `│`, `║` and `┃` can be used\nrespectively. It is also possible to provide any other character (e.g., blank\nspaces) either before or after any column. These are then copied either before\nor after the contents of each cell in each row.\n\nIn case a second string is given to `NewTable` it is interpreted as the *row\nspecification*:\n\n```Go\n\tt, _ := NewTable(\"| c | c  c |\", \"cct\")\n```\n\nThis line (where no error checking is performed!) creates three different\ncolumns whose contents are horizontally centered surrounded by a single space\nand with vertical single separators between adjacent columns and before and\nafter the first and last column. In addition, it sets the *vertical alignment*\nof each cell as follows: the contents of the first and second columns are\nvertically centered (`c`), whereas the contents of the last column are pushed to\nthe top of the cell ---`t`. The modifiers available to be used in the *row\nspecification* are shown next:\n\n| Syntax | Purpose |\n|:------:|:-------:|\n|  `t`   | the contents of the column are aligned to the top |\n|  `c`   | the contents of the column are vertically aligned |\n|  `b`   | the contents of the column are aligned to the bottom |\n\nBy default, all columns are vertically aligned to the top. In case a *row\nspecification* is given it must refer to as many columns as there are in the\n*column specification* given first or less. In contraposition to the *column\nspecification*, the *row specification* can only consist of any of the modifiers\nshown above.\n   \n`NewTable` returns a pointer to `Table` which can be used next for adding data\nto it and, in the end, printing it.\n\n## Second step: Adding rows ##\n\n`table` acknowledges two different types of rows either horizontal rules or\nlines of data.\n\n### Adding horizontal rules ###\n\nThere are three different services for adding horizontal rules anywhere in a\ntable:\n\n```Go\n   func (t *Table) AddSingleRule(cols ...int) error\n   func (t *Table) AddDoubleRule(cols ...int) error\n   func (t *Table) AddThickRule(cols ...int) error\n```\n\nWhen invoked with no arguments they just show a full horizontal rule spanning\nover all columns of the table. Single rules are shown with the UTF-8 character\n`─`; double rules are drawn using `═`, and thick rules use `━`.\n\nIf they are invoked with arguments, then these are taken in pairs, each pair\nstanding for a *starting* and *ending* column numbered from 0, so that\nhorizontal rules are drawn only over those columns in the given range.\n\nIn case it is not possible to process the given arguments then an informative\nerror is returned.\n\nA couple of examples follow:\n\n``` Go\n    t, _ := NewTable(\"|c|c|c|c|c|\")\n    t.AddThickRule ()\n    t.AddSingleRule(0, 1, 2, 3, 4, 5)\n```\n\n### Adding data ###\n\nData is added to the bottom of a table with:\n\n``` Go\n    func (t *Table) AddRow(cells ...any) error\n```\n\nIt accepts an arbitrary number of *any* arguments and adds the result of the\n`Sprintf` operation of each argument to each cell of the last row of the table.\nIf the number of arguments is strictly less than the number of columns given in\nthe *column specification* then the remaining cells are left empty. Thus, if no\nargument is provided, an empty line of data is generated. However, if the number\nof arguments given is strictly larger than the number of columns of the table an\nerror is returned.\n\nThe following example adds data to a table with three columns: \n\n``` Go\n\tt, err := NewTable(\"| c || c ||| c |\")\n\terr = t.AddRow(\"Year\\n1979\", \"Year\\n2013\", \"Year\\n2018\")\n\tif err != nil {\n\t\tlog.Fatalln(\" AddRow: Fatal error!\")\n\t}\n\terr = t.AddRow(\"Ariane\", \"Gaia\\nProba Series\\nSwarm\", \"Aeolus\\nBepicolombo\\nMetop Series\")\n\tif err != nil {\n\t\tlog.Fatalln(\" AddRow: Fatal error!\")\n\t}\n```\n\nNote that the contents of any cell can contain any newline characters `\\n`. If\nso, the text is split in as many lines as needed, i.e., `table` supports\nmulti-line cells.\n\n## Third step: Printing tables ##\n\nThe last step consists of printing the contents of any table. By definition,\ntables are stringers and thus, all that is required is just to print the\ncontents with a `Print`-like function:\n\n``` Go\n\tt, _ := NewTable(\"l | r \")\n\tt.AddThickRule()\n\tt.AddRow(\"Country\", \"Population\")\n\tt.AddSingleRule()\n\tt.AddRow(\"China\", \"1,394,015,977\")\n\tt.AddRow(\"India\", \"1,326,093,247\")\n\tt.AddRow(\"United States\", \"329,877,505\")\n\tt.AddRow(\"Indonesia\", \"267,026,366\")\n\tt.AddRow(\"Pakistan\", \"233,500,636\")\n\tt.AddRow(\"Nigeria\", \"214,028,302\")\n\tt.AddThickRule()\n\tfmt.Printf(\"%v\", t)\n```\n\nWhich produces the result shown next (all examples are shown as images to avoid\nyour browser to show unrealistic renderings as a result of your preferences):\n\n![example-1](figs/example-1.png \"example-1\")\n\n\n# Gotchas #\n\nBeyond the basic usage of tables, `table` provides other features which are\ndescribed next\n\n## ANSI color codes ##\n\nOf course, `table` fully supports UTF-8 encoded characters, but it also manages\n[ANSI color escape\nsequences](https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences)\nprovided that they are supported by your terminal. \n\nThere are several Go packages that can actually produce the additional\ncharacters required to show the output in various forms, but this implementation\nis not tied to any, so that the following examples explicitly show the specific\nANSI color codes required to render each fragment:\n\n``` Go\n\tt, err := NewTable(\"r l c c c l c\")\n\tif err != nil {\n\t\tlog.Fatalf(\" NewTable: Fatal error (%v)\", err)\n\t}\n\tt.AddRow(\"\\033[36;3;4mID\", \"Age\", \"Project\", \"Tags\", \"Due\", \"Description\", \"Urg \\033[0m\")\n\tt.AddRow(1, \"8mo\", \"personal.programming.go\", \"program\", \"\\033[33;1m2022-10-21\\033[0m\", \"Document table\", 15.3)\n\tt.AddRow(2, \"3mo\", \"gii.cag\", \"video\", \"\\033[33;1m2022-04-11\\033[0m\", \"Create a video promoting UC3M\", 14.4)\n\tt.AddRow(3, \"6w\", \"research.editorial.review\", \"aicomm\", \"\\033[33;1m2023-05-30\\033[0m\", \"Review the latest papers\", 14.1)\n\n\tfmt.Printf(\"%v\", t)\n```\n\nwhich produces:\n\n![example-2](figs/example-2.png \"example-2\")\n\nMind the trick! The table contains no horizontal rule and the same effect is\ncreated by underlining the header as in [task warrior](https://taskwarrior.org/)\n---as a matter of fact, the example resembles the output of the command `task\nlist` of task warrior. \n\nMany other combinations and tricks are possible for improving presentations in\ntabular form. For example, the following snippet shows how to colour the\nvertical separators and horizontal rules also:\n\n``` Go\n\tt, err := NewTable(\"\\033[38;2;160;10;10m| c \\033[38;2;10;160;10m| c \\033[38;2;80;80;160m| c \\033[38;2;160;80;40m|\\033[0m\", \"cb\")\n\tif err != nil {\n\t\tlog.Fatalln(\" NewTable: Fatal error!\")\n\t}\n\tt.AddRow(\"\\033[38;2;206;10;0mPlayer\\033[0m\", \"\\033[38;2;10;206;0mYear\\033[0m\", \"\\033[38;2;100;0;206mTournament\\033[0m\")\n\tt.AddSingleRule()\n\tt.AddRow(\"\\033[38;5;206mRafa Nadal\\033[0m\", \"2010\", \"French Open\\nWimbledon\\nUS Open\")\n\tt.AddSingleRule()\n\tt.AddRow(\"Roger Federer\", \"2007\", \"Australian Open\\nWimbledon\\nUS Open\")\n\tt.AddSingleRule()\n\n\tfmt.Printf(\"%v\", t)\t\n```\n\nwhich is rendered as follows:\n\n![example-3](figs/example-3.png \"example-3\")\n\n\nMind (again) the trick! The ANSI color codes of each line including the headers\nare automatically ended with `\\033[0m` just simply by adding it to the *column\nspecification* of the table. Of course, one could end each line manually but as\nthe example shows this is not necessary at all.\n\n## Multicolumns ##\n\nMulticolumns are defined as ordinary cells which span over several columns in\nthe same row. Because `table` creates column-orientated tables, it is also\npossible to substitute an arbitrary number of columns in the table by a\ndifferent number of columns with a different format or, in other words,\nmulticolumns can be used both for *merging* or *splitting* columns. For example:\n\n\n``` Go\n\tt, _ := NewTable(\"l c c || c c\")\n\tt.AddRow(Multicolumn(5, \"c\", \"Table 2: Overall Results\"))\n\tt.AddThickRule()\n\tt.AddRow(\"\", Multicolumn(2, \"c\", \"Females\"), Multicolumn(2, \"c\", \"Males\"))\n\tt.AddSingleRule(1, 5)\n\tt.AddRow(\"Treatment\", \"Mortality\", \"Mean\\nPressure\", \"Mortality\", \"Mean\\nPressure\")\n\tt.AddSingleRule()\n\tt.AddRow(\"Placebo\", 0.21, 163, 0.22, 164)\n\tt.AddRow(\"ACE Inhibitor\", 0.13, 142, 0.15, 144)\n\tt.AddRow(\"Hydralazine\", 0.17, 143, 0.16, 140)\n\tt.AddThickRule()\n\tt.AddRow(Multicolumn(5, \"c\", \"Adapted from\\nhttps://tex.stackexchange.com/questions/314025/making-stats-table-with-multicolumn-and-cline\"))\n\tt.AddSingleRule()\n\tfmt.Printf(\"%v\", t)\n```\n\nresults in the following table:\n\n![example-4](figs/example-4.png \"example-4\")\n\nNote that multicolumns are created with the function `Multicolumn` which expects\nfirst, the number of columns it has to take; their format which has to be given\naccording to the rules discussed in [First step: Create a table](#usage); and\nfinally, the contents to be shown in the multicolumn. Because `Multicolumn`\naccepts any valid column specification in its second argument, `Multicolumn`\nserves to various purposes:\n\n1. *Merging* columns: the previous example shows how to *merge* different\n   columns into one. The table is originally defined with 5 different columns so\n   that the first, second and last row merge a number of columns into one whose\n   specification is given in the definition of the multicolumn.\n\n2. *Splitting* columns: the following example shows how to use `Multicolumn` to\n   create a single multicolumn which, however, consists of various columns,\n   effectively splitting the original column into others:\n   \n``` Go\n\tt, _ := NewTable(\"r|c|\")\n\tt.AddThickRule(1, 5)\n\tt.AddRow(\"\", Multicolumn(1, \"|c|c|c|c|c|\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\"))\n\tt.AddSingleRule()\n\tt.AddRow(\"9:00\", \"Enter school\")\n\tt.AddSingleRule()\n\tt.AddRow(\"13:00\", \"Lunch\")\n\tt.AddSingleRule()\n\tt.AddRow(\"14:30\", \"More classes\")\n\tt.AddSingleRule()\n\tt.AddRow(\"17:00\",\n\t\tMulticolumn(1, \"|c|c|\", \"Basketball\", \"Guitar\"))\n\tt.AddSingleRule()\n\tt.AddRow(\"18:30\", \"Go home!\")\n\tt.AddThickRule()\n\tfmt.Printf(\"%v\", t)\n```\n  \n  which produces:\n  \n![example-5](figs/example-5.png \"example-5\")  \n\n3. Also to selectively *modify the appearance* of the table at selected points.\n   In the following example, the boxes shown in the middle and the bottom are\n   created using multicolumns of width 1. In fact, these multicolumns are used\n   just for modifying the vertical separators so that the boxes are correctly\n   drawn. Much the same happens with the multicolumn created for showing the\n   description of our planet below the thick rule: This line is created with a\n   multicolumn of width 3 which also modifies the column specification to\n   `C{30}` so that it actually takes several lines ---note in passing that ANSI\n   color escape sequences are used here to show the text slanted.\n\n``` Go\n\tt, _ := NewTable(\"    r   l c\")\n\tt.AddRow(Multicolumn(3, \"    c\", \"♁ Earth\"))\n\tt.AddThickRule()\n\tt.AddRow(Multicolumn(3, \"    C{30}\", \"\\033[37;3mEarth is the third planet from the Sun and the only astronomical object known to harbor life\\033[0m\"))\n\tt.AddSingleRule()\n\tt.AddRow(Multicolumn(1, \"   |c\", \"Feature\"),\n\t\tMulticolumn(1, \"   c\", \"Measure\"),\n\t\tMulticolumn(1, \"c|\", \"Unit\"))\n\tt.AddSingleRule()\n\tt.AddRow(\"Aphelion\", 152100000, \"km\")\n\tt.AddRow(\"Perihelion\", 147095000, \"km\")\n\tt.AddRow(\"Eccentricity\", 0.0167086)\n\tt.AddRow(\"Orbital period\", 365.256363004)\n\tt.AddRow(\"Semi-major axis\", 149598023, \"km\")\n\tt.AddSingleRule()\n\tt.AddRow(Multicolumn(3, \"   │c│\", \"\\033[37;3mData provided by Wikipedia\\033[0m\"))\n\tt.AddSingleRule()\n\tfmt.Printf(\"%v\", t)\n```\n\n  which yields the following results:\n\n![example-6](figs/example-6.png \"example-6\")\n\n \n  Other than this, this example shows also that tables can be indented by adding\n  the same text (e.g., blanks) to the beginning of each row.\n\n## Multirows ##\n\nMultirows are defined analogously to multicolumns, i.e., as ordinary cells which\nspan over an arbitrary number of rows in the same column. An important\ndifference with multicolumns though is that multirows *merge* several lines into\none, but they do not provide means for *splitting* a specific line into others.\nA couple of usages follow:\n\n\n\n## Nested tables ##\n\n`table` prints *stringers* and because tables as created by this package are\nalso *stringers*, they can then be nested to any degree:\n\n``` Go\n\tboard1, _ := NewTable(\"||cccccccc||\")\n\tboard1.AddDoubleRule()\n\tboard1.AddRow(\"\\u265c\", \"\\u265e\", \"\\u265d\", \"\\u265b\", \"\\u265a\", \"\\u265d\", \"\", \"\\u265c\")\n\tboard1.AddRow(\"\\u265f\", \"\\u265f\", \"\\u265f\", \"\\u265f\", \"\\u2592\", \"\\u265f\", \"\\u265f\", \"\\u265f\")\n\tboard1.AddRow(\"\", \"\\u2592\", \"\", \"\\u2592\", \"\", \"\\u265e\", \"\", \"\\u2592\")\n\tboard1.AddRow(\"\\u2592\", \"\", \"\\u2592\", \"\", \"\\u265f\", \"\", \"\\u2592\", \"\")\n\tboard1.AddRow(\"\", \"\\u2592\", \"\", \"\\u2592\", \"\\u2659\", \"\\u2659\", \"\", \"\\u2592\")\n\tboard1.AddRow(\"\\u2592\", \"\", \"\\u2658\", \"\", \"\\u2592\", \"\", \"\\u2592\", \"\")\n\tboard1.AddRow(\"\\u2659\", \"\\u2659\", \"\\u2659\", \"\\u2659\", \"\", \"\\u2592\", \"\\u2659\", \"\\u2659\")\n\tboard1.AddRow(\"\\u2656\", \"\", \"\\u2657\", \"\\u2655\", \"\\u2654\", \"\\u2657\", \"\\u2658\", \"\\u2656\")\n\tboard1.AddDoubleRule()\n\n\tboard2, _ := NewTable(\"||cccccccc||\")\n\tboard2.AddDoubleRule()\n\tboard2.AddRow(\"\\u265c\", \"\\u265e\", \"\\u265d\", \"\\u265b\", \"\\u265a\", \"\\u265d\", \"\\u265e\", \"\\u265c\")\n\tboard2.AddRow(\"\\u265f\", \"\\u265f\", \"\\u265f\", \"\", \"\\u265f\", \"\\u265f\", \"\\u265f\", \"\\u265f\")\n\tboard2.AddRow(\"\", \"\\u2592\", \"\", \"\\u2592\", \"\", \"\\u2592\", \"\", \"\\u2592\")\n\tboard2.AddRow(\"\\u2592\", \"\", \"\\u2592\", \"\", \"\\u2592\", \"\", \"\\u2592\", \"\")\n\tboard2.AddRow(\"\", \"\\u2592\", \"\", \"\\u2659\", \"\\u265f\", \"\\u2592\", \"\", \"\\u2592\")\n\tboard2.AddRow(\"\\u2592\", \"\", \"\\u2592\", \"\", \"\\u2592\", \"\\u2659\", \"\\u2592\", \"\")\n\tboard2.AddRow(\"\\u2659\", \"\\u2659\", \"\\u2659\", \"\\u2592\", \"\", \"\\u2592\", \"\\u2659\", \"\\u2659\")\n\tboard2.AddRow(\"\\u2656\", \"\\u2658\", \"\\u2657\", \"\\u2655\", \"\\u2654\", \"\\u2657\", \"\\u2658\", \"\\u2656\")\n\tboard2.AddDoubleRule()\n\n\tt, _ := NewTable(\"| c | c  c |\", \"cct\")\n\tt.AddSingleRule()\n\tt.AddRow(\"ECO Code\", \"Moves\", \"Board\")\n\tt.AddSingleRule()\n\tt.AddRow(\"C26 Vienna Game: Vienna Gambit\", \"1.e4 e5 2.♘c3 ♞6 3.f4\", board1)\n\tt.AddRow(\"D00 Blackmar-Diemer Gambit: Gedult Gambit\", \"1.e4 d5 2.d4 exd4 3.f3\", board2)\n\tt.AddSingleRule()\n\n\tfmt.Printf(\"%v\", t)\t\n```\n\nBoth chess boards are tables so that the last table, named `t` just simply adds\nthem to each row:\n\n![example-7](figs/example-7.png \"example-7\")\n\n\n# License #\n\nMIT License\n\nCopyright (c) 2023, Carlos Linares López\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n\n# Author #\n\nCarlos Linares Lopez \u003ccarlos.linares@uc3m.es\u003e  \nComputer Science Department \u003chttps://www.inf.uc3m.es/en\u003e  \nUniversidad Carlos III de Madrid \u003chttps://www.uc3m.es/home\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclinaresl%2Ftable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclinaresl%2Ftable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclinaresl%2Ftable/lists"}