Documention for: tsn.r Created by: sunanda on: 30-Apr-2007 Format: text/editable Downloaded on: 30-Apr-2025 [div [numbering-on [asis/style/font-size:small;overflow:auto;background-color:#ddffee script: tsn.r title: Tranched Serial Numbers purpose: Quick, safe way of allocating categorized unique serial numbers author: Sunanda date: 30-apr-2007 Version: 0.0.1 asis] [contents [div/style/border:thin,blue,solid;margin:1em;padding:1em [h2 purpose [p **tsn.r** is a tool for allocating **two-level** unique serial numbers. [p The numbers are **unique** as each time you call **tsn.r** (for the same **tsn set**) you will receive a new number that has not been issued before (for that **tsn set**). [p They are **two level** as on each call to **tsn.r** you supply a **category id**. The serial number issued also includes a **unique category serial number**. [p That may make more sense if you read the **quick example** (below) and the section **what could I possibly want with a script like this?** (much later on). div] [div/style/border:thin,green,solid;margin:1em;padding:1em [h2 quick example [asis/style/* [1] do %tsn.r ;; install the utility [2] my-set: tsn-api/make-new-set ;; create a new number set [3] my-cats: copy ["green" "red" "blue"] [4] my-tsns: make block! 50000 [5] loop 50000 [ [6] append my-tsns third tsn-api/get-tsn my-set random/only my-cats ] asis] [li **[1]** -- install **tsn.r** [li **[2]** -- create a **tsn set** called **my-set** [li **[3]** -- create some ""categories"" against which you wish to issue unique serial numbers [li **[4]** -- create a block to hold the unique serial numbers we are about to allocate [li **[5]** -- run a loop to create 50,000 unique serial numbers [li **[6]** -- allocate 50,000 unique numbers.... [list [li **append my-tsns third** ... the unique serial number comes back in the **third** item of the block [li **tsn-api/get-tsn** ... the function to allocate the unique serial number [li **my-set** ... your **tsn set** from which the numbers will be allocated [li **random/only my-cats** ... the category for which we want a unique serial number list] list] [p **my-tsns** now contains 50,000 unique serial numbers. You can use these as short, efficient, keys in database. You can also use them to get back to the **category id** and **unique category serial number**. See following example: [asis/style/* [1] length? my-tsns [2] length? unique my-tsns [3] for nn 1 10 1 [ [4] print mold tsn-api/get-cat-id my-set my-tsns/:nn ] asis] [li **[1] [2]** -- just to prove **tsn.r** issued 50,000 **unique** numbers [li **[3]** -- a loop to check some of them [li **[4]** -- a print of some results: [list [li **my-tsns/:nn** ... the **unique serial number** we are checking [li **tsn-api/get-cat-id** ... the request to be returned the **cat id** and **cat serial number** [li **my-set** ... the **tsn set** we are using [li **my-tsns/:nn** ...the unique serial number who's cat we want list] A typical result looks like: **["blue" 5 103]**: unique serial number 103 maps to **blue 5**: the fifth number issued for category ""blue"" list] [p The whole data structure to map 50,000 numbers back to the their categories is highly compact: [asis/style/* >> length? mold my-set == 476 asis] [p (Your precise number will vary as the example uses randomised data). div] [div/style/border:thin,red,solid;margin:1em;padding:1em [h2 installing [asis/style/* do %tsn.r asis] [p **tsn.r** also requires **rse-ids.r** (also in the Library) to be in the same folder. div] [div/style/*-2 [h2 api functions [p The Application Programmer Interface has these functions: [li **make-new-set** -- create a new **tsn set** [li **get-tsn** -- return the next **unique serial number** and **unique category serial number** [li **get-all-categories** -- returns all **category ids** [li **get-cat-id** -- given a **unique serial number** return its **category id** and its **unique category serial number** [li **get-all-tsns-for-cat** -- given a **category id**, return all defined **unique serial numbers** for it [li **remove-category** -- remove a category list] [p All the API functions (except **make-new-set** require the **tsn set** as a parameter. You can have multiple **tsn sets**; the serial numbers created will be unique only within that **tsn set** [h3 make-new-set [p Returns you an object that defines the **tsn set** [asis/style/* USAGE: my-set: tsn-api/make-new-set asis] [p You are responsible for: [li saving this object if you need persistence across sessions -- see below [li providing it to all future API calls list] [p examples: [asis/style/* tsn-set-1: tsn-api/make-new-set tsn-set-2: tsn-api/make-new-set asis] [p You now have **two** separate **tsn sets**. Each will return its own series of **unique serial numbers**. But note that the numbers are **not** unique across the two **tsn sets**: both may (for example) have a serial number 101. [h4 saving and reloading a tsn set [p If you wish to access or create unique serial numbers across multiple sessions, you need to save the **tsn set** object at the end of one session and reload it at the start of the next: [asis/style/* write %saved-tsn-set.txt mold tsn-set-1 . . . tsn-set-1: first reduce load/all %saved-tsn-set.txt asis] [p To reduce the risk of users editing or corrupting the saved file, consider saving it as binary or encloaking it: [asis/style/* write/binary %saved-tsn-set.txt encloak compress mold tsn-set-1 "secret key" . . . first reduce load/all decompress decloak read/binary %saved-tsn-set.txt "secret key" asis] [h3 get-tsn [p Given a ** category id**, returns a **unique serial number** and a **unique category serial number**. [asis/style/* USAGE: tsn-api/get-tsn tsn-set cat-id ARGUMENTS: tsn-set -- the tsn-set in use (Type: object) cat-id -- the category id (Type: string integer) asis] [p The function returns a **block!** with four entries: [li **cat-id** -- as supplied in the input [li **unique category serial number** -- the unique number allocated within this category [li **unique serial number** -- the unique number allocated within this **tsn-set** [li **stats info** --a not very useful code that tells you a bit about the numbers just allocated: [list [li **nc** -- new category: first number issued for this category [li **ot** -- old tranche: number issued within a reserved range [li **et** -- extended tranche: number issued in a tranche that has been extended [li **nt** -- new tranche: first number issued in a new tranche list] list] [p example: [asis/style/* print new-usn: third tsn-api/get-tsn my-set "red" == 4776 asis] [h3 get-all-categories [p Returns a block of all the defined categories [asis/style/* USAGE: tsn-api/get-all-categories tsn-set ARGUMENTS: tsn-set -- the tsn-set in use (Type: object) asis] [p example: [asis/style/* probe tsn-api/get-all-categories my-set == ["blue" "green" "red"] asis] [h3 get-cat-id [p Given a **unique serial number**, it returns the **category id** and **unique category serial number**. [asis/style/* USAGE: tsn-api/get-cat-id tsn-set usn ARGUMENTS: tsn-set -- the tsn-set in use (Type: object) usn -- unique serial number (Type: integer) asis] [p The function returns either: [li **none** -- the **unique serial number** is unknown / not allocated [li **a block** -- of three items: [list [li **category-id** (string or integer) [li **unique category serial number** (integer) [li **unique serial number** (integer) (same as input supplied) list] list] [p example: [asis/style/* probe tsn-api/get-cat-id my-set 1888 == ["red" 629 1888] asis] [h3 get-all-tsns-for-cat [p Given a category, returns all **unique serial numbers** allocated to that category: [asis/style/* USAGE: tsn-api/get-all-tsns-for-cat tsn-set cat-id ARGUMENTS: tsn-set -- the tsn-set in use (Type: object) cat-id -- category-id (Type string integer) asis] [p The function returns either: [li **none** -- the category is unknown [li **block** -- containing the **unique serial numbers** allocated to the category list] [p example: [asis/style/* probe tsn-api/get-all-tsns-for-cat my-set "red" == [1 2 3 4 ... 600 601 602 .... 1009 1010 .... ] asis] [h3 remove-category [p Removes a category and all **unique serial numbers** allocated to it. [p This does not mean the serial numbers will be reissued later. The publicly released version of **tsn.r** does **not** reuse numbers, ever. It does mean: [li any future call to **get-cat-id** for numbers that were allocated to that category will return **none**. [li any future call to **get-tsn** for that category will start a new sequence of **unique category serial numbers**. list] [asis/style/* USAGE: tsn-api/remove-category tsn-set cat-id ARGUMENTS: tsn-set -- the tsn-set in use (Type: object) cat-id -- category-id (Type string integer) asis] [p example: [asis/style/* probe tsn-api/remove-category my-set "green" == true asis] div] [div/style/*-2 [h2 what could I possibly want with a script like this? [p Good question! It is either something you need very badly, or it seems completely pointless. [h3 historical [p The original usage was for an experiment database-type application. The database supported various entities (let's call some of the entities **users**, **files**, **programs**, **memos**, to make a concrete example). [p Each entity type had unique entity numbers: eg users were numbered **1,2,3,4**; Files were numbered **1,2,3,4,5** etc. [p That meant we could have made unique serial numbers like this: **U1, U2, U3** (for users), **F1, F2, F3, F4** for files. [p But (reasoning omitted for simplicity) we needed (wanted!) all identifiers to be integers. [p **tsn.r** was an easy way to make that possible. It also enabled a simple (almost flat) database structure with records like this: [asis 20 [5775 377 4477 388] asis] [p meaning entity **20** is associated with entities **5775, 377, 4477 and 388** without us having to know what types of entity **[5775 377 4477 388]** are. [p That was a sort of advance on the **many-to-many** relationship that many databases support....More of an **any-to-any** relationship. And that came in very handy for some data manipulations. [h3 present day [p **tsn.r** is a component in the (soon to be released) **Altme World Indexer**. [p It allow us a simple way of flattening **[Group-id Post-id]** into a single, relatively low, integer for indexing purposes. div] [div/style/*-2 [h2 configuring [p There are two magic numbers you can change in a **tsn set**. Changing them may affect performance. They adjust how **unique serial number ranges** are reserved for categories. [p You can change the settings **globally** (the changes will affect all **tsn-sets** allocated after the change); or **locally** (the changes affect only the specific **tsn-set**. [h3 global change [asis/style/* tsn-api/tsn-template/smallest-tranche: 25 tsn-api/tsn-template/growth-factor: 1.1 asis] [h3 local change [asis/style/* my-set: tsn-api/make-new-set my-set/smallest-tranche: 25 my-set/growth-factor: 1.1 asis] [h3 smallest-tranche [p Defines the smallest range reserved for a category. [h3 growth-factor [p Defines the size of the second and subsequent ranges reserved for a category. If a category exhausts a range, then **tsn.r** reserves a range equal to the **current size of the category** times the **growth factor**. [p Example: category **red** has a highest **unique category serial number** of 950 and we need to allocate another range to it. If the **growth-factor** is 1.4, then **tsn.r** allocates a range that is **1330** wide (950 * 1.4). div] div]