Three word location

From Rosetta Code
Revision as of 14:46, 24 July 2020 by MaiconSoft (talk | contribs)
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.
Three Word Location.
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 a synthetic word 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.


C

Translation of: Go

<lang c>#include <stdio.h>

  1. 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

Translation of: Go

<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

Translation of: Wren

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>

  1. Three Word Location - convert latitude and longitude to three words

LAT = 28.3852 LON = -81.5638

  1. build word array W00000 ... W28125

wordarray = ["W" * string(x, pad=5) for x in 0:28125]

  1. make latitude and longitude positive integers

ILAT = Int(LAT * 10000 + 900000) ILON = Int(LON * 10000 + 1800000)

  1. build 43 bit integer containing latitude (21 bits) and longitude (22 bits)

LATLON = (ILAT << 22) + ILON

  1. isolate most significant 15 bits for word 1 index
  2. next 14 bits for word 2 index
  3. next 14 bits for word 3 index

W1 = (LATLON >> 28) & 0x7fff W2 = (LATLON >> 14) & 0x3fff W3 = LATLON & 0x3fff

  1. fetch each word from word array

w1 = wordarray[W1 + 1] w2 = wordarray[W2 + 1] w3 = wordarray[W3 + 1]

  1. display words

println("$w1 $w2 $w3")


  1. reverse the procedure
  1. look up each word

(w1index, w2index, w3index) = indexin([w1, w2, w3], wordarray) .- 1

  1. build the latlon integer from the word indexes

latlon = (w1index << 28) | (w2index << 14) | w3index


  1. isolate the latitude and longitude

ilon = latlon & 0xfffff ilat = latlon >> 22

  1. convert back to floating point values

lon = (ilon - 1800000) / 10000 lat = (ilat - 900000) / 10000

  1. 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

  1. 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 W27708 W01322
latitude = -81.5638 longitude = 28.3852

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

Phix

Translation of: Go

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

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)

<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 }


  1. 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

}


  1. 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.)

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

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. <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