Anadromes
An anadrome is similar to a palindrome except, rather than spelling the same word or phrase when reversed, it spells a different word or phrase. An anadrome is a special case of an anagram.

You are encouraged to solve this task according to the task description, using any language you may know.
Anadrome is a portmanteau of the words anagram and palindrome.
For instance, regal and lager are anadromes.
- Task
Using the words.txt file from https://github.com/dwyl/english-words, find and display all of the anadrome pairs with more than 6 characters.
Each word pair should only show up one time in the list.
-- Find all anadromes of length > 6
-- J. Carter 2023 Mar
with Ada.Containers.Indefinite_Ordered_Sets;
with Ada.Text_IO;
procedure Anadromes is
package Word_Sets is new Ada.Containers.Indefinite_Ordered_Sets (Element_Type => String);
function Reversed (S : in String) return String is
(if S = "" then S else S (S'Last) & Reversed (S (S'First .. S'Last - 1) ) );
File : Ada.Text_IO.File_Type;
W : Word_Sets.Set;
begin -- Anadromes
Ada.Text_IO.Open (File => File, Mode => Ada.Text_IO.In_File, Name => "words.txt");
Fill : loop
exit Fill when Ada.Text_IO.End_Of_File (File);
W.Insert (New_Item => Ada.Text_IO.Get_Line (File) );
end loop Fill;
Ada.Text_IO.Close (File => File);
Search : for Word of W loop
if Word'Length > 6 then
Backwards : declare
Rev : constant String := Reversed (Word);
begin -- Backwards
if Word < Rev and W.Contains (Rev) then
Ada.Text_IO.Put_Line (Item => Word & (1 .. 10 - Word'Length => ' ') & Rev);
end if;
end Backwards;
end if;
end loop Search;
end Anadromes;
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
scope # find some anadromes - words that when reversed are another word
# the anadromes must be at least 7 characters long
local constant words := [];
for word in io.lines( "words.txt" ) do
if size word > 6 then
words[ word ] := true
fi
od;
for w, _ in pairs( words ) do
local constant r := w@@reverse();
if r > w then
if words[ r ] then
printf( "%12s %s\n", w, r )
fi
fi
od
end- Output:
sallets stellas
revotes setover
reliver reviler
dessert tressed
amaroid diorama
dioramas samaroid
deliver reviled
leveler relevel
redrawer rewarder
desserts stressed
deifier reified
gateman nametag
relever reveler
pat-pat tap-tap
reknits stinker
deviler relived
degener reneged
Reads the words from standard input.
Unfortunately, Algol 68G doesn't like an array of STRINGs with more than 300 000 elements, even though it allows INT arrays to have millions - at least under Windows
(I haven't tried it with Linux).
So you will need to use another compiler under Windows.
As in the Wren sample, the words are quicksorted so binary searching can be used to find the reversed words.
This sample reads the words from standard input. The output shown for standard input redirected to words.txt.
BEGIN # find some anadromes: words that when reversed are also words #
# in-place quick sort an array of strings #
PROC s quicksort = ( REF[]STRING a, INT lb, ub )VOID:
IF ub > lb
THEN
# more than one element, so must sort #
INT left := lb;
INT right := ub;
# choosing the middle element of the array as the pivot #
STRING pivot := a[ left + ( ( right + 1 ) - left ) OVER 2 ];
WHILE
WHILE IF left <= ub THEN a[ left ] < pivot ELSE FALSE FI
DO
left +:= 1
OD;
WHILE IF right >= lb THEN a[ right ] > pivot ELSE FALSE FI
DO
right -:= 1
OD;
left <= right
DO
STRING t := a[ left ];
a[ left ] := a[ right ];
a[ right ] := t;
left +:= 1;
right -:= 1
OD;
s quicksort( a, lb, right );
s quicksort( a, left, ub )
FI # s quicksort # ;
# returns TRUE if item is in list, FALSE otherwise #
# - based on the iterative routine in the binary search task #
PROC contains = ( []STRING list, STRING item, INT lb, ub )BOOL:
BEGIN
INT low := lb,
INT high := ub;
WHILE low < high DO
INT mid = ( low + high ) OVER 2;
IF list[ mid ] > item THEN high := mid - 1
ELIF list[ mid ] < item THEN low := mid + 1
ELSE low := high := mid
FI
OD;
list[ low ] = item
END # contains # ;
# set the end of file handler for stand in #
on logical file end( stand in, ( REF FILE f )BOOL: at eof := TRUE );
[ 1 : 500 000 ]STRING words;
INT t count := 0;
INT w count := 0;
INT max length := 0;
BOOL at eof := FALSE;
WHILE
STRING word;
read( ( word, newline ) );
NOT at eof
DO
t count +:= 1;
INT w length := 1 + ( UPB word - LWB word );
IF w length > 6 THEN
w count +:= 1;
words[ w count ] := word;
IF w length > max length THEN max length := w length FI
FI
OD;
print( ( "read ", whole( t count, 0 ), " words, "
, "the longest is ", whole( max length, 0 ), " characters"
, newline
, " ", whole( w count, 0 ), " words are longer than 6 characters"
, newline, newline
)
);
s quicksort( words, 1, w count ); # sort the words for binary search #
print( ( "The following anadromes are present:", newline, newline ) );
INT a count := 0;
FOR i TO w count DO
STRING word = words[ i ];
STRING reverse word := "";
FOR w pos FROM LWB word TO UPB word DO word[ w pos ] +=: reverse word OD;
IF word < reverse word THEN
IF contains( words, reverse word, 1, w count ) THEN
# have an anadromic pair #
INT w length = 1 + ( UPB words[ i ] - LWB words[ i ] );
TO 10 - w length DO print( ( " " ) ) OD;
print( ( words[ i ], " :: ", reverse word, newline ) );
a count +:= 1
FI
FI
OD;
print( ( newline, "Found ", whole( a count, 0 ), " anadromes", newline ) )
END- Output:
read 466551 words, the longest is 45 characters
387537 words are longer than 6 characters
The following anadromes are present:
amaroid :: diorama
degener :: reneged
deifier :: reified
deliver :: reviled
dessert :: tressed
desserts :: stressed
deviler :: relived
dioramas :: samaroid
gateman :: nametag
leveler :: relevel
pat-pat :: tap-tap
redrawer :: rewarder
reknits :: stinker
relever :: reveler
reliver :: reviler
revotes :: setover
sallets :: stellas
Found 17 anadromes
use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
use scripting additions
on Anadromes(textFile, minLength)
set |⌘| to current application
-- Read the text from the file.
set theText to |⌘|'s class "NSString"'s stringWithContentsOfFile:(textFile's POSIX path) ¬
usedEncoding:(missing value) |error|:(missing value)
-- Lose paragraphs (one word per paragraph) which have fewer than minLength characters.
set theText to theText's stringByReplacingOccurrencesOfString:("(?m)^.{0," & minLength - 1 & "}\\R") withString:("") ¬
options:(|⌘|'s NSRegularExpressionSearch) range:({0, theText's |length|()})
-- Get the remaining paragraphs as an array.
set wordArray to (theText's componentsSeparatedByCharactersInSet:(|⌘|'s class "NSCharacterSet"'s controlCharacterSet()))
-- Derive a list of reversed words.
script o
property wordList : (wordArray) as list
end script
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to ""
repeat with i from 1 to (count o's wordList)
set o's wordList's item i to (o's wordList's item i's characters's reverse) as text
end repeat
-- Get a list of words that are in both the original and reversed groups.
set reversedWordSet to |⌘|'s class "NSSet"'s setWithArray:(o's wordList)
set filter to |⌘|'s class "NSPredicate"'s predicateWithFormat_("self IN %@", reversedWordSet)
set o's wordList to (wordArray's filteredArrayUsingPredicate:(filter)) as list
-- Build the output line by line, omitting palindromes and already matched word pairs.
set output to {}
repeat with i from 1 to (count o's wordList)
set thisWord to o's wordList's item i
set o's wordList's item i to missing value
set reversedWord to (thisWord's characters's reverse) as text
if ({reversedWord} is in o's wordList) then set output's end to thisWord & " <--> " & reversedWord
end repeat
set AppleScript's text item delimiters to linefeed
set output to output as text
set AppleScript's text item delimiters to astid
return output
end Anadromes
return Anadromes(((path to desktop as text) & "www.rosettacode.org:words.txt") as «class furl», 7)
- Output:
"amaroid <--> diorama
degener <--> reneged
deifier <--> reified
deliver <--> reviled
dessert <--> tressed
desserts <--> stressed
deviler <--> relived
dioramas <--> samaroid
gateman <--> nametag
leveler <--> relevel
pat-pat <--> tap-tap
redrawer <--> rewarder
reknits <--> stinker
relever <--> reveler
reliver <--> reviler
revotes <--> setover
sallets <--> stellas"
words: select read.lines ./"words.txt" => [6 < size &]
dict: #.raw flatten couple words array.of: size words null
loop words 'word [
reversed: reverse word
if and? word < reversed, key? dict reversed ->
print [word reversed]
]
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
# syntax: GAWK -f ANADROMES.AWK WORDS.TXT
#
# sorting:
# PROCINFO["sorted_in"] is used by GAWK
# SORTTYPE is used by Thompson Automation's TAWK
#
BEGIN {
width = 6
}
{ if (length($0) > width) {
arr[$0]++
}
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc" ; SORTTYPE = 1
for (i in arr) {
tmp = reverse(i)
if (tmp in arr) {
if (i == tmp) { continue }
if (tmp in shown_arr) { continue }
printf("%11s %11s\n",i,tmp)
shown_arr[i] = ""
}
}
printf("%d words, %d > %d characters, %d anadromes\n",NR,length(arr),width,length(shown_arr))
exit(0)
}
function reverse(str, i,rts) {
for (i=length(str); i>=1; i--) {
rts = rts substr(str,i,1)
}
return(rts)
}
- Output:
amaroid diorama
degener reneged
deifier reified
deliver reviled
dessert tressed
desserts stressed
deviler relived
dioramas samaroid
gateman nametag
leveler relevel
pat-pat tap-tap
redrawer rewarder
reknits stinker
relever reveler
reliver reviler
revotes setover
sallets stellas
466551 words, 387537 > 6 characters, 17 anadromes
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXLINES 1000000
#define MAXLEN 1000
char buffer[MAXLEN];
char* words[MAXLINES];
char* wordsReversed[MAXLINES];
void reverseString(char* s) {
int c, i, j;
for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
c = s[i];
s[i] = s[j];
s[j] = c;
}
}
int pstrcmp(const void* a, const void* b) {
return strcmp(*(const char**)a, *(const char**)b);
}
int main() {
int i, j;
int wordCount;
FILE* file = fopen("words.txt", "r");
wordCount = 0;
while (fgets(buffer, MAXLEN, file)) {
buffer[strcspn(buffer, "\n")] = 0;
if (strlen(buffer) > 6) {
words[wordCount] = malloc(strlen(buffer) + 1);
strcpy(words[wordCount], buffer);
wordCount++;
}
}
fclose(file);
for (i = 0; i < wordCount; i++) {
wordsReversed[i] = malloc(strlen(words[i]) + 1);
strcpy(wordsReversed[i], words[i]);
reverseString(wordsReversed[i]);
}
qsort(wordsReversed, wordCount, sizeof(char*), pstrcmp);
for (i = 0; i < wordCount; i++) {
strcpy(buffer, words[i]);
reverseString(buffer);
if (strcmp(words[i], buffer) < 0 &&
bsearch(&words[i], wordsReversed, wordCount, sizeof(char*), pstrcmp))
printf("%-12s%-12s\n", words[i], buffer);
}
for (i = 0; i < wordCount; i++) {
free(words[i]);
free(wordsReversed[i]);
}
return 0;
}
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
using System;
using System.Linq;
class Programm
{
static void Main()
{
var words = File.ReadAllLines("words.txt").Where(s => s.Length > 6).ToHashSet();
foreach (string w in words) {
var w_rev = new string(w.Reverse().ToArray());
if (words.Contains(w_rev) && w.CompareTo(w_rev) < 0)
Console.WriteLine("{0, -12}{1, -12}", w, w_rev);
}
}
}
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
#include <algorithm>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> words;
std::ifstream in("words.txt");
if (!in) {
std::cerr << "Cannot open file words.txt.\n";
return EXIT_FAILURE;
}
std::string line;
while (getline(in, line)) {
if (line.size() > 6)
words.push_back(line);
}
sort(words.begin(), words.end());
for (const std::string& word : words) {
std::string reversed(word.rbegin(), word.rend());
if (reversed > word &&
binary_search(words.begin(), words.end(), reversed)) {
std::cout << std::setw(8) << word << " <-> " << reversed << '\n';
}
}
return EXIT_SUCCESS;
}
- Output:
amaroid <-> diorama degener <-> reneged deifier <-> reified deliver <-> reviled dessert <-> tressed desserts <-> stressed deviler <-> relived dioramas <-> samaroid gateman <-> nametag leveler <-> relevel pat-pat <-> tap-tap redrawer <-> rewarder reknits <-> stinker relever <-> reveler reliver <-> reviler revotes <-> setover sallets <-> stellas
***************************************************************
* PROGRAM: ANADROMES
* AUTHOR: PETER HAGELUND
***************************************************************
IDENTIFICATION DIVISION.
PROGRAM-ID. ANADROMES.
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
REPOSITORY.
FUNCTION ALL INTRINSIC.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT WORD-FILE ASSIGN TO FILENAME
ORGANIZATION IS LINE SEQUENTIAL
ACCESS MODE IS SEQUENTIAL
FILE STATUS IS FILE-STATUS.
DATA DIVISION.
FILE SECTION.
FD WORD-FILE
RECORD CONTAINS 1 TO 80 CHARACTERS
DATA RECORD IS WORD-REC.
01 WORD-REC PIC X(80).
WORKING-STORAGE SECTION.
01 FILE-STATUS PIC X(2).
01 WORD-CNT PIC 9(9).
01 MEM-SIZE PIC 9(9).
01 MEM-PTR USAGE POINTER VALUE NULL.
01 IDX USAGE INDEX.
LINKAGE SECTION.
01 WORD-TABLE.
03 WORD-ENTRY OCCURS UNBOUNDED DEPENDING ON WORD-CNT
ASCENDING KEY IS WORD
INDEXED BY WORD-IDX.
05 WORD PIC X(30).
05 REV-WORD PIC X(30).
05 WORD-STATUS PIC X.
88 UNKNOWN VALUE ' '.
88 NOT-ANADROME VALUE 'N'.
88 IS-ANADROME VALUE 'Y'.
88 IS-OTHER VALUE 'O'.
PROCEDURE DIVISION.
MAIN SECTION.
* FIRST PASS: COUNT THE WORDS
PERFORM READ-WORDS.
* CALCULATE MEMORY NEEDED AND ALLOCATE IT
COMPUTE MEM-SIZE = WORD-CNT * LENGTH OF WORD-ENTRY.
ALLOCATE MEM-SIZE CHARACTERS RETURNING MEM-PTR.
SET ADDRESS OF WORD-TABLE TO MEM-PTR.
* SECOND PASS: READ THE WORDS INTO THE ALLOCATED TABLE
PERFORM READ-WORDS.
* SORT THE TABLE
SORT WORD-ENTRY ON ASCENDING KEY WORD.
* FIND THE ANADROMES
PERFORM VARYING IDX FROM 1 BY 1 UNTIL IDX > WORD-CNT
IF NOT UNKNOWN(IDX) THEN
EXIT PERFORM CYCLE
END-IF
IF WORD(IDX) = REV-WORD(IDX) THEN
SET NOT-ANADROME(IDX) TO TRUE
EXIT PERFORM CYCLE
END-IF
SEARCH ALL WORD-ENTRY
AT END
SET NOT-ANADROME(IDX) TO TRUE
WHEN WORD(WORD-IDX) = REV-WORD(IDX)
SET IS-ANADROME(IDX) TO TRUE
SET IS-OTHER(WORD-IDX) TO TRUE
DISPLAY
TRIM(WORD(IDX)),
' ',
TRIM(REV-WORD(IDX))
END-DISPLAY
END-SEARCH
END-PERFORM.
* FREE THE MEMORY FOR THE TABLE
FREE MEM-PTR.
GOBACK.
READ-WORDS SECTION.
MOVE 0 TO WORD-CNT.
OPEN INPUT WORD-FILE.
IF FILE-STATUS NOT = '00' THEN
DISPLAY 'OPEN FAILED: ', FILE-STATUS
EXIT SECTION
END-IF.
PERFORM UNTIL FILE-STATUS NOT = '00'
READ WORD-FILE
IF FILE-STATUS NOT = '00' THEN
EXIT PERFORM
END-IF
IF LENGTH(TRIM(WORD-REC)) > 6 THEN
ADD 1 TO WORD-CNT
IF MEM-PTR = NULL THEN
EXIT PERFORM CYCLE
END-IF
MOVE WORD-REC TO WORD(WORD-CNT)
MOVE REVERSE(TRIM(WORD-REC)) TO REV-WORD(WORD-CNT)
SET UNKNOWN(WORD-CNT) TO TRUE
END-IF
END-PERFORM.
CLOSE WORD-FILE.
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
The hash-table's equalp test is case-insensitive, and so we find 32 pairs rather than 17. However, the table maps each word to itself, and so looking up a reversed word gets it in its original case, as can be seen in the output.
(defun read-words (filename)
(let ((words '()))
(with-open-file (s filename :direction :input)
(loop
(let ((word (read-line s nil nil)))
(if word
(when (> (length word) 6)
(setq words (cons word words)))
(return (reverse words))))))))
(defun anadromes ()
(let ((words (read-words "notes/words.txt"))
(dict (make-hash-table :test #'equalp)))
(dolist (word words)
(setf (gethash word dict) word))
(mapcar
(lambda (word)
(list word (gethash (reverse word) dict)))
(remove-if-not
(lambda (word)
(let ((rev (reverse word)))
(and (string-lessp word rev)
(gethash rev dict))))
words))))
(format t "~%~:{~10A ~10A~%~}~%"
(anadromes))
- Output:
amaroid diorama anacara Aracana Annabal Labanna Artamus Sumatra Colbert Trebloc degener reneged deifier reified Delbert trebled Delevan naveled deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid Eimmart trammie Emmeram maremme gateman nametag Latimer remital Lattimer remittal lessees Seessel leveler relevel Nicolaus Sualocin pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover ROYGBIV vibgyor Rotanev Venator sallets stellas sennits Stinnes
words = File.read_lines("words.txt").select {|w| w.size > 6 }.to_set
words.compact_map {|word|
reversed = word.reverse
if word < reversed && words.includes?(reversed)
word
end
}.sort!.each do |anadrome|
printf "%-10s %s\n", anadrome, anadrome.reverse
end
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
import std.stdio, std.file, std.string, std.range, std.algorithm;
void main() {
auto words = readText("words.txt").splitLines.filter!"a.length > 6".array.sort;
foreach (w; words)
if (w < w.dup.reverse && words.contains(w.dup.reverse))
writefln("%-12s %-12s", w, w.dup.reverse);
}
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
It turns out that for the task at hand, the time created to create an index is far greater than the savings that accrue from having it, so the 'CREATE INDEX' line has been commented out.
create or replace function isPalindrome(s) as (
reverse(s) = s
);
create or replace table words as
from read_csv_auto(
'words.txt',
header=false, sep='', quote='', columns={'word': VARCHAR})
where NOT isPalindrome(word);
-- CREATE INDEX word_index ON words(word);
create or replace function anadromes(mn) as table (
with candidates as (
select word, reverse(word) as anadrome
from words
where word < anadrome and anadrome IN (from words)
)
from candidates
where length(word) >= mn
order by word
);
from anadromes(7);
- Output:
┌──────────┬──────────┐ │ word │ anadrome │ │ varchar │ varchar │ ├──────────┼──────────┤ │ amaroid │ diorama │ │ degener │ reneged │ │ deifier │ reified │ │ deliver │ reviled │ │ dessert │ tressed │ │ desserts │ stressed │ │ deviler │ relived │ │ dioramas │ samaroid │ │ gateman │ nametag │ │ leveler │ relevel │ │ pat-pat │ tap-tap │ │ redrawer │ rewarder │ │ reknits │ stinker │ │ relever │ reveler │ │ reliver │ reviler │ │ revotes │ setover │ │ sallets │ stellas │ ├──────────┴──────────┤ │ 17 rows 2 columns │ └─────────────────────┘
// Anadromes. Nigel Galloway: June 26th., 2022
let words=seq{use n=System.IO.File.OpenText("words.txt") in while not n.EndOfStream do yield n.ReadLine()}|>Seq.filter(fun n->6<(Seq.length n))|>Seq.map(fun n->n.ToCharArray())|>Set.ofSeq
Set.intersect words (words|>Set.map(Array.rev))|>Set.iter(fun n->if n<Array.rev n then printfn "%s" (System.String n))
- Output:
amaroid degener deifier deliver dessert desserts deviler dioramas gateman leveler pat-pat redrawer reknits relever reliver revotes sallets
USING: assocs grouping hash-sets io.encodings.ascii io.files
kernel math prettyprint sequences sets sets.extras ;
"words.txt" ascii file-lines [ length 6 > ] filter dup >hash-set '[ reverse _ in? ] filter
[ reverse ] zip-with [ all-equal? ] reject [ fast-set ] unique-by .
- Output:
{
{ "amaroid" "diorama" }
{ "degener" "reneged" }
{ "deifier" "reified" }
{ "deliver" "reviled" }
{ "dessert" "tressed" }
{ "desserts" "stressed" }
{ "deviler" "relived" }
{ "dioramas" "samaroid" }
{ "gateman" "nametag" }
{ "leveler" "relevel" }
{ "pat-pat" "tap-tap" }
{ "redrawer" "rewarder" }
{ "reknits" "stinker" }
{ "relever" "reveler" }
{ "reliver" "reviler" }
{ "revotes" "setover" }
{ "sallets" "stellas" }
}
!
! Anadromes
! tested with Intel ifx (IFX) 2025.2.1 20250806 on Kubuntu 25.04
! GNU Fortran (Ubuntu 14.2.0-19ubuntu2) 14.2.0 on Kubuntu 25.04
! VSI Fortran x86-64 V8.6-001 on OpenVMS x86_64 V9.2-3
! No Non-standard features used, should compile on any fairly recent Fortran.
! U.B., September 2025
program Anadromes
implicit none
integer, parameter :: longestWord = 50 ! is long enough for words.txt (45)
character(len=longestWord) :: word ! 1 word to read
character(len=longestWord), allocatable :: Words(:) ! the entire collection of all words
integer :: capacity, content ! usage of above container
integer :: l, io_stat, ii
content = 0
! Begin with moderate words array size
capacity = 1024
call resize (Words, capacity)
! going to read all words from unixdict.txt
open(unit=10, file='words.txt', status='old', action='read', iostat=io_stat)
if (io_stat .ne. 0) then
print *, "Error opening file" ! ERROR: would be useless to try reading words.
stop
end if
do ! read all words, until ERROR or EOF
read (10,'(A)', iostat=io_stat) word
if (io_stat < 0) exit ! EOF: Normal end of this loop
if (io_stat > 0) then
print *, "Read error" ! ERROR (unexpected): would be useless to proceed
exit
end if
! Consider only words longer than 6 characters, skip shorter words.
l = len_trim (word) ! use this instead of Q format
if (l .gt. 6) call addWord ()
end do
! For correct and efficient binary search, the word list must be sorted
call quicksort_strings (words, 1, content)
do ii=1,content
call checkWord (ii) ! Prints a pair if an anadrome is found
end do
close(10)
contains
! ------------------------------------
! add a new word to the list of words.
! ------------------------------------
subroutine addWord()
! No local variables here. Just working on Main program's variables.
content = content+1
! If we would exceed capacity of array 'Words', we need to increment capacity
if (content .gt. capacity) then
capacity = 2*capacity
call resize (Words, capacity)
end if
Words(content) = word
! for case insensitivity, convert uppercase letters to lowercase
! do ip=1,l
! if (word(ip:ip) .ge. 'A' .and. word(ip:ip) .le. 'Z') &
! Words(content)(ip:ip) = char (ichar( Words(content)(ip:ip) ) - ichar('A') + ichar('a'))
! enddo
end subroutine addWord
! ----------------------------------------------------------------
! See if a word is also in word list when its letters are reversed
! ----------------------------------------------------------------
subroutine checkWord (iw)
integer, intent(in) :: iw
character (len=longestWord) :: revWord
integer :: ip
revWord = reversed (iw)
ip = find (revword(:l) )
if (ip .ne. 0 .and. words(iw) .lt. revword) &
print '(A9, " <--> ", A)' , & ! right-justified <-> left justified
words(iw)(:len_trim(words(iw))) ,revWord(:len_trim(words(iw)))
end subroutine checkWord
! -----------------------------------------------
! construct word in reversed order of its letters
! -----------------------------------------------
function reversed (iw) result (r)
integer, intent(in) :: iw
character (len=longestWord) :: r
integer:: l, ii
l = len_trim (words(iw))
r = ' '
do ii = l, 1, -1
r (1-ii+l:1-ii+l) = words(iw)(ii:ii)
enddo
end function reversed
! ----------------------------------------------
! Increase allocated size of string array 'var'
! ----------------------------------------------
subroutine resize(var, newSize)
character (len=longestWord), allocatable, intent(inout) :: var(:) ! The array to be increased
integer, intent(in) :: newSize ! The new size of var
character (len=longestWord), allocatable :: tmp(:) ! Temporary storage
integer :: oldSize ! Current array size
integer :: ii ! Loop index
! Copy allocated values to temporary, then allocate var with
! new size and copy back saved values.
! Could be done with move_alloc but this would not be portable to OpenVMS Fortran
if (allocated(var)) then
oldSize = size(var)
else
oldSize = 0
end if
if (newSize .gt. oldSize) then ! only increment
if (oldSize .gt.0) then
allocate(tmp (oldSize))
tmp(:oldSize) = var(:oldSize)
deallocate (var)
end if
allocate(var(newSize))
if (allocated(tmp) .and. allocated (var) ) then
oldSize = min(size(tmp, 1), size(var, 1))
var(:oldSize) = tmp(:oldSize)
deallocate (tmp)
end if
do ii=oldSize+1, newSize
var (ii)(1:) = ' '
end do
end if
end subroutine resize
!------------------------------------------------------------
! Find a word in a list of possible keywords.
! Returns index of the word in that array, or 0 if not found.
! Binary search, array must be sorted.
!------------------------------------------------------------
function find (needle) result(index)
character (len=longestWord) , intent(in) :: needle
integer :: Index
integer :: left, right, mid, wordlen
integer :: comparison
left = 1
right = content
index = 0 ! will signal 'not found'
wordlen = len_trim(needle)
mid = left + (left-right)/2
do while (left <= right)
mid = left + (right - left) / 2
if (Words (mid) .eq. needle ) then
index = mid
return
else if (Words (mid) .lt. needle) then
! index of needle is between mid+1 and right.
left = mid + 1
else
! index of needle is between left and mid-1
right = mid - 1
end if
end do
end function find
recursive subroutine quicksort_strings(arr, low, high)
character(len=*), dimension(:), intent(inout) :: arr
integer, intent(in) :: low, high
integer :: pivot_index
integer :: i, j, mid
character(len=len(arr(1))) :: pivot, temp
if (low < high) then
! The word list is already "almost sorted", so use midlle word as pivot.
mid = low + (high-low) / 2
pivot = arr(mid)
!Move pivot to the end
temp = arr(mid)
arr(mid) = arr(high)
arr(high)=temp
i = low - 1
do j = low, high - 1
if (arr(j) <= pivot) then
i = i + 1
temp = arr(i)
arr(i) = arr(j)
arr(j) = temp
end if
end do
temp = arr(i+1)
arr(i+1) = arr(high)
arr(high) = temp
pivot_index = i + 1
call quicksort_strings(arr, low, pivot_index - 1)
call quicksort_strings(arr, pivot_index + 1, high)
end if
end subroutine quicksort_strings
end program Anadromes
- Output:
amaroid <--> diorama degener <--> reneged deifier <--> reified deliver <--> reviled dessert <--> tressed desserts <--> stressed deviler <--> relived dioramas <--> samaroid gateman <--> nametag leveler <--> relevel pat-pat <--> tap-tap redrawer <--> rewarder reknits <--> stinker relever <--> reveler reliver <--> reviler revotes <--> setover sallets <--> stellas real 0m0.372s user 0m0.345s sys 0m0.027s
Now with binary search, super fast
ReDim Shared As String words(500000)
Dim Shared As Integer wordCnt
Sub readWords(filename As String)
Dim As Integer ff = Freefile()
Dim As String word
If Open(filename For Input As #ff) <> 0 Then
Print "Cannot open file " & filename & "."
Exit Sub
End If
While Not Eof(ff)
Line Input #ff, word
If Len(word) > 6 Then
words(wordCnt) = word
wordCnt += 1
End If
Wend
Close #ff
wordCnt -= 1
ReDim Preserve words(wordCnt)
End Sub
Sub quicksort(qs() As string, l As integer, r As Integer)
Dim As integer size = r - l + 1
If size < 2 Then Exit Sub
Dim As Integer i = l, j = r
Dim As String pivot = qs(l + size \ 2)
Do
While qs(i) < pivot
i += 1
Wend
While pivot < qs(j)
j -= 1
Wend
If i <= j Then
Swap qs(i), qs(j)
i += 1
j -= 1
End If
Loop Until i > j
If l < j Then quicksort(qs(), l, j)
If i < r Then quicksort(qs(), i, r)
End Sub
Function reverseWord(s As String) As String
Dim As String reversed = s
Dim As Integer i, l = Len(reversed) - 1
For i = 0 To l
reversed[i] = s[l - i]
Next
Return reversed
End Function
' Main program
Dim As Integer i, c, hi, lo, md
Dim As String word, reversed
Dim As boolean fl
readWords("i:\words.txt")
' binary search needs a sorted list
quicksort(words(), LBound(words), UBound(words))
For i = 0 To wordCnt - 1
word = words(i)
reversed = reverseWord(word)
' binary search
lo = i + 1 : hi = wordCnt : md = (lo + hi) \ 2 : fl = FALSE
If words(lo) = reversed Then fl = TRUE
If words(hi) = reversed Then fl = TRUE
While (lo + 1 < hi) And fl = FALSE
If words(md) = reversed Then fl = TRUE : Exit While
If words(md) < reversed Then lo = md Else hi = md
md = (lo + hi) \ 2
Wend
If fl = TRUE Then
c += 1
Print Using "\ \ <=> \ \"; word; reversed
End If
Next i
Print
Print "Found"; c; " anadromes"
Sleep
- Output:
amaroid <=> diorama degener <=> reneged deifier <=> reified deliver <=> reviled dessert <=> tressed desserts <=> stressed deviler <=> relived dioramas <=> samaroid gateman <=> nametag leveler <=> relevel pat-pat <=> tap-tap redrawer <=> rewarder reknits <=> stinker relever <=> reveler reliver <=> reviler revotes <=> setover sallets <=> stellas Found 17 anadromes
local fn loadDict as CFArrayRef
CFURLRef url = fn URLWithString( @"https://raw.githubusercontent.com/thundergnat/rc-run/master/rc/resources/words.txt" )
CFStringRef string = lcase( fn StringWithContentsOfURL( url, NSASCIIStringEncoding, NULL ) )
CFArrayRef array = fn StringComponentsSeparatedByCharactersInSet( string, fn CharacterSetNewlineSet )
array = fn arraySortedarrayUsingSelector( array, @"caseInsensitiveCompare:" )
end fn = array
void local fn anadromes( array as CFArrayRef )
uint32 r, i, top, bot, med, count = 0
CFStringRef string
print
for r = 0 to len(array)-1
if len( array[r] ) < 7 then continue
string = @""
for i = 0 to len(array[r]) - 1
string = fn StringByAppendingString( mid( array[r],i,1 ), string )
next
if fn StringIsEqual( array[r], string ) then continue
bot = r+1 : top = len( array )-1
while ( top - bot ) > 1
med = ( bot + top )>>1
select fn StringCompare( string, array[med] )
case NSOrderedAscending : top = med
case NSOrderedDescending : bot = med
case else : count++
printf @"%4llu %@ %10s", count, array[r], fn StringUTF8String( string )
break
end select
wend
next
end fn
window 1, @"Anadromes",(0,0,209,502)
CFArrayRef array : array = fn loadDict
CFTimeInterval t : t = fn CACurrentMediaTime
fn anadromes( array )
printf @"\t\t%.3f secs",fn CACurrentMediaTime - t
handleevents- Output:
package main
import (
"bufio"
"cmp"
"fmt"
"log"
"os"
"slices"
"strings"
"unicode/utf8"
)
func main() {
words := readUniqueWords("words.txt", 6)
anadromes := getAnadromes(words)
lefts := sortedKeys(anadromes)
for _, left := range lefts {
right := anadromes[left]
fmt.Printf("%9s ↔ %s\n", left, right)
}
}
func readUniqueWords(filename string, minLen int) map[string]struct{} {
file, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
words := map[string]struct{}{}
for scanner.Scan() {
word := scanner.Text()
if utf8.RuneCountInString(word) > minLen {
word = strings.ToLower(word)
words[word] = struct{}{}
}
}
return words
}
func getAnadromes(words map[string]struct{}) map[string]string {
seen := map[string]struct{}{}
anadromes := map[string]string{}
for _, word := range sortedKeys(words) {
rword := reverse(word)
if rword != word {
if _, found := words[rword]; found {
if _, found := seen[word]; !found {
if _, found := seen[rword]; !found {
anadromes[word] = rword
seen[word] = struct{}{}
seen[rword] = struct{}{}
}
}
}
}
}
return anadromes
}
func sortedKeys[K cmp.Ordered, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for key := range m {
keys = append(keys, key)
}
slices.Sort(keys)
return keys
}
func reverse(text string) string {
runes := []rune(text)
slices.Reverse(runes)
return string(runes)
}
- Output:
amaroid ↔ diorama anacara ↔ aracana annabal ↔ labanna artamus ↔ sumatra colbert ↔ trebloc degener ↔ reneged deifier ↔ reified delbert ↔ trebled delevan ↔ naveled deliver ↔ reviled dessert ↔ tressed desserts ↔ stressed deviler ↔ relived dioramas ↔ samaroid eimmart ↔ trammie emmeram ↔ maremme gateman ↔ nametag latimer ↔ remital lattimer ↔ remittal lessees ↔ seessel leveler ↔ relevel nicolaus ↔ sualocin pat-pat ↔ tap-tap redrawer ↔ rewarder reknits ↔ stinker relever ↔ reveler reliver ↔ reviler revotes ↔ setover rotanev ↔ venator roygbiv ↔ vibgyor sallets ↔ stellas sennits ↔ stinnes
import qualified Data.Set as S
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
-- The anadromes of a list of words. We convert all words to lower case.
anadromes :: [T.Text] -> [(T.Text, T.Text)]
anadromes ws = let set = S.fromList $ map T.toLower ws
in S.foldr (step set) [] set
where step set w ps = let rev = T.reverse w
in if w < rev && S.member rev set
then (w, rev) : ps
else ps
main :: IO ()
main = TIO.interact (T.unlines . map anaShow . anadromes . longEnough . T.lines)
where longEnough = filter ((> 6) . T.length)
anaShow (x, y) = T.unwords [x, y]
- Output:
amaroid diorama anacara aracana annabal labanna artamus sumatra colbert trebloc degener reneged deifier reified delbert trebled delevan naveled deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid eimmart trammie emmeram maremme gateman nametag latimer remital lattimer remittal lessees seessel leveler relevel nicolaus sualocin pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover rotanev venator roygbiv vibgyor sallets stellas sennits stinnes
The following works in both languages:
procedure main()
words := list()
word_tab := table()
f := open("words.txt") | stop("Cannot open words.txt")
every put(words, (*(w := !f) > 6, w)) do word_tab[reverse(w)] := w
every (w2 := \word_tab[w1 := !words],w1 ~== w2) do {
write(right(w1,20)," -> ",w2)
every delete(word_tab,w1|w2)
}
end
Output:
amaroid -> diorama
degener -> reneged
deifier -> reified
deliver -> reviled
dessert -> tressed
desserts -> stressed
deviler -> relived
dioramas -> samaroid
gateman -> nametag
leveler -> relevel
pat-pat -> tap-tap
redrawer -> rewarder
reknits -> stinker
relever -> reveler
reliver -> reviler
revotes -> setover
sallets -> stellas
Inspecting other entries here, it seems clear that we cannot ignore case. Otherwise, 'trebled' would be an anadrome.
Anyways, the basic approach here is to identify a canonical key for each word, look for paired keys and organize the words based on those keys:
words=: cutLF fread 'words.txt'
canon=: {.@/:~@(,:|.) each words
akeys=: (~. #~ 2 = #/.~) canon
tkeys=: (#~ 6 < #@>) akeys
order=: /: canon
pairs=: _2]\ (order{canon e. tkeys) # order { words
This gives us:
pairs
┌────────┬────────┐
│amaroid │diorama │
├────────┼────────┤
│degener │reneged │
├────────┼────────┤
│deifier │reified │
├────────┼────────┤
│deliver │reviled │
├────────┼────────┤
│dessert │tressed │
├────────┼────────┤
│desserts│stressed│
├────────┼────────┤
│deviler │relived │
├────────┼────────┤
│dioramas│samaroid│
├────────┼────────┤
│gateman │nametag │
├────────┼────────┤
│leveler │relevel │
├────────┼────────┤
│pat-pat │tap-tap │
├────────┼────────┤
│redrawer│rewarder│
├────────┼────────┤
│reknits │stinker │
├────────┼────────┤
│relever │reveler │
├────────┼────────┤
│reliver │reviler │
├────────┼────────┤
│revotes │setover │
├────────┼────────┤
│sallets │stellas │
└────────┴────────┘
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
public class Anadromes {
public static void main(String[] args) throws IOException {
List<String> words = Files.lines(Path.of("words.txt")).filter( word -> word.length() > 6 ).sorted().toList();
System.out.println("The anadrome pairs with more than 6 letters are:");
for ( String word : words ) {
String wordReversed = new StringBuilder(word).reverse().toString();
if ( wordReversed.compareTo(word) > 0 && Collections.binarySearch(words, wordReversed) > 0 ) {
System.out.println(word + " <--> " + wordReversed);
}
}
}
}
- Output:
The anadrome pairs with more than 6 letters are: amaroid <--> diorama degener <--> reneged deifier <--> reified deliver <--> reviled dessert <--> tressed desserts <--> stressed deviler <--> relived dioramas <--> samaroid gateman <--> nametag leveler <--> relevel pat-pat <--> tap-tap redrawer <--> rewarder reknits <--> stinker relever <--> reveler reliver <--> reviler revotes <--> setover sallets <--> stellas
const fs = require('fs');
fs.readFile("words.txt", "utf8", (err, data) => {
if (err) {
console.error("Error reading the file:", err);
return;
}
const words = data.split(/\r?\n/); // This regex splits on both Unix and Windows line endings
const wordSet = new Set(words);
const seenWords = new Set();
const wordList = [];
for (let word of wordSet) {
if (word.length > 6) {
const reversedWord = word.split('').reverse().join('');
const wordPair = `${word}:${reversedWord}`;
const reversedWordPair = `${reversedWord}:${word}`;
if (wordSet.has(reversedWord) && reversedWord !== word && !seenWords.has(reversedWordPair)) {
wordList.push([word, reversedWord]);
seenWords.add(wordPair); // Store seen words as a string pair to make it easy to check
}
}
}
console.log(wordList.length);
wordList.sort((a, b) => a[0].localeCompare(b[0])).forEach(([word, reversedWord]) => {
console.log(`${word.padStart(9)} ${reversedWord}`);
});
});
- Output:
17 amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
Constructing a very large dictionary (i.e. JSON object) would be time-consuming and would require quite a lot of memory, so this implementation takes advantage of jq's `bsearch` for binary search. It also confines the search to "words" that begin with a lower-case letter. The result is the same listing of 17 words as found elsewhere on this page.
< words.txt jq -nrR '
def rev: explode | reverse | implode;
([inputs
| select(length > 6 and
(.[:1] | . >= "a" and
. <= "z" and
. <= .[-1:] )) ] | sort) as $words
| $words[]
| rev as $reverse
| select(. < $reverse and ($words | bsearch($reverse) > -1))
| . + " <=> " + $reverse
'
function anadromes(minsize, csense = true, fname = "words.txt")
words = Set(filter(w -> length(w) >= minsize, split((csense ? identity : lowercase)(read(fname, String)), r"\s+")))
found = [(w, reverse(w)) for w in words if (r = reverse(w)) in words && w < r]
println("Total $(length(found)) case $(csense ? "" : in)sensitive anadrome pairs found.")
foreach(a -> println(a[1], " <=> ", a[2]), sort!(found))
end
anadromes(7)
anadromes(7, false)
- Output:
Total 17 case sensitive anadrome pairs found. amaroid <=> diorama degener <=> reneged deifier <=> reified deliver <=> reviled dessert <=> tressed desserts <=> stressed deviler <=> relived dioramas <=> samaroid gateman <=> nametag leveler <=> relevel pat-pat <=> tap-tap redrawer <=> rewarder reknits <=> stinker relever <=> reveler reliver <=> reviler revotes <=> setover sallets <=> stellas Total 32 case insensitive anadrome pairs found. amaroid <=> diorama anacara <=> aracana annabal <=> labanna artamus <=> sumatra colbert <=> trebloc degener <=> reneged deifier <=> reified delbert <=> trebled delevan <=> naveled deliver <=> reviled dessert <=> tressed desserts <=> stressed deviler <=> relived dioramas <=> samaroid eimmart <=> trammie emmeram <=> maremme gateman <=> nametag latimer <=> remital lattimer <=> remittal lessees <=> seessel leveler <=> relevel nicolaus <=> sualocin pat-pat <=> tap-tap redrawer <=> rewarder reknits <=> stinker relever <=> reveler reliver <=> reviler revotes <=> setover rotanev <=> venator roygbiv <=> vibgyor sallets <=> stellas sennits <=> stinnes
word_list = {}
for line in io.lines("words.txt") do
if #line > 6 then word_list[line] = true end
end
for word in pairs(word_list) do
local rev = string.reverse(word)
if word > rev and word_list[rev] then
io.write(string.format("%-10s %s \n", word, rev))
end
end
- Output:
The program finds 17 anadromes:
relevel leveler setover revotes relived deviler stinker reknits reified deifier samaroid dioramas tap-tap pat-pat nametag gateman stressed desserts reneged degener diorama amaroid reviler reliver stellas sallets rewarder redrawer reviled deliver reveler relever tressed dessert
module Anadromes {
document a$
load.doc a$, "words.txt"
// ? doc.par(a$)=466549
list1=list
final1=list
m=Paragraph(a$, 0)
count=0
If Forward(a$,m) then
While m {
word=Paragraph$(a$,(m))
if len(word)>6 then
if exist(list1, word) else
append list1, word
end if
wordR=strrev$(word)
if wordR<>word then
if not exist(final1, wordR) then
if exist(list1, wordR) then
if not exist(final1, word) then
append final1, wordR:=word
count++
? count
refresh
end if
end if
end if
end if
end if
}
end if
sort final1
document doc$
k=each(final1)
while k
Print format$("{0:-12} <-> {1:12}", eval$(k!), eval$(k))
doc$=format$("{0:-12} <-> {1:12}", eval$(k!), eval$(k))+{
}
end while
save.doc doc$, "out.txt"
}
Anadromes
- Output:
amaroid <-> diorama
degener <-> reneged
deifier <-> reified
deliver <-> reviled
dessert <-> tressed
desserts <-> stressed
deviler <-> relived
dioramas <-> samaroid
gateman <-> nametag
leveler <-> relevel
pat-pat <-> tap-tap
redrawer <-> rewarder
reknits <-> stinker
relever <-> reveler
reliver <-> reviler
revotes <-> setover
sallets <-> stellas
words = Select[ReadList["words.txt", Word], StringLength[#] > 6 &];
Map[{#, StringReverse[#]} &, Intersection[words, Map[StringReverse, words]]] //
Select[Order @@ # == 1 &] // TableForm
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
This implementation is for use with the Mini Micro version of MiniScript. The command-line version does not include a HTTP library. Modify the declaration of wordList object to use the file class instead of the http class.
getPairs = function(words)
pairs = []
for i in range(0, words.len - 2)
for j in range(i + 1, words.len - 1)
pairs.push([words[i], words[j]])
end for
end for
return pairs
end function
isReversed = function(word1, word2)
for i in range(0, word1.len - 1)
if word1[i] != word2[-(i+1)] then return false
end for
return true
end function
makeKey = function(word)
return word.split("").sort.join("")
end function
wordList = http.get("https://raw.githubusercontent.com/dwyl/english-words/master/words.txt").split(char(10))
wordSets = {}
for word in wordList
k = makeKey(word)
if not wordSets.hasIndex(k) then
wordSets[k] = [word]
else
wordSets[k].push(word)
end if
end for
anadromes = []
for wordSet in wordSets.values
if wordSet.len > 1 and wordSet[0].len > 6 then
pairs = getPairs(wordSet)
for pair in pairs
if isReversed(pair[0], pair[1]) then print pair
end for
end if
end for
- Output:
["amaroid", "diorama"] ["degener", "reneged"] ["deifier", "reified"] ["deliver", "reviled"] ["deviler", "relived"] ["dessert", "tressed"] ["desserts", "stressed"] ["dioramas", "samaroid"] ["gateman", "nametag"] ["leveler", "relevel"] ["relever", "reveler"] ["revotes", "setover"] ["pat-pat", "tap-tap"] ["redrawer", "rewarder"] ["reknits", "stinker"] ["reliver", "reviler"] ["sallets", "stellas"]
import std/[sets, strutils]
func reversed(s: string): string =
## Return the reverse of a string.
## Works only for ASCII strings.
result.setLen(s.len)
for i in 1..s.len:
result[i - 1] = s[^i]
var wordSet: OrderedSet[string]
for word in lines("words.txt"):
if word.len > 6:
wordSet.incl word
for word in wordSet:
let rev = reversed(word)
if rev > word and rev in wordSet:
echo word.alignLeft(12), rev
- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
class SelfNumbers {
function : Main(args : String[]) ~ Nil {
if(args->Size() = 1) {
input := System.IO.Filesystem.FileReader->ReadFile(args[0]);
word_list := input->Split("\n");
searchable_words := Collection.Set->New()<String>;
each(word in word_list) {
word := word->Trim();
if(word->Size() > 6) {
searchable_words->Insert(word);
};
};
count := 0; limit := 17;
words := searchable_words->GetKeys()<String>;
each(word in words) {
if(word <> Nil) {
reversed_word := word->Reverse();
if(reversed_word->Compare(word) > 0 & searchable_words->Has(reversed_word)) {
show_count := count + 1;
"{$show_count}: |{$word}|, |{$reversed_word}|"->PrintLine();
count += 1;
};
};
if(count = limit) {
break;
};
};
"---"->PrintLine();
};
}
}1: |amaroid|, |diorama| 2: |degener|, |reneged| 3: |deifier|, |reified| 4: |deliver|, |reviled| 5: |dessert|, |tressed| 6: |desserts|, |stressed| 7: |deviler|, |relived| 8: |dioramas|, |samaroid| 9: |gateman|, |nametag| 10: |leveler|, |relevel| 11: |pat-pat|, |tap-tap| 12: |redrawer|, |rewarder| 13: |reknits|, |stinker| 14: |relever|, |reveler| 15: |reliver|, |reviler| 16: |revotes|, |setover| 17: |sallets|, |stellas| ---
module StrSet = Set.Make(String)
let read_line_seq ch =
let rec repeat () =
match input_line ch with
| s -> Seq.Cons (s, repeat)
| exception End_of_file -> Nil
in repeat
let string_rev s =
let last = pred (String.length s) in
String.init (succ last) (fun i -> s.[last - i])
let get_anadromes set =
let aux s =
let r = string_rev s in
if s < r && StrSet.mem r set
then Some (s, r)
else None
in
Seq.filter_map aux (StrSet.to_seq set)
let () = read_line_seq stdin |> Seq.filter (fun s -> String.length s > 6)
|> Seq.map String.lowercase_ascii |> StrSet.of_seq |> get_anadromes
|> Seq.iter (fun (s, r) -> Printf.printf "%9s | %s\n" s r)
- Output:
amaroid | diorama anacara | aracana annabal | labanna artamus | sumatra colbert | trebloc degener | reneged deifier | reified delbert | trebled delevan | naveled deliver | reviled dessert | tressed desserts | stressed deviler | relived dioramas | samaroid eimmart | trammie emmeram | maremme gateman | nametag latimer | remital lattimer | remittal lessees | seessel leveler | relevel nicolaus | sualocin pat-pat | tap-tap redrawer | rewarder reknits | stinker relever | reveler reliver | reviler revotes | setover rotanev | venator roygbiv | vibgyor sallets | stellas sennits | stinnes
program anadromes;
// Capitalized first letter is used only for type names, like "String"
uses
classes, // for TStringList
strUtils; // for reverseString
var
words: TStringList;
line, reverseWord: String;
inFile: TextFile;
begin
words := TStringList.create;
//otherwise every new line would be immediately sorted.
words.sorted := false;
assign(inFile, 'words.txt');
reset(inFile);
while not eof(inFile) do
begin
readLn(inFile, line);
if length(line) > 6 then words.add(line);
end;
close(inFile);
//all lines are read, now sort
words.caseSensitive := true;
words.sorted := true;
for line in words do
begin
reverseWord := reverseString(line);
if (line > reverseWord) and (words.indexOf(reverseWord) > -1) then
writeLn(line: 10, ' <-> ', reversestring(line));
end;
words.free;
end.
- Output:
diorama <-> amaroid nametag <-> gateman reified <-> deifier relevel <-> leveler relived <-> deviler reneged <-> degener reveler <-> relever reviled <-> deliver reviler <-> reliver rewarder <-> redrawer samaroid <-> dioramas setover <-> revotes stellas <-> sallets stinker <-> reknits stressed <-> desserts tap-tap <-> pat-pat tressed <-> dessert //sort every new inserted line before real 0m0,422s user 0m0,391s sys 0m0,029s //sort after all lines are read after real 0m0,241s user 0m0,221s sys 0m0,020s
program AnadromesAVL;
{uses the much more efficient AVL tree instead of Tstringlist to store all the words
reducing runtime by a factor 10}
{$mode ObjFPC}{$H+}
uses
Classes, AvgLvlTree, SysUtils, strutils;
type
TMyData = class
public
Woord: string;
end;
var
Tree: TAvgLvlTree;
function CompareMyData(Data1, Data2: Pointer): Integer;
begin
if TMyData(Data1).Woord > TMyData(Data2).Woord then result := -1 else
if TMyData(Data1).Woord < TMyData(Data2).Woord then result := 1 else
result := 0;
end;
procedure AddToTree(Data: string);
var
MyData: TMyData;
begin
MyData := TMyData.Create;
MyData.Woord := Data;
Tree.Add(MyData);
end;
var
Node: TAvgLvlTreeNode;
Infile: TextFile;
Line: string;
AnadromesList: TStringList;
MyData1, MyData2: TMyData;
begin
AnadromesList := TStringList.Create;
AnadromesList.Sorted := True;
Tree := TAvgLvlTree.Create(@CompareMyData);
AssignFile(Infile, 'words.txt');
try
Reset(Infile);
while not EOF(Infile) do
begin
Readln(Infile, Line);
if Length(Line) > 6 then
AddToTree(Line);
end;
CloseFile(Infile);
except
on E: EInOutError do
Writeln('File handling error occurred. Details: ', E.Message);
end;
MyData2 := TMyData.Create;
for Node in Tree do
begin
MyData1 := TMyData(Node.Data);
Line := MyData1.Woord;
MyData2.Woord := ReverseString(MyData1.Woord);
if (Line <> MyData2.Woord) and (Tree.Find(MyData2) <> nil) then
if AnadromesList.IndexOf(MyData2.Woord) = -1 then
AnadromesList.Add(Line);
end;
for Line in AnadromesList do
Writeln(Line:10, ' <-> ', ReverseString(Line));
AnadromesList.Free;
Tree.Free;
MyData2.Free;
end.
- Output:
amaroid <-> diorama degener <-> reneged deifier <-> reified deliver <-> reviled dessert <-> tressed desserts <-> stressed deviler <-> relived dioramas <-> samaroid gateman <-> nametag leveler <-> relevel pat-pat <-> tap-tap redrawer <-> rewarder reknits <-> stinker relever <-> reveler reliver <-> reviler revotes <-> setover sallets <-> stellas
// Anadromes. Nigel Galloway: August 30th., 2022
begin
var n := ReadLines('words.txt').Where(n -> Length(n) > 6);
foreach var v in n.Intersect(n.Select(n -> ReverseString(n))) do if v < ReverseString(v) then writeln(v);
end.
- Output:
amaroid degener deifier deliver dessert desserts deviler dioramas gateman leveler pat-pat redrawer reknits relever reliver revotes sallets
use strict;
use warnings;
my(%W,%A);
for my $w ( grep { /[A-z\-]{7,}/ } split "\n", do { local( @ARGV, $/ ) = ( 'words.txt' ); <> } ) {
my $r = reverse $w;
if ($W{$r}) { $A{$r} = sprintf "%10s ↔ %s\n", $r, $w }
else { $W{$w} = $w }
}
print $A{$_} for sort keys %A;
- Output:
amaroid ↔ diorama degener ↔ reneged deifier ↔ reified deliver ↔ reviled dessert ↔ tressed desserts ↔ stressed deviler ↔ relived dioramas ↔ samaroid gateman ↔ nametag leveler ↔ relevel pat-pat ↔ tap-tap redrawer ↔ rewarder reknits ↔ stinker relever ↔ reveler reliver ↔ reviler revotes ↔ setover sallets ↔ stellas
with javascript_semantics integer m = iff(platform()=JS?5:7) sequence words = unique(unix_dict(m,`words.txt`)) function anadrome(string w) return w<reverse(w) and binary_search(reverse(w),words)>0 end function sequence r = sort(filter(words,anadrome)), s = columnize({r,apply(r,reverse)}), t = join(s,"\n",fmt:="%8s <=> %-8s") printf(1,"Found %d anadromes:\n%s\n",{length(r),t})
- Output:
On the desktop:
Found 17 anadromes: amaroid <=> diorama degener <=> reneged deifier <=> reified deliver <=> reviled dessert <=> tressed desserts <=> stressed deviler <=> relived dioramas <=> samaroid gateman <=> nametag leveler <=> relevel pat-pat <=> tap-tap redrawer <=> rewarder reknits <=> stinker relever <=> reveler reliver <=> reviler revotes <=> setover sallets <=> stellas
Under p2js, aka in a browser, from where it cannot realistically read disk files, unix_dict() ignores the filename parameter and uses the smaller unixdict.txt, as by now quite some time ago converted into a dirty great big Phix sequence constant and then transpiled into a JavaScript Array, for which we also drop the minimum length to 5, and get:
Found 5 anadromes: damon <=> nomad kramer <=> remark lager <=> regal leper <=> repel lever <=> revel
Note that the order of the elements when tables are traversed is indeterminate, so the order of the words may vary each time the program is run.
do -- find some anadromes - words that when reversed are another word
-- the anadromes must be at least 7 characters long
local words = {}
for word in io.lines( "words.txt" ) do
if # word > 6 then
words[ word ] = true
end
end
for w, _ in pairs( words ) do
local r = w:reverse()
if r > w then
if words[ r ] then
io.write( string.format( "%12s %s\n", w, r ) )
end
end
end
end
- Output:
degener reneged
leveler relevel
dioramas samaroid
deviler relived
dessert tressed
deifier reified
deliver reviled
sallets stellas
amaroid diorama
reliver reviler
reknits stinker
redrawer rewarder
revotes setover
desserts stressed
pat-pat tap-tap
relever reveler
gateman nametag
# anadrome.py by Xing216
with open("words.txt","r") as f:
words = f.read().splitlines()
word_set = set(words)
seen_words = []
word_list = []
for word in word_set:
if len(word) > 6:
reversed_word = word[::-1]
if reversed_word in word_set and reversed_word != word \
and (reversed_word,word) not in seen_words:
word_list.append((word, reversed_word))
seen_words.append((word,reversed_word))
for word,reversed_word in sorted(word_list, key=lambda x: x[0]):
print(f"{word:>9} {reversed_word}")
- Output:
amaroid diorama deifier reified desserts stressed dioramas samaroid leveler relevel nametag gateman redrawer rewarder reknits stinker relived deviler reneged degener reveler relever reviled deliver reviler reliver setover revotes stellas sallets tap-tap pat-pat tressed dessert
[ temp put
[ dup [] = iff
[ drop false ]
done
dup size 2 / split
behead temp share
2dup = iff
[ 2drop 2drop true ]
done
$< if swap
drop again ]
temp release ] is has$ ( [ $ --> b )
$ "rosetta/words.txt" sharefile drop
nest$
[] swap witheach
[ dup size 7 < iff
drop done
nested join ]
sort$
[ dup [] != while
behead
2dup reverse has$ iff
[ dup echo$ sp
reverse echo$ cr ]
again
drop again ]
drop- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
anagra <- function(x) {
flipfun <- function(x) {
x <- rev(x)
x <- paste(x, collapse = "")
return(x)
}
x <- strsplit(x, split = "")
x <- lapply(x, flipfun)
x <- unlist(x)
return(x)
}
sortfun <- function(x) {
x <- unlist(x)
x <- unname(x)
x <- sort(x)
x <- data.frame(orig = x[1], flip = x[2])
return(x)
}
anadrome <- read.delim("words.txt")[, 1]
anadrome <- anadrome[nchar(anadrome) > 6]
anadrome <- anadrome[anagra(anadrome) %in% anadrome]
anadrome <- data.frame(orig = anadrome, flip = anagra(anadrome))
anadrome <- anadrome[anadrome$orig != anadrome$flip, ]
anadrome <- lapply(1:nrow(anadrome), function(i) sortfun(anadrome[i, ]))
anadrome <- do.call(rbind, anadrome)
anadrome <- anadrome[duplicated(anadrome), ]
for (i in 1:nrow(anadrome)) {
cat(paste0(i, " ", anadrome[i, 1], " <-> ", anadrome[i, 2]), "\n")
}
- Output:
1 amaroid <-> diorama 2 gateman <-> nametag 3 deifier <-> reified 4 leveler <-> relevel 5 deviler <-> relived 6 degener <-> reneged 7 relever <-> reveler 8 deliver <-> reviled 9 reliver <-> reviler 10 redrawer <-> rewarder 11 dioramas <-> samaroid 12 revotes <-> setover 13 sallets <-> stellas 14 reknits <-> stinker 15 desserts <-> stressed 16 pat-pat <-> tap-tap 17 dessert <-> tressed
my @words = 'words.txt'.IO.slurp.words.grep: *.chars > 6;
my %words = @words.pairs.invert;
put join "\n", @words.map: { %words{$_}:delete and sprintf "%10s ↔ %s", $_, .flip if ($_ ne .flip) && %words{.flip} }
- Output:
amaroid ↔ diorama degener ↔ reneged deifier ↔ reified deliver ↔ reviled dessert ↔ tressed desserts ↔ stressed deviler ↔ relived dioramas ↔ samaroid gateman ↔ nametag leveler ↔ relevel pat-pat ↔ tap-tap redrawer ↔ rewarder reknits ↔ stinker relever ↔ reveler reliver ↔ reviler revotes ↔ setover sallets ↔ stellas
Rebol [
title: "Rosetta code: Anadromes"
file: %Anadromes.r3
url: https://rosettacode.org/wiki/Anadromes
]
;; download word list if not cached locally
unless exists? %words.txt [
write %words.txt
read https://raw.githubusercontent.com/dwyl/english-words/refs/heads/master/words.txt
]
words: read/lines %words.txt
count: length? words
;; pre-filter: keep only lowercase words longer than 6 chars
filtered: make map! count / 2
foreach word words [
if 6 < length? word [
word: lowercase word
filtered/:word: reverse copy word
]
]
print [count "words," length? filtered "> 6 characters."]
;; find anadromes: pairs where both word and its reverse exist in the filtered map
result: copy []
foreach [word reversed] filtered [
if all [
find/case filtered reversed ;; reverse also exists as a word
word < reversed ;; avoid recording each pair twice
][
repend result [word reversed]
]
]
print ["Found" (length? result) / 2 "anadromes:"]
foreach [word reversed] result [
printf [-17 " ↔ "] [word reversed]
]
- Output:
466550 words, 387535 > 6 characters.
Found 32 anadromes:
amaroid ↔ diorama
anacara ↔ aracana
annabal ↔ labanna
artamus ↔ sumatra
colbert ↔ trebloc
degener ↔ reneged
deifier ↔ reified
delbert ↔ trebled
delevan ↔ naveled
deliver ↔ reviled
dessert ↔ tressed
desserts ↔ stressed
deviler ↔ relived
dioramas ↔ samaroid
eimmart ↔ trammie
emmeram ↔ maremme
gateman ↔ nametag
latimer ↔ remital
lattimer ↔ remittal
lessees ↔ seessel
leveler ↔ relevel
nicolaus ↔ sualocin
pat-pat ↔ tap-tap
redrawer ↔ rewarder
reknits ↔ stinker
relever ↔ reveler
reliver ↔ reviler
revotes ↔ setover
roygbiv ↔ vibgyor
rotanev ↔ venator
sallets ↔ stellas
sennits ↔ stinnes
load "stdlib.ring"
see "work..." + nl
txt = read("words.txt")
txt = str2list(txt)
for n = 1 to len(txt)
txtStr = string(txt[n])
txtRev = reverse(txtStr)
ind = find(txt,txtRev)
if ind > 0 and len(txtRev) > 6
see txtStr + " " + txtRev + nl
ok
next
see "done..." + nl
func reverse(rev)
re = ""
revstr = string(rev)
for n = len(revstr) to 1 step -1
re = re + revstr[n]
re = string(re)
next
return re- Output:
amaroid diorama degener reneged deifier reified deliver reviled dessert tressed desserts stressed deviler relived dioramas samaroid gateman nametag leveler relevel pat-pat tap-tap redrawer rewarder reknits stinker relever reveler reliver reviler revotes setover sallets stellas
words = File.readlines("words.txt", chomp: true).reject{|word| word.size <= 6}
reversed_words = words.map(&:reverse)
reversables = (words & reversed_words).reject{|word| word == word.reverse}
res = reversables.uniq{|w| [w, w.reverse].sort}
res.each{|w| puts "#{w} - #{w.reverse}".center(20) }
- Output:
amaroid - diorama degener - reneged deifier - reified deliver - reviled dessert - tressed desserts - stressed deviler - relived dioramas - samaroid gateman - nametag leveler - relevel pat-pat - tap-tap redrawer - rewarder reknits - stinker relever - reveler reliver - reviler revotes - setover sallets - stellas
use std::collections::BTreeSet;
use std::fs::File;
use std::io::{self, BufRead};
fn load_dictionary(filename: &str, min_length: usize) -> std::io::Result<BTreeSet<String>> {
let file = File::open(filename)?;
let mut dict = BTreeSet::new();
for line in io::BufReader::new(file).lines() {
let word = line?;
if word.len() >= min_length {
dict.insert(word);
}
}
Ok(dict)
}
fn main() {
match load_dictionary("words.txt", 7) {
Ok(dictionary) => {
for word in &dictionary {
let rev = String::from_iter(word.chars().rev());
if rev > *word && dictionary.contains(&rev) {
println!("{:<8} <-> {}", word, rev);
}
}
}
Err(error) => eprintln!("{}", error),
}
}
- Output:
amaroid <-> diorama degener <-> reneged deifier <-> reified deliver <-> reviled dessert <-> tressed desserts <-> stressed deviler <-> relived dioramas <-> samaroid gateman <-> nametag leveler <-> relevel pat-pat <-> tap-tap redrawer <-> rewarder reknits <-> stinker relever <-> reveler reliver <-> reviler revotes <-> setover sallets <-> stellas
$ include "seed7_05.s7i";
const func boolean: binarySearch (in array string: haystack, in string: needle) is func
result
var boolean: result is FALSE;
local
var integer: low is 1;
var integer: high is -1;
var integer: middle is -1;
begin
high := length(haystack);
while result = FALSE and low <= high do
middle := low + (high - low) div 2;
if needle < haystack[middle] then
high := pred(middle);
elsif needle > haystack[middle] then
low := succ(middle);
else
result := TRUE;
end if;
end while;
end func;
const proc: main is func
local
var file: dictionary is STD_NULL;
var string: word is "";
var string: reversed is "";
var array string: words is (array string).value;
begin
dictionary := open("words.txt", "r");
while not eof(dictionary) do
readln(dictionary, word);
words &:= word;
end while;
close(dictionary);
words := sort(words);
for word range words do
reversed := reverse(word);
if length(word) > 6 and word < reversed and binarySearch(words, reversed) then
writeln(word <& " <-> " <& reversed);
end if;
end for;
end func;- Output:
amaroid <-> diorama degener <-> reneged deifier <-> reified deliver <-> reviled dessert <-> tressed desserts <-> stressed deviler <-> relived dioramas <-> samaroid gateman <-> nametag leveler <-> relevel pat-pat <-> tap-tap redrawer <-> rewarder reknits <-> stinker relever <-> reveler reliver <-> reviler revotes <-> setover sallets <-> stellas
program find_anadromes;
read_file;
anadromes := {
{word, reverse word}
: word in words
| reverse word in words
and reverse word /= word
};
loop for anadrome in anadromes do
print(anadrome);
end loop;
read_file::
words := {};
dictfile := open("words.txt", "r");
loop doing
geta(dictfile, word);
while word /= om do
if #word <= 6 then continue; end if;
words with:= word;
end loop;
close(dictfile);
end program;- Output:
{amaroid diorama}
{degener reneged}
{deifier reified}
{deliver reviled}
{dessert tressed}
{desserts stressed}
{deviler relived}
{dioramas samaroid}
{gateman nametag}
{leveler relevel}
{'pat-pat' 'tap-tap'}
{redrawer rewarder}
{reknits stinker}
{relever reveler}
{reliver reviler}
{revotes setover}
{sallets stellas}
var S = Hash()
File('words.txt').open_r.each {|word|
word.len > 6 || next
if (S.has(word.flip)) {
say "#{word.flip} <=> #{word}"
}
else {
S{word} = true
}
}
- Output:
amaroid <=> diorama gateman <=> nametag deifier <=> reified leveler <=> relevel deviler <=> relived degener <=> reneged relever <=> reveler deliver <=> reviled reliver <=> reviler redrawer <=> rewarder dioramas <=> samaroid revotes <=> setover sallets <=> stellas reknits <=> stinker desserts <=> stressed pat-pat <=> tap-tap dessert <=> tressed
A complete implementation of a partitioning approach with async processing.
Supporting class:
import Foundation
import Algorithms
extension String {
var firstAndLast: String { String(self.first!) + String(self.last!) }
var backwards: String { String(self.reversed()) }
}
class AnadromeIndex {
// Stucture to index by length and the first and last character
// [ length: [ firstAndLast: [ words ] ] ]
/// Only compare words of same length, and where
/// firstAndLast corresponds to its reverse
var dict: [Int: [String: [String]]] = [:]
func loadFile(
fileURL: URL,
minLen: Int,
byLine: Bool = false
) async -> (Int, Int) {
var linesRead = 0
var wordsIndexed = 0
do {
if byLine {
for try await line in fileURL.lines {
linesRead += 1
if line.count < minLen { continue }
wordsIndexed += 1
self.addWord( line
.trimmingCharacters(in: .whitespacesAndNewlines)
)
}
} else {
let fileString = try String(contentsOf: fileURL, encoding: .utf8)
for line in fileString.components(separatedBy: .newlines) {
linesRead += 1
if line.count < minLen { continue }
wordsIndexed += 1
self.addWord( line
.trimmingCharacters(in: .whitespacesAndNewlines)
)
}
}
} catch {
debugPrint(error)
}
return (linesRead, wordsIndexed)
}
func addWord(_ str: String) {
let len = str.count
let index = str.firstAndLast
if let azDict = dict[len] {
if azDict[index] != nil {
self.dict[len]![index]!.append(str)
} else {
self.dict[len]!.updateValue([str], forKey: index)
}
} else {
dict.updateValue([index: [str]], forKey: len)
}
}
func findAnadromes() async -> [String] {
var done: [String] = []
var results: [String] = []
let allResults = await withTaskGroup(
of: [String].self,
returning: [String].self) { group in
// By length
for len in self.dict.keys.sorted() {
if let lenSet = self.dict[len] {
// By firstAndLast characters
for az in lenSet.keys.sorted() {
let za = az.backwards
if done.contains(where: { $0 == "\(len)\(az)" || $0 == "\(len)\(za)" })
|| lenSet[za] == nil { continue }
done += ["\(len)\(az)", "\(len)\(za)"]
group.addTask {
let lh = lenSet[az]!
let rh = lenSet[za]!
let f = await self.searchProduct(lh: lh, rh: rh)
return f
}
}
}
}
for await result in group {
results += result
}
return results
}
return allResults
}
func searchProduct(lh: [String], rh: [String]) async -> [String] {
var found: [String] = []
for ( s1, s2 ) in product(lh, rh) {
if s1 == s2 { continue } // palindrome
if s1 == String(s2.reversed())
&& !found.contains(where: {$0 == s2 || $0 == s1}) {
found.append(s1)
}
}
return found
}
}
Usage:
import Foundation
import ArgumentParser
@main
struct AnadromeFinder: AsyncParsableCommand {
@Argument(help: "File containing words", transform: URL.init(fileURLWithPath:))
var file: URL
@Option(name: .shortAndLong)
var minLen: Int = 6
@Flag(name: .shortAndLong)
var verbose = false
mutating func run() async throws {
let indexSet = AnadromeIndex()
var start = Date()
let (lines, words) = await indexSet.loadFile(fileURL: file, minLen: minLen)
if verbose {
printStderr("\(lines.formatted()) lines read,",
"\(words.formatted()) hashed in",
"\(since(start).formatted()) sec")
}
start = Date()
let found = await indexSet.findAnadromes()
if verbose {
printStderr("\(found.count.formatted()) matches found in",
"\(since(start)) seconds")
}
// Print results
for e in found.sorted(by: {a, b in
if a.count == b.count { return a < b }
return a.count < b.count
}) {
print(e, e.backwards)
}
}
func since(_ dt: Date) -> TimeInterval {
return Date().timeIntervalSince(dt)
}
func printStderr(
_ items: Any...,
separator: String = " ",
terminator: String = "\n"
) {
let output = items
.map { String(describing: $0) }
.joined(separator: separator) + terminator
FileHandle.standardError.write(output.data(using: .utf8)!)
}
}
Output:
% ./Anadromes --verbose /Users/username/Downloads/words.txt
466,551 lines read, 427,054 hashed in 5.52172 sec
83 matches found in 7.691561937332153 seconds
abrood doorba
agenes senega
amunam manuma
animal lamina
animes semina
bruted deturb
darter retrad
decart traced
decurt truced
deflow wolfed
degami imaged
denier reined
denies seined
depots stoped
derats stared
dessus sussed
dewans snawed
dialer relaid
diaper repaid
dibrom morbid
dorter retrod
drawer reward
elides sedile
elutes setule
enserf fresne
ergate etagre
eviler relive
gangan nagnag
gawgaw wagwag
golfer reflog
hallan nallah
lemmas sammel
lesson nossel
liever reveil
looter retool
manitu utinam
mooder redoom
nedder redden
pupils slipup
rebuts stuber
recaps spacer
recart tracer
redips spider
reflow wolfer
reives sevier
reknit tinker
remeet teemer
repins sniper
report troper
repots stoper
retros sorter
scares seracs
secret terces
selahs shales
serves sevres
sinnet tennis
skeets steeks
sleeps speels
sleets steels
sloops spools
snoops spoons
spirts strips
sports strops
sprits stirps
struts sturts
territ tirret
amaroid diorama
degener reneged
deifier reified
deliver reviled
dessert tressed
deviler relived
gateman nametag
leveler relevel
pat-pat tap-tap
reknits stinker
relever reveler
reliver reviler
revotes setover
sallets stellas
desserts stressed
dioramas samaroid
redrawer rewarder
import "io" for File
import "./sort" for Sort, Find
import "./fmt" for Fmt
var wordList = "words.txt" // local copy
var words = File.read(wordList)
.trimEnd()
.split("\n")
.where { |word| word.count > 6 }
.toList
Sort.quick(words) // need strict lexicographical order to use binary search
var anadromes = []
for (word in words) {
var word2 = word[-1..0]
if (word != word2 && !anadromes.contains(word2) && Find.first(words, word2) >= 0) {
anadromes.add(word)
}
}
System.print("The anadrome pairs with more than 6 letters are:")
for (ana in anadromes) Fmt.print("$8s <-> $8s", ana, ana[-1..0])
- Output:
The anadrome pairs with more than 6 letters are: amaroid <-> diorama degener <-> reneged deifier <-> reified deliver <-> reviled dessert <-> tressed desserts <-> stressed deviler <-> relived dioramas <-> samaroid gateman <-> nametag leveler <-> relevel pat-pat <-> tap-tap redrawer <-> rewarder reknits <-> stinker relever <-> reveler reliver <-> reviler revotes <-> setover sallets <-> stellas
!ys-0
DICT =: '/usr/share/dict/words'
defn main(dict=DICT, limit=20):
words =:
split('able elba abut tuba agar raga avid diva bard drab', ' ')
word-set =: set(words)
loop words words, left limit:
when left > 0:
when-not words:empty?:
word =: words.0
rev =: word:reverse
when compare(word rev) < 0:
when in?(rev word-set):
say: "$word $rev"
left =: left.--
recur: words:rest, left
- Output:
$ ys anadromes.ys able elba abut tuba agar raga avid diva bard drab