I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# Three word location

Three word location is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

If one were to enter the words: 'softly affiliate activation' at the what3words.com site, the response would be a location in Walt Disney World. The latitude and longitude for that spot is 28.3852 -81.5638. Using that service enables anyone to specify any place on the Earth with three words.

Task: Provide a similar solution - Display a location on the Earth with three words derived from a latitude longitude pair.

For: latitude = 28.3852 longitude = -81.5638 Display: W18497 W11324 W01322

Implementation:

Build an array of 28126, 6 character words in the form W00000 thru W28125.

Convert latitude and longitude into positive integers.

Build a 43 bit integer containing latitude (21 bits) and longitude (22 bits).

Isolate most significant 15 bits for word 1 index.

Isolate next 14 bits for word 2 index.

Isolate next 14 bits for word 3 index.

Fetch each word from the word array.

Display the words.

Reverse the procedure and display the original latitude and longitude.

Extra credit: Find a way to populate the word array with actual words.

## AppleScript

### Index words

When the words are index-based as in the task description, it's not necessary to generate all 28126.

`on locationToWords({latitude, longitude})    -- "Convert" the coordinates to positive integers by adding enough degrees to ensure positive results,    -- multiplying by enough to left shift by four decimal places, and rounding.    set intLat to ((latitude + 90) * 10000) as integer    set intLong to ((longitude + 180) * 10000) as integer    -- Derive a 15-bit and two 14-bit values from the two results' 43 bits.    set output to {intLat div 64, intLat mod 64 * 256 + intLong div 16384, intLong mod 16384}    -- Coerce the three values to text "words" beginning with "W" and any necessary leading zeros.    repeat with thisIndex in output        set thisIndex's contents to "W" & text 2 thru 6 of ((100000 + thisIndex) as text)    end repeat     return outputend locationToWords on wordsToLocation(threeWords)    set indices to {}    repeat with thisWord in threeWords        set end of indices to (text 2 thru -1 of thisWord) as integer    end repeat    set intLat to (beginning of indices) * 64 + (item 2 of indices) div 256 mod 64    set intLong to (item 2 of indices) mod 256 * 16384 + (end of indices)     return {intLat / 10000 - 90, intLong / 10000 - 180}end wordsToLocation -- Task code:local location, threeWords, checkLocationset location to {28.3852, -81.5638}set threeWords to locationToWords(location)set checkLocation to wordsToLocation(threeWords)return {location, threeWords, checkLocation}`
Output:
`{{28.3852, -81.5638}, {"W18497", "W11324", "W01322"}, {28.3852, -81.5638}}`

### Actual words

`on locationToWords({latitude, longitude}, listOfWords)    script o        property wordList : listOfWords    end script     set intLat to ((latitude + 90) * 10000) as integer    set intLong to ((longitude + 180) * 10000) as integer    set output to {intLat div 64, intLat mod 64 * 256 + intLong div 16384, intLong mod 16384}    repeat with thisIndex in output        set thisIndex's contents to item (thisIndex + 1) of o's wordList -- AppleScript indices are 1-based.    end repeat     return outputend locationToWords on wordsToLocation(threeWords, listOfWords)    script o        property wordList : listOfWords    end script     set indices to {}    repeat with thisWord in threeWords        set thisWord to thisWord's contents        set i to 1        repeat until (item i of o's wordList is thisWord)            set i to i + 1            if (i > 28126) then error "wordsToLocation() handler: The word “" & thisWord & "” isn't in the word list."        end repeat        set end of indices to i - 1 -- Converted to 0-based index.    end repeat    set intLat to (beginning of indices) * 64 + (item 2 of indices) div 256 mod 64    set intLong to (item 2 of indices) mod 256 * 16384 + (end of indices)     return {intLat / 10000 - 90, intLong / 10000 - 180}end wordsToLocation -- Task code:local o, location, threeWords, checkLocation-- Use the words in unixdict.txt. It only has 25110 of them by AppleScript's count,-- so make up the shortfall with invented plurals and third-persons-singular.script    property wordList : words of (read file ((path to desktop as text) & "unixdict.txt") as «class utf8»)    property additionalWords : {}end scriptset o to resultrepeat with i from 1 to (28126 - (count o's wordList))    tell item i of o's wordList        if (it ends with "s") then            set end of o's additionalWords to it & "es"        else            set end of o's additionalWords to it & "s"        end if    end tellend repeatset o's wordList to o's wordList & o's additionalWords set location to {28.3852, -81.5638}set threeWords to locationToWords(location, o's wordList)set checkLocation to wordsToLocation(threeWords, o's wordList)return {location, threeWords, checkLocation}`
Output:
`{{28.3852, -81.5638}, {"quote", "hygiene", "aristotelean"}, {28.3852, -81.5638}}`

## AutoHotkey

Conversion based on Wren
WordList From link suggested by Symsyn

`URLDownloadToFile, http://www-personal.umich.edu/~jlawler/wordlist, % A_Temp "\wordlist.txt"FileRead, wordList, % A_Temp "\wordlist.txt" LL := [28.3852, -81.5638]num := LL2num(LL)words := LL2words(wordList, LL)LL2 := words2LL(wordList, words) MsgBox, 262144, , % result := "(LL = " LL.1 ", " LL.2 "LL2num = " num.1 ", " num.2 ", " num.3 "LL2words = " words.1 ", " words.2 ", " words.3 "words2LL = " LL2.1 ", " LL2.2 ")"return;-----------------------------------------------LL2words(wordList, LL){    ; Latitude/Longitude to 3 words    num := LL2num(LL)    wli := wordList(wordList).1    return [wli[num.1], wli[num.2], wli[num.3]]};-----------------------------------------------words2LL(wordList, w){    ; 3 words to Latitude/Longitude    iow := wordList(wordList).2    LL := num2LL([iow[w.1], iow[w.2], iow[w.3]])    return [ll.1, ll.2]};-----------------------------------------------wordList(wordList){        ; word list to two arrays    wli:=[], iow:=[]    ; word list index, index of word    for i, word in StrSplit(wordList, "`n", "`r")        if (word ~= "^[a-z]+\$") && (StrLen(word) <= 8) && (StrLen(word) > 3)            wli.Push(word), iow[word] := wli.MaxIndex()    return [wli, iow]};-----------------------------------------------LL2num(LL){                ; Latitude/Longitude to 3 numbers    ilat := LL.1*10000 + 900000    ilon := LL.2*10000 + 1800000    latlon := (ilat << 22) + ilon    return [(latlon >> 28) & 0x7fff, (latlon >> 14) & 0x3fff, latlon & 0x3fff]};-----------------------------------------------num2LL(w){                ; 3 numbers to Latitude/Longitude    latlon := (w.1 << 28) | (w.2 << 14) | w.3    ilat := latlon >> 22    ilon := latlon & 0x3fffff    return [(ilat-900000)/10000, (ilon-1800000)/10000]};-----------------------------------------------`
Output:
```LL = 28.3852, -81.5638
LL2num = 18497, 11324, 1322
LL2words = malleus, fasten, analytic
words2LL = 28.385200, -81.563800```

## C

Translation of: Go
`#include <stdio.h>#include <stdlib.h> typedef long long int64; void to_word(char *ws, int64 w) {    sprintf(ws, "W%05lld", w);} int64 from_word(char *ws) {    return atoi(++ws);} int main() {    double lat, lon;    int64 latlon, ilat, ilon, w1, w2, w3;    char w1s[7], w2s[7], w3s[7];    printf("Starting figures:\n");    lat = 28.3852;    lon = -81.5638;    printf("  latitude = %0.4f, longitude = %0.4f\n", lat, lon);     // convert lat and lon to positive integers    ilat = (int64)(lat*10000 + 900000);    ilon = (int64)(lon*10000 + 1800000);     // build 43 bit BigInt comprising 21 bits (lat) and 22 bits (lon)    latlon = (ilat << 22) + ilon;     // isolate relevant bits    w1 = (latlon >> 28) & 0x7fff;    w2 = (latlon >> 14) & 0x3fff;    w3 = latlon & 0x3fff;     // convert to word format    to_word(w1s, w1);    to_word(w2s, w2);    to_word(w3s, w3);     // and print the results    printf("\nThree word location is:\n");    printf("  %s %s %s\n", w1s, w2s, w3s);     /* now reverse the procedure */    w1 = from_word(w1s);    w2 = from_word(w2s);    w3 = from_word(w3s);     latlon = (w1 << 28) | (w2 << 14) | w3;    ilat = latlon >> 22;    ilon = latlon & 0x3fffff;    lat = (double)(ilat-900000) / 10000;    lon = (double)(ilon-1800000) / 10000;     // and print the results    printf("\nAfter reversing the procedure:\n");    printf("  latitude = %0.4f, longitude = %0.4f\n", lat, lon);    return 0;}`
Output:
```Starting figures:
latitude = 28.3852, longitude = -81.5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28.3852, longitude = -81.5638
```

## Delphi

Translation of: Go
` program Three_word_location; {\$APPTYPE CONSOLE} {\$R *.res} uses  System.SysUtils; type  TThreeWordLocation = array of string;   TGlobalPosition = record  private    FLatitude: Double;    FLongitude: Double;    FWs: TThreeWordLocation;    class function toWord(w: int64): string; static;    class function fromWord(ws: string): int64; static;    procedure SetLatitude(const Value: Double);    procedure SetLongitude(const Value: Double);    procedure Recalculate;    function GetTWLocationAsStr: string;  public    constructor Create(_lat, _lon: Double); overload;    constructor Create(Ws: TThreeWordLocation); overload;    procedure Assign(Ws: TThreeWordLocation);    property Latitude: Double read FLatitude write SetLatitude;    property Longitude: Double read FLongitude write SetLongitude;    property TWLocation: TThreeWordLocation read FWs;    property TWLocationAsStr: string read GetTWLocationAsStr;  end; { TGlobalPosition } constructor TGlobalPosition.Create(_lat, _lon: Double);begin  FLatitude := _lat;  FLongitude := _lon;  Recalculate;end; constructor TGlobalPosition.Create(ws: TThreeWordLocation);begin  Assign(ws);end; class function TGlobalPosition.fromWord(ws: string): int64;begin  Result := StrToInt(ws.Substring(1));end; function TGlobalPosition.GetTWLocationAsStr: string;var  i: Integer;begin  Result := '';  for i := 0 to 2 do    Result := Result + ' ' + FWs[i];  Result := Result.Trim;end; procedure TGlobalPosition.Recalculate;var  i: Integer;  w: array[0..2] of int64;  ilat, ilon, latlon: Int64;begin  SetLength(FWs, 3);   // convert lat and lon to positive integers  ilat := Round(FLatitude * 10000 + 900000);  ilon := Round(FLongitude * 10000 + 1800000);    // build 43 bit BigInt comprising 21 bits (lat) and 22 bits (lon)  latlon := (ilat shl 22) + ilon;   // isolate relevant bits  w[0] := (latlon shr 28) and \$7fff;  w[1] := (latlon shr 14) and \$3fff;  w[2] := latlon and \$3fff;   // convert to word format  for i := 0 to 2 do    FWs[i] := toWord(w[i]);end; procedure TGlobalPosition.SetLatitude(const Value: Double);begin  FLatitude := Value;  Recalculate;end; procedure TGlobalPosition.SetLongitude(const Value: Double);begin  FLongitude := Value;  Recalculate;end; class function TGlobalPosition.toWord(w: int64): string;begin  result := format('W%.5d', [w]);end; procedure TGlobalPosition.Assign(Ws: TThreeWordLocation);var  i: Integer;  w: array[0..2] of int64;  ilat, ilon, latlon: Int64;begin  SetLength(FWs, 3);  for i := 0 to 2 do  begin    FWs[i] := Ws[i];    w[i] := fromWord(Ws[i]);  end;   latlon := (w[0] shl 28) or (w[1] shl 14) or w[2];  ilat := latlon shr 22;  ilon := latlon and \$3fffff;  FLatitude := (ilat - 900000) / 10000;  FLongitude := (ilon - 1800000) / 10000;end; var  pos: TGlobalPosition; begin  pos.Create(28.3852, -81.5638);   Writeln('Starting figures:');  Writeln(Format('  latitude = %0.4f, longitude = %0.4f', [pos.Latitude, pos.Longitude]));   Writeln(#10'Three word location is:');  Writeln('  ', pos.TWLocationAsStr);   Writeln(#10'After reversing the procedure:');   // pos.Create(['W18497','W11324','W01322']);  pos.Create(pos.TWLocation);  Writeln(Format('  latitude = %0.4f, longitude = %0.4f', [pos.Latitude, pos.Longitude]));   Readln;end.  `
Output:
```Starting figures:
latitude = 28,3852, longitude = -81,5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28,3852, longitude = -81,5638
```

## FreeBASIC

Translation of: Nim
`Print "Starting figures:"Dim As Double lat = 28.3852, longi = -81.5638Print Using "  latitude = &, longitude = &"; lat; longi ' Convert "lat" and "long" to positive integers.Dim As Integer ilat = Cint(lat * 10000 + 900000)Dim As Integer ilong = Cint(longi * 10000 + 1800000) ' Build 43 bit int comprising 21 bits (lat) and 22 bits (lon).Dim As Double latlong = ilat Shl 22 + ilong ' Isolate relevant bits.Dim As Integer w1 = latlong Shr 28 And &H7fffDim As Integer w2 = latlong Shr 14 And &H3fffDim As Integer w3 = latlong And &H3fff ' Convert to word format.Dim As String*5 w1s = String(5, "0"), w2s = String(5, "0"), w3s = String(5, "0")Mid(w1s, 6-Len(Str(w1))) = Str(w1)Mid(w2s, 6-Len(Str(w2))) = Str(w2)Mid(w3s, 6-Len(Str(w3))) = Str(w3) ' Print the results.Print !"\nThree word location is:"Print Using "  W\   \ W\   \ W\   \"; w1s; w2s; w3s latlong = w1 Shl 28 Or w2 Shl 14 Or w3ilat = latlong Shr 22ilong = latlong And &H3ffffflat = (ilat - 900000) / 10000longi = (ilong - 1800000) / 10000 ' Print the results.Print !"\nAfter reversing the procedure:"Print Using "  latitude = &, longitude = &"; lat; longiSleep`
Output:
`Igual que la entrada de Nim`

## Go

Translation of: Wren

Though no need for big integers as we have int64 built in.

`package main import (    "fmt"    "strconv") func toWord(w int64) string { return fmt.Sprintf("W%05d", w) } func fromWord(ws string) int64 {    var u, _ = strconv.ParseUint(ws[1:], 10, 64)    return int64(u)} func main() {    fmt.Println("Starting figures:")    lat := 28.3852    lon := -81.5638    fmt.Printf("  latitude = %0.4f, longitude = %0.4f\n", lat, lon)     // convert lat and lon to positive integers    ilat := int64(lat*10000 + 900000)    ilon := int64(lon*10000 + 1800000)     // build 43 bit BigInt comprising 21 bits (lat) and 22 bits (lon)    latlon := (ilat << 22) + ilon     // isolate relevant bits    w1 := (latlon >> 28) & 0x7fff    w2 := (latlon >> 14) & 0x3fff    w3 := latlon & 0x3fff     // convert to word format    w1s := toWord(w1)    w2s := toWord(w2)    w3s := toWord(w3)     // and print the results    fmt.Println("\nThree word location is:")    fmt.Printf("  %s %s %s\n", w1s, w2s, w3s)     /* now reverse the procedure */    w1 = fromWord(w1s)    w2 = fromWord(w2s)    w3 = fromWord(w3s)     latlon = (w1 << 28) | (w2 << 14) | w3    ilat = latlon >> 22    ilon = latlon & 0x3fffff    lat = float64(ilat-900000) / 10000    lon = float64(ilon-1800000) / 10000     // and print the results    fmt.Println("\nAfter reversing the procedure:")    fmt.Printf("  latitude = %0.4f, longitude = %0.4f\n", lat, lon)}`
Output:
```Starting figures:
latitude = 28.3852, longitude = -81.5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28.3852, longitude = -81.5638
```

## Julia

Direct translation from the SymSyn example given by the task creator, though note that idiomatic Julia would usually code this as two small encode() and decode() functions.

` # Three Word Location - convert latitude and longitude to three wordsLAT = 28.3852LON = -81.5638 # build word array W00000 ... W28125wordarray = ["W" * string(x, pad=5) for x in 0:28125] # make latitude and longitude positive integersILAT = Int(LAT * 10000 + 900000)ILON = Int(LON * 10000 + 1800000) # build 43 bit integer containing latitude (21 bits) and longitude (22 bits)LATLON = (ILAT << 22) + ILON # isolate most significant 15 bits for word 1 index# next 14 bits for word 2 index# next 14 bits for word 3 indexW1 = (LATLON >> 28) & 0x7fffW2 = (LATLON >> 14) & 0x3fffW3 = LATLON & 0x3fff # fetch each word from word arrayw1 = wordarray[W1 + 1]w2 = wordarray[W2 + 1]w3 = wordarray[W3 + 1] # display wordsprintln("\$w1 \$w2 \$w3")  # reverse the procedure # look up each word(w1index, w2index, w3index) = indexin([w1, w2, w3], wordarray) .- 1 # build the latlon integer from the word indexeslatlon = (w1index << 28) | (w2index << 14) | w3index  # isolate the latitude and longitudeilon =  latlon & 0xfffffilat = latlon >> 22 # convert back to floating point valueslon = (ilon - 1800000) / 10000lat = (ilat - 900000) / 10000 # display valuesprintln("latitude = \$lat longitude = \$lon") `
Output:
```W18497 W11324 W01322
latitude = 28.3852 longitude = -81.5638
```

### Idiomatic version with scrambling

`using Random const LAT = 28.3852const LON = -81.5638 # build word array W00000 ... W28125const wordarray = ["W" * string(x, pad=5) for x in 0:28125] function threewordencode(lat, lon, seed=0) # returns vector of 3 strings    arr = wordarray    if seed != 0        rng = MersenneTwister(seed)        arr = shuffle(rng, deepcopy(wordarray))    end    i = (Int(lat * 10000 + 900000) << 22) | Int(lon * 10000 + 1800000)    return map(x -> arr[x + 1], [(i >> 28) & 0x7fff, (i >> 14) & 0x3fff, i & 0x3fff])end function threeworddecode(w1, w2, w3, seed=0) # returns pair of Float64    arr = wordarray    if seed != 0        rng = MersenneTwister(seed)        arr = shuffle(rng, deepcopy(wordarray))    end    (i1, i2, i3) = indexin([w1, w2, w3], arr) .- 1    latlon = (i1 << 28) | (i2 << 14) | i3    ilon, ilat = latlon & 0xfffff, latlon >> 22    return  (ilon - 1800000) / 10000, (ilat - 900000) / 10000end words = threewordencode(LAT, LON)println(join(words, " ")) lat, lon = threeworddecode(words...)println("latitude = \$lat longitude = \$lon") println("\nWith scramble using key 12345678:")words = threewordencode(LAT, LON, 12345678)println(join(words, " "))lat, lon = threeworddecode(words..., 12345678)println("latitude = \$lat longitude = \$lon") `
Output:
```W18497 W11324 W01322
latitude = -81.5638 longitude = 28.3852

With scramble using key 12345678:
W20242 W23427 W16215
latitude = -81.5638 longitude = 28.3852
```

## Kotlin

Translation of: Go
`fun toWord(w: Long): String {    return "W%05d".format(w)} fun fromWord(ws: String): Long {    return ws.substring(1).toUInt().toLong()} fun main() {    println("Starting figures:")    var lat = 28.3852    var lon = -81.5638    println("  latitude = %.4f, longitude = %.4f".format(lat, lon))    println()     // convert lat and lon to positive integers    var ilat = (lat * 10000 + 900000).toLong()    var ilon = (lon * 10000 + 1800000).toLong()     // build 43 bit BigInt comprising 21 bits (lat) and 22 bits (lon)    var latlon = (ilat shl 22) + ilon     // isolate relevant bits    var w1 = (latlon shr 28) and 0x7fff    var w2 = (latlon shr 14) and 0x3fff    var w3 = latlon and 0x3fff     // convert to word format    val w1s = toWord(w1)    val w2s = toWord(w2)    val w3s = toWord(w3)     // and print the results    println("Three word location is:")    println("  \$w1s \$w2s \$w3s")    println()     /* now reverse the procedure */    w1 = fromWord(w1s)    w2 = fromWord(w2s)    w3 = fromWord(w3s)     latlon = (w1 shl 28) or (w2 shl 14) or w3    ilat = latlon shr 22    ilon = latlon and 0x3fffff    lat = (ilat - 900000).toDouble() / 10000    lon = (ilon - 1800000).toDouble() / 10000     // and print the results    println("After reversing the procedure:")    println("  latitude = %.4f, longitude = %.4f".format(lat, lon))}`
Output:
```Starting figures:
latitude = 28.3852, longitude = -81.5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28.3852, longitude = -81.5638```

## Lua

Translation of: C
`function toWord(w)    return string.format("W%05d", w)end function fromWord(ws)    return tonumber(string.sub(ws, 2, -1))end ------------------------------------------------------------------------- print("Starting figures:")lat = 28.3852lon = -81.5638print(string.format("  latitude = %0.4f, longitude = %0.4f", lat, lon))print() -- convert from lat and lon to positive integersilat = lat * 10000 +  900000ilon = lon * 10000 + 1800000 -- build 43 bit number comprising 21 bits (lat) and 22 bits (lon)latlon = math.floor((ilat << 22) + ilon) -- isloate relevant bitsw1 = (latlon >> 28) & 0x7fffw2 = (latlon >> 14) & 0x3fffw3 =  latlon        & 0x3fff -- convert to word formatw1s = toWord(w1)w2s = toWord(w2)w3s = toWord(w3) -- and print the resultsprint("Three word location is:")print("  " .. w1s .. " " .. w2s .. " " .. w3s)print() ------------------------------------------------------------------------- -- now reverse the procedurew1 = fromWord(w1s)w2 = fromWord(w2s)w3 = fromWord(w3s) latlon = (w1 << 28) | (w2 << 14) | w3ilat = latlon >> 22ilon = latlon & 0x3ffffflat = (ilat -  900000) / 10000lon = (ilon - 1800000) / 10000 -- and print the resultsprint("After reversing the procedure:")print(string.format("  latitude = %0.4f, longitude = %0.4f", lat, lon))`
Output:
```Starting figures:
latitude = 28.3852, longitude = -81.5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28.3852, longitude = -81.5638```

## Nim

Translation of: Go
`import strformat, strutils func toWord(w: int64): string = &"W{w:05}" func fromWord(ws: string): int64 = ws[1..5].parseInt() echo "Starting figures:"var  lat = 28.3852  long = -81.5638echo &"  latitude = {lat:0.4f}, longitude = {long:0.4f}" # Convert "lat" and "long" to positive integers.var  ilat = int64(lat * 10_000 + 900_000)  ilong = int64(long * 10_000 + 1_800_000) # Build 43 bit int comprising 21 bits (lat) and 22 bits (lon).var latlong = ilat shl 22 + ilong # Isolate relevant bits.var  w1 = latlong shr 28 and 0x7fff  w2 = latlong shr 14 and 0x3fff  w3 = latlong and 0x3fff # Convert to word format.let  w1s = w1.toWord  w2s = w2.toWord  w3s = w3.toWord # Print the results.echo "\nThree word location is:"echo &"  {w1s} {w2s} {w3s}" # Reverse the procedure.w1 = w1s.fromWordw2 = w2s.fromWordw3 = w3s.fromWord latlong = w1 shl 28 or w2 shl 14 or w3ilat = latlong shr 22ilong = latlong and 0x3ffffflat = float(ilat - 900_000) / 10_000long = float(ilong - 1_800_000) / 10_000 # Print the results.echo "\nAfter reversing the procedure:"echo &"  latitude = {lat:0.4f}, longitude = {long:0.4f}"`
Output:
```Starting figures:
latitude = 28.3852, longitude = -81.5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28.3852, longitude = -81.5638```

## Perl

Translation of: Raku
`use strict;use warnings;use feature 'say';use bignum; # without this, round-trip results not exact use Math::AnyNum 'polymod'; # SYNTHETICS HANDLING my @synth;push @synth, join '', @\$_ for map { [split /:/] } glob '{b,d,f,h,j,k,l,m,n,p,r,s,t,w,y,z}:{a,e,i,o,u}';my(%htnys,\$c); \$htnys{\$_} = \$c++ for @synth;my \$exp  = @synth;my \$prec = 10_000; sub bin2dec { unpack('N', pack('B32', substr('0' x 32 . shift, -32))) } sub synth { join '', reverse @synth[polymod(shift() + int(rand 18) * 28126, \$exp, \$exp) ] } sub thnys {    my @n = @htnys{ shift() =~ /(..)(..)(..)/ };  # NB notation on hash slice: % -> @    (\$n[2] + \$n[1]*\$exp + \$n[0]*\$exp**2) % 28126} # ENCODE / DECODE sub w_encode {    my(\$lat, \$lon, \$f) = @_;    \$f = \&synth unless \$f;    my @words;    my \$bits = sprintf '%021b%022b', int((\$lat+90)*\$prec), int((\$lon+180)*\$prec);    push @words, &\$f(bin2dec(\$_)) for \$bits =~ / (.{15}) (.{14}) (.{14}) /x;    @words} sub w_decode {    my(\$w, \$f) = @_;    \$f = \&thnys unless \$f;    my \$s = '%015b';    my \$bin = sprintf(\$s, &\$f(\$\$w[0])) . substr(sprintf(\$s, &\$f(\$\$w[1])), 1) . substr(sprintf(\$s, &\$f(\$\$w[2])), 1);    (bin2dec(substr(\$bin,0,21))/\$prec - 90), (bin2dec(substr(\$bin,21))/\$prec - 180)} # TESTING for ([ 51.4337,     -0.2141,   'Wimbledon'],     [ 21.2596,   -157.8117,   'Diamond Head crater'],     [-55.9652,    -67.2256,   'Monumento Cabo De Hornos'],     [ 71.170924,   25.782998, 'Nordkapp, Norway'],     [ 45.762983,    4.834520, 'Café Perl, Lyon'],     [ 48.391541, -124.736731, 'Cape Flattery Lighthouse, Tatoosh Island'],    ) {    my(\$lat, \$lon, \$description) = @\$_;    my @words = w_encode \$lat, \$lon;    my @index = w_encode \$lat, \$lon, sub { shift };    printf "Coordinates: %s, %s (%s)\n   To Index: %s\n  To 3-word: %s\nFrom 3-word: %s, %s\n From Index: %s, %s\n\n",      \$lat, \$lon, \$description, join(' ',@index), join(' ',@words), w_decode(\@words), w_decode(\@index, sub { shift() });}`
Output:
```Coordinates: 51.4337, -0.2141 (Wimbledon)
To Index: 22099 365 12003
To 3-word: yotema ritomi rahiku
From 3-word: 51.4337, -0.2141
From Index: 51.4337, -0.2141

Coordinates: 21.2596, -157.8117 (Diamond Head crater)
To Index: 17384 5133 8891
To 3-word: hayibi batufo jokube
From 3-word: 21.2596, -157.8117
From Index: 21.2596, -157.8117

Coordinates: -55.9652, -67.2256 (Monumento Cabo De Hornos)
To Index: 5317 15428 13632
To 3-word: fubeha zidura nerupe
From 3-word: -55.9652, -67.2256
From Index: -55.9652, -67.2256

Coordinates: 71.170924, 25.782998 (Nordkapp, Norway)
To Index: 25182 15741 9829
To 3-word: zorenu jaboda kiyika
From 3-word: 71.1709, 25.7829
From Index: 71.1709, 25.7829

Coordinates: 45.762983, 4.83452 (Café Perl, Lyon)
To Index: 21212 15728 13337
To 3-word: ludefu bimepo demojo
From 3-word: 45.7629, 4.8345
From Index: 45.7629, 4.8345

Coordinates: 48.391541, -124.736731 (Cape Flattery Lighthouse, Tatoosh Island)
To Index: 21623 11041 11960
From 3-word: 48.3915, -124.7368
From Index: 48.3915, -124.7368```

## Phix

Translation of: Go
```--
-- demo\rosetta\Three_word_location.exw
-- ====================================
--
with javascript_semantics

function toWord(integer w)
return sprintf("W%05d", w)
end function

function fromWord(string ws)
sequence r = scanf(ws,"W%05d")
integer res = r[1][1]
return res
end function

printf(1,"Starting figures:\n")
atom lat =  28.3852,
lon = -81.5638
printf(1,"  latitude = %0.4f, longitude = %0.4f\n", {lat, lon})

-- convert lat and lon to positive integers
integer ilat := floor((lat+90)*10000),
ilon := floor((lon+180)*10000)

-- build 43 bit BigInt comprising 21 bits (lat) and 22 bits (lon)
-- (std phix atoms have 53/64 bits of precision on 32/64 bit, both plenty)
atom latlon := (ilat << 22) + ilon

-- isolate relevant bits
integer w1 = (latlon >> 28) && 0x7fff,
w2 = (latlon >> 14) && 0x3fff,
w3 = latlon && 0x3fff

-- convert to word format
string w1s = toWord(w1),
w2s = toWord(w2),
w3s = toWord(w3)

-- and print the results
printf(1,"\nThree word location is:\n")
printf(1,"  %s %s %s\n", {w1s, w2s, w3s})

-- now reverse the procedure
w1 = fromWord(w1s)
w2 = fromWord(w2s)
w3 = fromWord(w3s)

-- NB: or_bits (likewise ||), being expressly 32-bit, is NOT appropriate here...
latlon = (w1 << 28) + (w2 << 14) + w3
ilat = latlon >> 22
ilon = latlon && 0x3fffff
lat = ilat/10000 - 90
lon = ilon/10000 - 180

-- and print the results
printf(1,"\nAfter reversing the procedure:\n")
printf(1,"  latitude = %0.4f, longitude = %0.4f\n", {lat, lon})
```
Output:
```Starting figures:
latitude = 28.3852, longitude = -81.5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28.3852, longitude = -81.5638
```

## Raku

Works with: Rakudo version 2020.07

In large part due to the complete lack of specification, reference implementation, or guidance from the task creator, came up with my own bespoke synthetic word list.

Words always consist of a series of consonant/vowel pairs. Uses a cut down alphabet to reduce possible confusion from overlapping pronunciation.

Some letters with overlapping pronunciation are removed: c: confusable with k or s, g: overlaps with j, x: overlaps with z, q: just because, v: similar to w and we have way more than enough characters anyway.

As it is, with this alphabet we can form 512000 different 6 character "words"; 28126 is a drop in the bucket. To spread out the the words a bit, add a bit of randomness. 28126 fits into 512000 18 and a bit times. Add a random multiple of 28126 to the encoder then modulus it back out on decode. Will get different results on different runs.

We don't bother to pre-calculate and store the words, just generate them on the fly.

Official pronunciation guide:

• a - long a (say may day)
• e - long e (he me see)
• i - long i (hi sigh die)
• o - long o (go so low)
• u - long u (due boo moo)
`# SYNTHETICS HANDLINGmy @synth = flat < b d f h j k l m n p r s t w y z > X~ < a e i o u >;my %htnys = @synth.antipairs;my \$exp   = @synth.elems; sub synth (Int \$v) { @synth[(\$v + (^18).pick * 28126).polymod(\$exp xx *).reverse || 0].join } sub thnys (Str \$v) { (sum %htnys{\$v.comb(2).reverse} Z* 1, \$exp, \$exp**2) % 28126 }  # ENCODE / DECODEsub w-encode ( Rat(Real) \$lat, Rat(Real) \$lon, :&f = &synth ) {    \$_ = ((\$lat +  90) * 10000).round.fmt('%021b') ~ ((\$lon + 180) * 10000).round.fmt('%022b');    (:2(.substr(0,15)), :2(.substr(15,14)),:2(.substr(29)))».&f} sub w-decode ( *@words, :&f = &thnys ) {    my \$bin = (@words».&f Z, <0 1 1>).map({.[0].fmt('%015b').substr(.[1])}).join;    (:2(\$bin.substr(0,21))/10000) - 90, (:2(\$bin.substr(21))/10000) - 180}  # TESTINGfor 51.4337,  -0.2141, # Wimbledon    21.2596,-157.8117, # Diamond Head crater   -55.9652, -67.2256, # Monumento Cabo De Hornos    59.3586,  24.7447, # Lake Raku    29.2021,  81.5324, # Village Raku    -7.1662,  53.9470, # The Indian ocean, south west of Seychelles    28.3852, -81.5638  # Walt Disney World  -> \$lat, \$lon {    my @words = w-encode \$lat, \$lon;    my @index = w-encode \$lat, \$lon, :f( { \$_ } );    printf "Coordinates: %s, %s\n   To Index: %s\n  To 3-word: %s\nFrom 3-word: %s, %s\n From Index: %s, %s\n\n",      \$lat, \$lon, @index.Str, @words.Str, w-decode(@words), w-decode @index, :f( { \$_ } );}`
Output:
```Coordinates: 51.4337, -0.2141
To Index: 22099 365 12003
To 3-word: zofobe fohujo habute
From 3-word: 51.4337, -0.2141
From Index: 51.4337, -0.2141

Coordinates: 21.2596, -157.8117
To Index: 17384 5133 8891
To 3-word: nijemo zanaza fupawu
From 3-word: 21.2596, -157.8117
From Index: 21.2596, -157.8117

Coordinates: -55.9652, -67.2256
To Index: 5317 15428 13632
To 3-word: zanohu julaso husese
From 3-word: -55.9652, -67.2256
From Index: -55.9652, -67.2256

Coordinates: 59.3586, 24.7447
To Index: 23337 4732 15831
To 3-word: kapupi hokame supoku
From 3-word: 59.3586, 24.7447
From Index: 59.3586, 24.7447

Coordinates: 29.2021, 81.5324
To Index: 18625 5535 10268
To 3-word: dijule nutuza nefini
From 3-word: 29.2021, 81.5324
From Index: 29.2021, 81.5324

Coordinates: -7.1662, 53.947
To Index: 12942 12942 12942
To 3-word: rakudo rakudo rakudo
From 3-word: -7.1662, 53.947
From Index: -7.1662, 53.947

Coordinates: 28.3852, -81.5638
To Index: 18497 11324 1322
To 3-word: tabesa nekaso bupodo
From 3-word: 28.3852, -81.5638
From Index: 28.3852, -81.5638```

(Ok, I admit I manipulated that second to last one, but it is a correct and valid 3-word location in this implementation. There is less than 1 chance in 5000 that it will produce that specific word group though.)

### A thought experiment

A little thought experiment... Latitude, longitude to four decimal places is accurate to about 11.1 meters at the equator, smaller the further from the equator you get. What would it take to support five decimal places? (Accurate to 1.11 meters.)

```360 * 100000 == 36000000; ceiling 36000000.log(2) == 26;```

So we need 26 bits to cover 360.00000; half of that for 180.00000, or `26 bits + 25 bits == 51 bits`. `51 / 3 == 17`. `2**17 == 131072` indices. The previous synthetics routine provides much more than enough.

How many sylabics will we need to minimally cover it?

`∛131072 == 50.7968...`

So at least 51. The synthetics routine provide sylabics in blocks of 5, so we would need at least 11 consonants.

Capriciously and somewhat randomly cutting down the list we arrive at this.

10 times better accuracy in the same three, 6-letter word space.

`# SYNTHETICS HANDLINGmy @synth = flat < b d f j k n p r s t w > X~ < a e i o u >;my %htnys = @synth.antipairs;my \$exp   = @synth.elems;my \$prec  = 100_000;  sub synth (Int \$v) { @synth[\$v.polymod(\$exp xx *).reverse || 0].join } sub thnys (Str \$v) { sum %htnys{\$v.comb(2).reverse} Z× 1, \$exp, \$exp² }  # ENCODE / DECODEsub w-encode ( Rat(Real) \$lat, Rat(Real) \$lon, :&f = &synth ) {    \$_ = ((\$lat +  90) × \$prec).round.fmt('%025b') ~ ((\$lon + 180) × \$prec).round.fmt('%026b');    (:2(.substr(0,17)), :2(.substr(17,17)), :2(.substr(34)))».&f} sub w-decode ( *@words, :&f = &thnys ) {    my \$bin = @words».&f.map({.fmt('%017b')}).join;    (:2(\$bin.substr(0,25))/\$prec) - 90, (:2(\$bin.substr(25))/\$prec) - 180}  # TESTINGfor 51.43372,  -0.21412, # Wimbledon center court    21.25976,-157.81173, # Diamond Head summit   -55.96525, -67.22557, # Monumento Cabo De Hornos    28.3852,  -81.5638,  # Walt Disney World    89.99999, 179.99999, # test some   -89.99999,-179.99999  # extremes  -> \$lat, \$lon {    my @words = w-encode \$lat, \$lon;    printf "Coordinates: %s, %s\n   To Index: %s\n  To 3-word: %s\nFrom 3-word: %s, %s\n\n",      \$lat, \$lon, w-encode(\$lat, \$lon, :f({\$_})).Str, @words.Str, w-decode(@words);}`
Output:
```Coordinates: 51.43372, -0.21412
To Index: 55247 71817 21724
To 3-word: jofuni kosasi diduwu
From 3-word: 51.43372, -0.21412

Coordinates: 21.25976, -157.81173
To Index: 43460 110608 121675
To 3-word: fukafa repebo safija
From 3-word: 21.25976, -157.81173

Coordinates: -55.96525, -67.22557
To Index: 13294 108118 5251
To 3-word: bukeru rasaso besane
From 3-word: -55.96525, -67.22557

Coordinates: 28.3852, -81.5638
To Index: 46244 28747 13220
To 3-word: jajasu duniri bukaka
From 3-word: 28.3852, -81.5638

Coordinates: 89.99999, 179.99999
To Index: 70312 65298 86271
To 3-word: kofoki kepifo nonope
From 3-word: 89.99999, 179.99999

Coordinates: -89.99999, -179.99999
To Index: 0 512 1
To 3-word: ba duji be
From 3-word: -89.99999, -179.99999```

## Symsyn

` | Three Word Location - convert latitude and longitude to three words lat : 28.3852lon : -81.5638 | build word array W00000 ... W28125  i if i <= 28125    ~ i \$r    #\$r szr    'W00000' \$t    (6-szr) szr     szr #\$t    + \$r \$t    + \$t \$wordarray     + i    goif endif | make latitude and longitude positive integers  {lat * 10000 + 900000} ilat {lon * 10000 + 1800000} ilon | build 43 bit integer containing latitude (21 bits) and longitude (22 bits)  ilat latlon shl latlon 22 + ilon latlon | isolate most significant 15 bits for word 1 index| next 14 bits for word 2 index| next 14 bits for word 3 index  latlon:42:15 w1 latlon:27:14 w2 latlon:13:14 w3 | fetch each word from word array     (w1*6+1) w1  \$wordarray.w1 \$w1 6  (w2*6+1) w2  \$wordarray.w2 \$w2 6  (w3*6+1) w3  \$wordarray.w3 \$w3 6 | display words  "\$w1 ' ' \$w2 ' ' \$w3" []  | reverse the procedure  | look up each word  call bsearch 0 28125 \$w1 result w1index  call bsearch 0 28125 \$w2 result w2index  call bsearch 0 28125 \$w3 result w3index | build the latlon integer from the word indexes  w1index latlon shl latlon 14 + w2index latlon shl latlon 14 + w3index latlon | isolate the latitude and longitude  latlon:21:22 ilon latlon:42:21 ilat | convert back to floating point values  {(ilon - 1800000) / 10000} lon {(ilat - 900000) / 10000} lat | display values  "'latitude = ' lat ' longitude = ' lon" []   stop bsearch param L H \$word if L <= H     ((L + H) shr 1) M    (M*6+1) I    \$wordarray.I \$w 6    if \$w > \$word        - 1 M H    else       if \$w < \$word           + 1 M L       else                return M       endif    endif    goif endif return -1   `
Output:
```W18497 W11324 W01322
latitude =          28.3852 longitude =         -81.5638
```

Using Real Words

`  | Three Word Location - convert latitude and longitude to three words  lat : 28.3852lon : -81.5638 | Build real word array of the first 28126 words of 8 | or less characters from list of 69905 words sorted alphabetically | at http://www-personal.umich.edu/~jlawler/wordlist.| There are 36282 words of 8 or less characters here.   opentext 'LargeWordList.txt' wf if ioresult <> 0    stop endif if i <= 28125    [wf] \$s    if ioresult <> 0       go closefile    endif    #\$s wsz    if wsz <= 8       + \$s \$wordarray       (8-wsz) wsz       + '        ' \$wordarray wsz       + i    endif    goif endifclosefile close wf | make latitude and longitude positive integers  {lat * 10000 + 900000} ilat {lon * 10000 + 1800000} ilon | build 43 bit integer containing latitude (21 bits) and longitude (22 bits)  ilat latlon shl latlon 22 + ilon latlon | isolate most significant 15 bits for word 1 index| next 14 bits for word 2 index| next 14 bits for word 3 index  latlon:42:15 w1 latlon:27:14 w2 latlon:13:14 w3 | fetch each word from word array     (w1*8+1) w1  \$wordarray.w1 \$w1 8  (w2*8+1) w2  \$wordarray.w2 \$w2 8  (w3*8+1) w3  \$wordarray.w3 \$w3 8 | display words  "\$w1 ' ' \$w2 ' ' \$w3" []  | reverse the procedure  | look up each word  call bsearch 0 28125 \$w1 result w1index  call bsearch 0 28125 \$w2 result w2index  call bsearch 0 28125 \$w3 result w3index | build the latlon integer from the word indexes  w1index latlon shl latlon 14 + w2index latlon shl latlon 14 + w3index latlon | isolate the latitude and longitude  latlon:21:22 ilon latlon:42:21 ilat | convert back to floating point values  {(ilon - 1800000) / 10000} lon {(ilat - 900000) / 10000} lat | display values  "'latitude = ' lat ' longitude = ' lon" []   stop bsearch param L H \$word if L <= H     ((L + H) shr 1) M    (M*8+1) I    \$wordarray.I \$w 8    if \$w > \$word        - 1 M H    else       if \$w < \$word           + 1 M L       else                return M       endif    endif    goif endif return -1   `
Output:
```ling     everyone amoral
latitude =          28.3852 longitude =         -81.5638
```

## Vlang

Translation of: Go
`import strconv fn to_word(w i64) string { return 'W\${w:05}' } fn from_word(ws string) i64 {    u, _ := strconv.common_parse_uint2(ws[1..], 10, 64)    return i64(u)} fn main() {    println("Starting figures:")    mut lat := 28.3852    mut lon := -81.5638    println("  latitude = \${lat:.4}, longitude = \${lon:.4}")     // convert lat and lon to positive integers    mut ilat := i64(lat*10000 + 900000)    mut ilon := i64(lon*10000 + 1800000)     // build 43 bit BigInt comprising 21 bits (lat) and 22 bits (lon)    mut latlon := (ilat << 22) + ilon     // isolate relevant bits    mut w1 := (latlon >> 28) & 0x7fff    mut w2 := (latlon >> 14) & 0x3fff    mut w3 := latlon & 0x3fff     // convert to word format    w1s := to_word(w1)    w2s := to_word(w2)    w3s := to_word(w3)     // and print the results    println("\nThree word location is:")    println("  \$w1s \$w2s \$w3s")     /* now reverse the procedure */    w1 = from_word(w1s)    w2 = from_word(w2s)    w3 = from_word(w3s)     latlon = (w1 << 28) | (w2 << 14) | w3    ilat = latlon >> 22    ilon = latlon & 0x3fffff    lat = f64(ilat-900000) / 10000    lon = f64(ilon-1800000) / 10000     // and print the results    println("\nAfter reversing the procedure:")    println("  latitude = \${lat:.4}, longitude = \${lon:.4}")}`
Output:
```Starting figures:
latitude = 28.3852, longitude = -81.5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28.3852, longitude = -81.5638
```

## Wren

Library: Wren-fmt
Library: Wren-big

This just follows the steps in the task description though I couldn't see any point in creating a 28,126 element array when two simple functions will do.

Note that bitwise operations are limited to 32-bit unsigned integers in Wren which isn't big enough here so we use BigInts instead.

`import "/fmt" for Fmtimport "/big" for BigInt // functions to convert to and from the word format 'W00000'var toWord   = Fn.new { |w| Fmt.swrite("W\$05d", w) }var fromWord = Fn.new { |w| Num.fromString(w[1..-1]) } // set latitude and longitude and print themSystem.print("Starting figures:")var lat = 28.3852var lon = -81.5638Fmt.print("  latitude = \$0.4f, longitude = \$0.4f", lat, lon) // convert lat and lon to positive BigIntsvar ilat = BigInt.new(lat * 10000 + 900000)var ilon = BigInt.new(lon * 10000 + 1800000) // build 43 bit BigInt comprising 21 bits (lat) and 22 bits (lon)var latlon = (ilat << 22) + ilon // isolate relevant bits and convert back to 'normal' intsvar w1 = ((latlon >> 28) & 0x7fff).toSmallvar w2 = ((latlon >> 14) & 0x3fff).toSmallvar w3 = (latlon & 0x3fff).toSmall // convert to word formatw1 = toWord.call(w1)w2 = toWord.call(w2)w3 = toWord.call(w3) // and print the resultsSystem.print("\nThree word location is:")Fmt.print("  \$s \$s \$s", w1, w2, w3) /* now reverse the procedure */w1 = BigInt.new(fromWord.call(w1))w2 = BigInt.new(fromWord.call(w2))w3 = BigInt.new(fromWord.call(w3))latlon = (w1 << 28) | (w2 << 14) | w3ilat = (latlon >> 22).toSmallilon = (latlon & 0x3fffff).toSmalllat = (ilat - 900000) / 10000lon = (ilon - 1800000) / 10000 // and print the resultsSystem.print("\nAfter reversing the procedure:")Fmt.print("  latitude = \$0.4f, longitude = \$0.4f", lat, lon)`
Output:
```Starting figures:
latitude = 28.3852, longitude = -81.5638

Three word location is:
W18497 W11324 W01322

After reversing the procedure:
latitude = 28.3852, longitude = -81.5638
```