Three word location: Difference between revisions
(Julia output also corrected on second) |
No edit summary |
||
Line 1: | Line 1: | ||
{{draft task}} |
{{draft task}} |
||
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. |
|||
Three Word Location. |
|||
⚫ | |||
latitude longitude pair. |
|||
For: |
|||
latitude = 28.3852 |
|||
longitude = -81.5638 |
|||
Display: W18497 W11324 W01322 |
|||
⚫ | |||
⚫ | |||
in the form W00000 thru W28125. |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
For: latitude = 28.3852 longitude = -81.5638 Display: W18497 W11324 W01322 |
|||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
Extra credit: Find a way to populate the word array with actual words. |
|||
Revision as of 17:21, 27 July 2020
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.
C
<lang c>#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;
}</lang>
- 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
<lang Delphi> 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.
</lang>
- 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
Go
Though no need for big integers as we have int64 built in. <lang go>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)
}</lang>
- 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. <lang julia>
- Three Word Location - convert latitude and longitude to three words
LAT = 28.3852 LON = -81.5638
- build word array W00000 ... W28125
wordarray = ["W" * string(x, pad=5) for x in 0:28125]
- make latitude and longitude positive integers
ILAT = 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 index
W1 = (LATLON >> 28) & 0x7fff W2 = (LATLON >> 14) & 0x3fff W3 = LATLON & 0x3fff
- fetch each word from word array
w1 = wordarray[W1 + 1] w2 = wordarray[W2 + 1] w3 = wordarray[W3 + 1]
- display words
println("$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 indexes
latlon = (w1index << 28) | (w2index << 14) | w3index
- isolate the latitude and longitude
ilon = latlon & 0xfffff ilat = latlon >> 22
- convert back to floating point values
lon = (ilon - 1800000) / 10000 lat = (ilat - 900000) / 10000
- display values
println("latitude = $lat longitude = $lon")
</lang>
- Output:
W18497 W11324 W01322 latitude = 28.3852 longitude = -81.5638
Idiomatic version with scrambling
<lang julia>using Random
const LAT = 28.3852 const LON = -81.5638
- build word array W00000 ... W28125
const 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) / 10000
end
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")
</lang>
- Output:
W18497 W11324 W01322 latitude = -81.5638 longitude = 28.3852 With scramble using key 12345678: W20242 W23427 W16215 latitude = -81.5638 longitude = 28.3852
Phix
Requires 0.8.2+ - I used this task as an excuse to add << and >> bit shift operators,
alongside && and || as infix versions of and/or_bits(). Pre-0.8.2 code left in as comments.
Note the precedence of these ops is not yet finalised, they need extra parenthesis for now.
<lang Phix>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*power(2,22) + ilon -- (pre-0.8.2) atom latlon := (ilat << 22) + ilon
-- isolate relevant bits --integer w1 = and_bits(floor(latlon/power(2,28)),0x7fff), --("") -- w2 = and_bits(floor(latlon/power(2,14)),0x3fff), -- w3 = and_bits(latlon,0x3fff) 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*power(2,28) + w2*power(2,14) + w3 -- (pre-0.8.2) --ilat = floor(latlon/power(2,22)) --ilon = and_bits(latlon,0x3fffff) 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})</lang>
- 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
The task
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)
<lang perl6># SYNTHETICS HANDLING my @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 / DECODE
sub 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
}
- TESTING
for 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( { $_ } );
}</lang>
- 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.
<lang perl6># SYNTHETICS HANDLING my @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 / DECODE
sub 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
}
- TESTING
for 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);
}</lang>
- 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
<lang Symsyn> | Three Word Location - convert latitude and longitude to three words
lat : 28.3852 lon : -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
</lang>
- Output:
W18497 W11324 W01322 latitude = 28.3852 longitude = -81.5638
Wren
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. <lang ecmascript>import "/fmt" for Fmt import "/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 them System.print("Starting figures:") var lat = 28.3852 var lon = -81.5638 Fmt.print(" latitude = $0.4f, longitude = $0.4f", lat, lon)
// convert lat and lon to positive BigInts var 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' ints var w1 = ((latlon >> 28) & 0x7fff).toSmall var w2 = ((latlon >> 14) & 0x3fff).toSmall var w3 = (latlon & 0x3fff).toSmall
// convert to word format w1 = toWord.call(w1) w2 = toWord.call(w2) w3 = toWord.call(w3)
// and print the results System.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) | w3 ilat = (latlon >> 22).toSmall ilon = (latlon & 0x3fffff).toSmall lat = (ilat - 900000) / 10000 lon = (ilon - 1800000) / 10000
// and print the results System.print("\nAfter reversing the procedure:") Fmt.print(" latitude = $0.4f, longitude = $0.4f", lat, lon)</lang>
- 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