initial release
This commit is contained in:
commit
23cbff14b7
12 changed files with 251 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal 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
3
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
- Initial version.
|
||||||
14
LICENSE
Normal file
14
LICENSE
Normal 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
42
README.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# WhatFreeWords
|
||||||
|
|
||||||
|
[](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
30
analysis_options.yaml
Normal 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
|
||||||
6
example/whatfreewords_example.dart
Normal file
6
example/whatfreewords_example.dart
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import 'package:whatfreewords/whatfreewords.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
var awesome = Awesome();
|
||||||
|
print('awesome: ${awesome.isAwesome}');
|
||||||
|
}
|
||||||
6
lib/src/whatfreewords_base.dart
Normal file
6
lib/src/whatfreewords_base.dart
Normal 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;
|
||||||
|
}
|
||||||
3
lib/src/whatfreewords_cipher.dart
Normal file
3
lib/src/whatfreewords_cipher.dart
Normal file
File diff suppressed because one or more lines are too long
110
lib/src/whatfreewords_scrambler.dart
Normal file
110
lib/src/whatfreewords_scrambler.dart
Normal 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
3
lib/whatfreewords.dart
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
library whatfreewords;
|
||||||
|
|
||||||
|
export 'src/whatfreewords_scrambler.dart';
|
||||||
12
pubspec.yaml
Normal file
12
pubspec.yaml
Normal 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
|
||||||
15
test/whatfreewords_test.dart
Normal file
15
test/whatfreewords_test.dart
Normal 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));
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue