initial release

This commit is contained in:
randogoth 2025-02-02 00:13:39 +02:00
commit 23cbff14b7
12 changed files with 251 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/
# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock

3
CHANGELOG.md Normal file
View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial version.

14
LICENSE Normal file
View file

@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

42
README.md Normal file
View file

@ -0,0 +1,42 @@
# WhatFreeWords
[![](http://www.wtfpl.net/wp-content/uploads/2012/12/wtfpl-badge-4.png")](http://www.wtfpl.net/)
This is the Dart port of the original [JavaScript implementation](https://github.com/pballett/whatfreewords) that was published as a small test to show how one might reversibly map each ~1m² element of the globe onto a 3-tuple of words.
## Installation
In your Flutter/Dart `pubspec.yaml` file include
```yaml
dependencies:
whatfreewords:
git:
url: https://github.com/randogoth/whatfreewords.git
```
## Usage
```dart
import 'package:whatfreewords/whatfreewords.dart';
// coordinate to wfw
final List<double> coord = [51.50844113, -0.116708278];
final words = coordToWords(coord);
print(words); // demagogue.shuts.troll
// wfw to coordinate
final coords = wordsToCoord(words);
print(coords); // [51.5084, -0.1167]
```
Check it out [here](https://pballett.github.io/whatfreewords/).
To do this we needed three things:
1. A function which maps metre-accurate (latitude, longitude) pairs into three 5-digit integers below 40k.
2. An ordered list of 40k words which acts as a bijection between integers and words.
3. A scrambling function which can be called in the first function to ensure nearby (latitude,longitude) pairs are mapped to very different integers
Note that all of these functions must be invertible for the mapping to be reversible.
The scrambling function implemented here is inspired by format preserving encryption: it maps a sequence of 14 digits to another by passing it through a [Feistel network](https://en.wikipedia.org/wiki/Feistel_cipher). There is no need for this encryption to be secure, so the implementation has been simplifed. The point is just to map 14-digits to 14-digits, to exhibit sensitive dependence on the input and to be reversible.

30
analysis_options.yaml Normal file
View file

@ -0,0 +1,30 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View file

@ -0,0 +1,6 @@
import 'package:whatfreewords/whatfreewords.dart';
void main() {
var awesome = Awesome();
print('awesome: ${awesome.isAwesome}');
}

View file

@ -0,0 +1,6 @@
// TODO: Put public facing types in this file.
/// Checks if you are awesome. Spoiler: you are.
class Awesome {
bool get isAwesome => true;
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,110 @@
import 'dart:math';
import 'whatfreewords_cipher.dart';
Map<String, String> ilist =
wlist.map((key, value) => MapEntry(value, key));
String f(String L, String R) {
int x = int.parse(L);
int y = int.parse(R);
List<String> block = [
"1032547698",
"7215839046",
"9532817640",
"1094365872",
"6895730412",
"1074389256",
"5749208163",
"8976543201",
"2807694315",
"8746293105"
];
return block[y][x];
}
String F(String L, String K) {
int x = int.parse(L);
int k = int.parse(K);
String temp = "${x * x * x * k * 3 + 23 * k * k * x * x * x + k * (x + k) + 3}";
return temp.substring(4, 10);
}
String feistPass(String input, String k) {
String L = input.substring(0, 6);
String R = input.substring(6, 12);
String Rout = "";
String temp = F(R, k);
for (int i = 0; i < 6; i++) {
Rout += f(L[i], temp[i]);
}
return R + Rout;
}
String key = "248473";
String scrambler(String input) {
for (int i = 0; i < key.length; i++) {
input = feistPass(input, key[i]);
}
return input;
}
String unscrambler(String input) {
input = input.substring(6, 12) + input.substring(0, 6);
for (int i = 0; i < key.length; i++) {
input = feistPass(input, key[key.length - 1 - i]);
}
return input.substring(6, 12) + input.substring(0, 6);
}
String coordToWords(List<double> coordinate) {
int lat = (coordinate[0] * 10000).round() + 900000;
int lon = (coordinate[1] * 10000).round() + 1800000;
String latStr = lat.toString().padLeft(7, '0');
String lonStr = lon.toString().padLeft(7, '0');
String lat0 = latStr[0];
String lon0 = lonStr[0];
String latlonRest = scrambler(latStr.substring(1, 7) + lonStr.substring(1, 7));
lat = int.parse(lat0 + latlonRest.substring(0, 6));
lon = int.parse(lon0 + latlonRest.substring(6, 12));
String ind1 = lat.toString().substring(0, 5);
String ind2 = lon.toString().substring(0, 5);
String ind3 = "0" + lat.toString().substring(5, 7) + lon.toString().substring(5, 7);
return "${wlist[ind1]}.${wlist[ind2]}.${wlist[ind3]}";
}
List<double> wordsToCoord(String words) {
List<String> word = words.split(".");
String ind1 = ilist[word[0]]!;
String ind2 = ilist[word[1]]!;
String ind3 = ilist[word[2]]!;
String lat = ind1 + ind3.substring(1, 3);
String lon = ind2 + ind3.substring(3, 5);
String latlonRest = lat.substring(1, 7) + lon.substring(1, 7);
latlonRest = unscrambler(latlonRest);
lat = lat[0] + latlonRest.substring(0, 6);
lon = lon[0] + latlonRest.substring(6, 12);
double latitude = (int.parse(lat) - 900000) / 10000.0;
double longitude = (int.parse(lon) - 1800000) / 10000.0;
return [latitude, longitude];
}
void main() {
List<double> coordinate = [51.50844113, -0.116708278];
String words = coordToWords(coordinate);
print("Words: $words");
List<double> backToCoord = wordsToCoord(words);
print("Coordinates: ${backToCoord[0]}, ${backToCoord[1]}");
}

3
lib/whatfreewords.dart Normal file
View file

@ -0,0 +1,3 @@
library whatfreewords;
export 'src/whatfreewords_scrambler.dart';

12
pubspec.yaml Normal file
View file

@ -0,0 +1,12 @@
name: whatfreewords
description: A library for converting coordinates to words and back using a Feistel network.
version: 1.0.0
homepage: https://github.com/randogoth/whatfreewords
environment:
sdk: '>=2.17.0 <3.0.0'
dependencies:
dev_dependencies:
test: ^1.21.0

View file

@ -0,0 +1,15 @@
import 'package:whatfreewords/whatfreewords.dart';
import 'package:test/test.dart';
void main() {
test('Coordinate to Words and Back', () {
final List<double> coord = [51.50844113, -0.116708278];
final words = coordToWords(coord);
print(words);
final backToCoord = wordsToCoord(words);
print(backToCoord);
expect(backToCoord[0], closeTo(coord[0], 0.0001));
expect(backToCoord[1], closeTo(coord[1], 0.0001));
});
}