From d737e04f47516c619841e57ceef9879437467f64 Mon Sep 17 00:00:00 2001 From: randogoth Date: Sun, 2 Feb 2025 09:39:55 +0200 Subject: [PATCH] rigor --- lib/src/whatfreewords_scrambler.dart | 61 +++++++++++-- test/whatfreewords_test.dart | 128 +++++++++++++++++++++++++-- 2 files changed, 171 insertions(+), 18 deletions(-) diff --git a/lib/src/whatfreewords_scrambler.dart b/lib/src/whatfreewords_scrambler.dart index 0b3e6f4..314cf6c 100644 --- a/lib/src/whatfreewords_scrambler.dart +++ b/lib/src/whatfreewords_scrambler.dart @@ -1,7 +1,7 @@ import 'dart:math'; import 'whatfreewords_cipher.dart'; -Map ilist = +final Map ilist = wlist.map((key, value) => MapEntry(value, key)); String f(String L, String R) { @@ -19,6 +19,11 @@ String f(String L, String R) { "2807694315", "8746293105" ]; + + if (x < 0 || x >= 10 || y < 0 || y >= 10) { + throw RangeError("Index out of bounds: x=$x, y=$y"); + } + return block[y][x]; } @@ -26,10 +31,15 @@ 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}"; + + temp = temp.padLeft(10, '0'); + return temp.substring(4, 10); } String feistPass(String input, String k) { + assert(input.length == 12, "Input to feistPass must be 12 characters long"); + String L = input.substring(0, 6); String R = input.substring(6, 12); String Rout = ""; @@ -58,6 +68,12 @@ String unscrambler(String input) { } String coordToWords(List coordinate) { + if (coordinate.length != 2 || + coordinate[0] < -90 || coordinate[0] > 90 || + coordinate[1] < -180 || coordinate[1] > 180) { + throw ArgumentError("Invalid coordinate values: $coordinate"); + } + int lat = (coordinate[0] * 10000).round() + 900000; int lon = (coordinate[1] * 10000).round() + 1800000; @@ -69,25 +85,46 @@ String coordToWords(List coordinate) { 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)); + lat = int.parse(lat0 + latlonRest.substring(0, 6).padRight(6, '0')); + lon = int.parse(lon0 + latlonRest.substring(6, 12).padRight(6, '0')); - 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); + String latString = lat.toString().padLeft(7, '0'); + String lonString = lon.toString().padLeft(7, '0'); + + if (latString.length < 7 || lonString.length < 7) { + throw ArgumentError("Coordinate string lengths are incorrect: lat=$latString, lon=$lonString"); + } + + String ind1 = latString.substring(0, 5); + String ind2 = lonString.substring(0, 5); + String ind3 = "0" + latString.substring(5, 7) + lonString.substring(5, 7); return "${wlist[ind1]}.${wlist[ind2]}.${wlist[ind3]}"; } List wordsToCoord(String words) { List word = words.split("."); - String ind1 = ilist[word[0]]!; - String ind2 = ilist[word[1]]!; - String ind3 = ilist[word[2]]!; + + // Ensure exactly 3 words are provided and each exists in the dictionary + if (word.length != 3 || !ilist.containsKey(word[0]) || !ilist.containsKey(word[1]) || !ilist.containsKey(word[2])) { + throw ArgumentError("Invalid words format: $words"); + } + + String? ind1 = ilist[word[0]]; + String? ind2 = ilist[word[1]]; + String? ind3 = ilist[word[2]]; + + if (ind1 == null || ind2 == null || ind3 == null || ind3.length < 5) { + throw ArgumentError("Invalid word mapping for words: $words"); + } String lat = ind1 + ind3.substring(1, 3); String lon = ind2 + ind3.substring(3, 5); + if (lat.length < 7 || lon.length < 7) { + throw ArgumentError("Malformed coordinate indices: lat=$lat, lon=$lon"); + } + String latlonRest = lat.substring(1, 7) + lon.substring(1, 7); latlonRest = unscrambler(latlonRest); @@ -97,9 +134,15 @@ List wordsToCoord(String words) { double latitude = (int.parse(lat) - 900000) / 10000.0; double longitude = (int.parse(lon) - 1800000) / 10000.0; + // Additional validation: Ensure valid latitude/longitude range + if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) { + throw ArgumentError("Computed coordinates out of range: lat=$latitude, lon=$longitude"); + } + return [latitude, longitude]; } + void main() { List coordinate = [51.50844113, -0.116708278]; String words = coordToWords(coordinate); diff --git a/test/whatfreewords_test.dart b/test/whatfreewords_test.dart index 6489a43..d973b66 100644 --- a/test/whatfreewords_test.dart +++ b/test/whatfreewords_test.dart @@ -1,15 +1,125 @@ -import 'package:whatfreewords/whatfreewords.dart'; +import 'dart:math'; import 'package:test/test.dart'; +import 'package:whatfreewords/whatfreewords.dart'; void main() { - test('Coordinate to Words and Back', () { - final List coord = [51.50844113, -0.116708278]; - final words = coordToWords(coord); - print(words); - final backToCoord = wordsToCoord(words); - print(backToCoord); + + + group('WhatFreeWords Tests', () { + test('Coordinate to Words and Back', () { + final List coord = [51.50844113, -0.116708278]; + final words = coordToWords(coord); + final backToCoord = wordsToCoord(words); + + expect(backToCoord[0], closeTo(coord[0], 0.0001)); + expect(backToCoord[1], closeTo(coord[1], 0.0001)); + }); + + test('Edge Case: Maximum Latitude and Longitude', () { + final List coord = [90.0, 180.0]; + final words = coordToWords(coord); + final backToCoord = wordsToCoord(words); + + expect(backToCoord[0], closeTo(coord[0], 0.0001)); + expect(backToCoord[1], closeTo(coord[1], 0.0001)); + }); + + test('Edge Case: Minimum Latitude and Longitude', () { + final List coord = [-90.0, -180.0]; + final words = coordToWords(coord); + final backToCoord = wordsToCoord(words); + + expect(backToCoord[0], closeTo(coord[0], 0.0001)); + expect(backToCoord[1], closeTo(coord[1], 0.0001)); + }); + + test('Invalid Input: Out of Range Coordinates', () { + expect(() => coordToWords([100.0, 200.0]), throwsA(isA())); + expect(() => coordToWords([-100.0, -200.0]), throwsA(isA())); + }); + + test('Invalid Input: Malformed Words String', () { + expect(() => wordsToCoord("invalid.word.string"), throwsA(isA())); + }); + + test('Consistency Check: Multiple Calls Yield Same Result', () { + final List coord = [34.0522, -118.2437]; + final words1 = coordToWords(coord); + final words2 = coordToWords(coord); + expect(words1, equals(words2)); + }); + + test('Inverse Mapping: Words Should Convert Back Accurately', () { + final List coord = [37.7749, -122.4194]; + final words = coordToWords(coord); + final backToCoord = wordsToCoord(words); + expect(backToCoord[0], closeTo(coord[0], 0.0001)); + expect(backToCoord[1], closeTo(coord[1], 0.0001)); + }); + + test('Randomized Coordinate Encoding and Decoding', () { + final Random rand = Random(); + for (int i = 0; i < 100; i++) { + final double lat = -90 + rand.nextDouble() * 180; + final double lon = -180 + rand.nextDouble() * 360; + + final words = coordToWords([lat, lon]); + final backToCoord = wordsToCoord(words); + + expect(backToCoord[0], closeTo(lat, 0.0001)); + expect(backToCoord[1], closeTo(lon, 0.0001)); + } + }); + + test('Boundary Conditions', () { + final List> validCoords = [ + [-90.0, 0.0], + [90.0, 0.0], + [0.0, -180.0], + [0.0, 180.0] + ]; + + for (var coord in validCoords) { + final words = coordToWords(coord); + final backToCoord = wordsToCoord(words); + expect(backToCoord[0], closeTo(coord[0], 0.0001)); + expect(backToCoord[1], closeTo(coord[1], 0.0001)); + } + + final List> invalidCoords = [ + [-90.1, 0.0], + [90.1, 0.0], + [0.0, -180.1], + [0.0, 180.1] + ]; + + for (var coord in invalidCoords) { + expect(() => coordToWords(coord), throwsA(isA())); + } + }); + + test('Minimum and Maximum Encodable Values', () { + final List minCoord = [-89.9999, -179.9999]; + final List maxCoord = [89.9999, 179.9999]; + + final wordsMin = coordToWords(minCoord); + final wordsMax = coordToWords(maxCoord); + + final backToCoordMin = wordsToCoord(wordsMin); + final backToCoordMax = wordsToCoord(wordsMax); + + expect(backToCoordMin[0], closeTo(minCoord[0], 0.0001)); + expect(backToCoordMin[1], closeTo(minCoord[1], 0.0001)); + expect(backToCoordMax[0], closeTo(maxCoord[0], 0.0001)); + expect(backToCoordMax[1], closeTo(maxCoord[1], 0.0001)); + }); + + test('Invalid Word Combination', () { + expect(() => wordsToCoord("valid.valid.invalid"), throwsA(isA())); + expect(() => wordsToCoord("invalid.valid.valid"), throwsA(isA())); + expect(() => wordsToCoord("valid.invalid.valid"), throwsA(isA())); + }); + - expect(backToCoord[0], closeTo(coord[0], 0.0001)); - expect(backToCoord[1], closeTo(coord[1], 0.0001)); }); } \ No newline at end of file