import Data.Char

sentence = start ++ foldMap add (zip [2..] $ tail $ words sentence)
  where
    start = "Four is the number of letters in the first word of this sentence, "
    add (i, w) = unwords [spellInteger (alphaLength w), "in the", spellOrdinal i ++ ", "]

alphaLength w = fromIntegral $ length $ filter isAlpha w

main = mapM_ (putStrLn . say) [1000,10000,100000,1000000]
  where
    ws = words sentence
    say n =
      let (a, w:_) = splitAt (n-1) ws
      in "The " ++ spellOrdinal n ++ " word is \"" ++ w ++ "\" which has " ++
         spellInteger (alphaLength  w) ++ " letters. The sentence length is " ++
         show (length $ unwords a) ++ " chars."


import java.util.HashMap;
import java.util.Map;

public class FourIsTheNumberOfLetters {

    public static void main(String[] args) {
        String [] words = neverEndingSentence(201);
        System.out.printf("Display the first 201 numbers in the sequence:%n%3d: ", 1);
        for ( int i = 0 ; i < words.length ; i++ ) {
            System.out.printf("%2d ", numberOfLetters(words[i]));
            if ( (i+1) % 25 == 0 ) {
                System.out.printf("%n%3d: ", i+2);
            }
        }
        System.out.printf("%nTotal number of characters in the sentence is %d%n", characterCount(words));
        for ( int i = 3 ; i <= 7 ; i++ ) {
            int index = (int) Math.pow(10, i);
            words = neverEndingSentence(index);
            String last = words[words.length-1].replace(",", "");
            System.out.printf("Number of letters of the %s word is %d. The word is \"%s\".  The sentence length is %,d characters.%n", toOrdinal(index), numberOfLetters(last), last, characterCount(words));
        }
    }

    @SuppressWarnings("unused")
    private static void displaySentence(String[] words, int lineLength) {
        int currentLength = 0;
        for ( String word : words ) {
            if ( word.length() + currentLength > lineLength ) {
                String first = word.substring(0, lineLength-currentLength);
                String second = word.substring(lineLength-currentLength);
                System.out.println(first);
                System.out.print(second);
                currentLength = second.length();
            }
            else {
                System.out.print(word);
                currentLength += word.length();
            }
            if ( currentLength == lineLength ) {
                System.out.println();
                currentLength = 0;
            }
            System.out.print(" ");
            currentLength++;
            if ( currentLength == lineLength ) {
                System.out.println();
                currentLength = 0;
            }
        }
        System.out.println();
    }

    private static int numberOfLetters(String word) {
        return word.replace(",","").replace("-","").length();
    }

    private static long characterCount(String[] words) {
        int characterCount = 0;
        for ( int i = 0 ; i < words.length ; i++ ) {
            characterCount += words[i].length() + 1;
        }
        //  Extra space counted in last loop iteration
        characterCount--;
        return characterCount;
    }

    private static String[] startSentence = new String[] {"Four", "is", "the", "number", "of", "letters", "in", "the", "first", "word", "of", "this", "sentence,"};

    private static String[] neverEndingSentence(int wordCount) {
        String[] words = new String[wordCount];
        int index;
        for ( index = 0 ; index < startSentence.length && index < wordCount ; index++ ) {
            words[index] = startSentence[index];
        }
        int sentencePosition = 1;
        while ( index < wordCount ) {
            //  X in the Y
            //  X
            sentencePosition++;
            String word = words[sentencePosition-1];
            for ( String wordLoop : numToString(numberOfLetters(word)).split(" ") ) {
                words[index] = wordLoop;
                index++;
                if ( index == wordCount ) {
                    break;
                }
            }
            // in
            words[index] = "in";
            index++;
            if ( index == wordCount ) {
                break;
            }
            //  the
            words[index] = "the";
            index++;
            if ( index == wordCount ) {
                break;
            }
            //  Y
            for ( String wordLoop : (toOrdinal(sentencePosition) + ",").split(" ") ) {
                words[index] = wordLoop;
                index++;
                if ( index == wordCount ) {
                    break;
                }
            }
        }
        return words;
    }

    private static final String[] nums = new String[] {
            "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine",
            "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"
    };

    private static final String[] tens = new String[] {"zero", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"};

    private static final String numToString(long n) {
        return numToStringHelper(n);
    }

    private static final String numToStringHelper(long n) {
        if ( n < 0 ) {
            return "negative " + numToStringHelper(-n);
        }
        int index = (int) n;
        if ( n <= 19 ) {
            return nums[index];
        }
        if ( n <= 99 ) {
            return tens[index/10] + (n % 10 > 0 ? "-" + numToStringHelper(n % 10) : "");
        }
        String label = null;
        long factor = 0;
        if ( n <= 999 ) {
            label = "hundred";
            factor = 100;
        }
        else if ( n <= 999999) {
            label = "thousand";
            factor = 1000;
        }
        else if ( n <= 999999999) {
            label = "million";
            factor = 1000000;
        }
        else if ( n <= 999999999999L) {
            label = "billion";
            factor = 1000000000;
        }
        else if ( n <= 999999999999999L) {
            label = "trillion";
            factor = 1000000000000L;
        }
        else if ( n <= 999999999999999999L) {
            label = "quadrillion";
            factor = 1000000000000000L;
        }
        else {
            label = "quintillion";
            factor = 1000000000000000000L;
        }
        return numToStringHelper(n / factor) + " " + label + (n % factor > 0 ? " " + numToStringHelper(n % factor ) : "");
    }

    private static Map<String,String> ordinalMap = new HashMap<>();
    static {
        ordinalMap.put("one", "first");
        ordinalMap.put("two", "second");
        ordinalMap.put("three", "third");
        ordinalMap.put("five", "fifth");
        ordinalMap.put("eight", "eighth");
        ordinalMap.put("nine", "ninth");
        ordinalMap.put("twelve", "twelfth");
    }

    private static String toOrdinal(long n) {
        String spelling = numToString(n);
        String[] split = spelling.split(" ");
        String last = split[split.length - 1];
        String replace = "";
        if ( last.contains("-") ) {
            String[] lastSplit = last.split("-");
            String lastWithDash = lastSplit[1];
            String lastReplace = "";
            if ( ordinalMap.containsKey(lastWithDash) ) {
                lastReplace = ordinalMap.get(lastWithDash);
            }
            else if ( lastWithDash.endsWith("y") ) {
                lastReplace = lastWithDash.substring(0, lastWithDash.length() - 1) + "ieth";
            }
            else {
                lastReplace = lastWithDash + "th";
            }
            replace = lastSplit[0] + "-" + lastReplace;
        }
        else {
            if ( ordinalMap.containsKey(last) ) {
                replace = ordinalMap.get(last);
            }
            else if ( last.endsWith("y") ) {
                replace = last.substring(0, last.length() - 1) + "ieth";
            }
            else {
                replace = last + "th";
            }
        }
        split[split.length - 1] = replace;
        return String.join(" ", split);
    }

}


using DataStructures # for deque

const seed = "Four is the number of letters in the first word of this sentence, "
const (word2, word3) = ("in", "the")

lettercount(w) = length(w) -  length(collect(eachmatch(r"-", w)))
splits(txt) = [x.match for x in eachmatch(r"[\w\-]+", txt)]
todq(sentence) = (d = Deque{String}(); map(x->push!(d, x), splits(sentence)[2:end]); d)

struct CountLetters
    seedsentence::String
    words::Deque{String}
    commasafter::Vector{Int}
    CountLetters(s) = new(s, todq(s), [13])
    CountLetters() = CountLetters(seed)
end

function Base.iterate(iter::CountLetters, state = (1, 5, ""))
    if length(iter.words) < 1
        return nothing
    end
    returnword = popfirst!(iter.words)
    nextwordindex = state[1] + 1
    wordlen = lettercount(returnword)
    wordvec = vcat(num2text(wordlen), word2, word3, splits(numtext2ordinal(num2text(nextwordindex))))
    map(x -> push!(iter.words, x), wordvec)
    push!(iter.commasafter, length(iter.words))
    added = length(returnword) + (nextwordindex in iter.commasafter ? 2 : 1)
    (wordlen, (nextwordindex, state[2] + added, returnword))
end

Base.eltype(iter::CountLetters) = Int

function firstN(n = 201)
    countlet = CountLetters()
    print("It is interesting how identical lengths align with 20 columns.\n   1:   4")
    iter_result = iterate(countlet)
    itercount = 2
    while iter_result != nothing
        (wlen, state) = iter_result
        print(lpad(string(wlen), 4))
        if itercount % 20 == 0
            print("\n", lpad(itercount+1, 4), ":")
        elseif itercount >= n
            break
        end
        iter_result = iterate(countlet, state)
        itercount += 1
    end
    println()
end

function sumwords(iterations)
    countlet = CountLetters()
    iter_result = iterate(countlet)
    itercount = 2
    while iter_result != nothing
        (wlen, state) = iter_result
        if itercount == iterations
            return state
        end
        iter_result = iterate(countlet, state)
        itercount += 1
    end
    throw("Iteration failed on \"Four is the number\" task.")
end

firstN()

for n in [2202, 1000, 10000, 100000, 1000000, 10000000]
    (itercount, totalletters, lastword) = sumwords(n)
    println("$n words -> $itercount iterations, $totalletters letters total, ",
            "last word \"$lastword\" with $(length(lastword)) letters.")
end


// version 1.1.4-3

val names = mapOf(
    1 to "one",
    2 to "two",
    3 to "three",
    4 to "four",
    5 to "five",
    6 to "six",
    7 to "seven",
    8 to "eight",
    9 to "nine",
    10 to "ten",
    11 to "eleven",
    12 to "twelve",
    13 to "thirteen",
    14 to "fourteen",
    15 to "fifteen",
    16 to "sixteen",
    17 to "seventeen",
    18 to "eighteen",
    19 to "nineteen",
    20 to "twenty",
    30 to "thirty",
    40 to "forty",
    50 to "fifty",
    60 to "sixty",
    70 to "seventy",
    80 to "eighty",
    90 to "ninety"
)

val bigNames = mapOf(
    1_000L to "thousand",
    1_000_000L to "million",
    1_000_000_000L to "billion",
    1_000_000_000_000L to "trillion",
    1_000_000_000_000_000L to "quadrillion",
    1_000_000_000_000_000_000L to "quintillion"
)

val irregOrdinals = mapOf(
    "one" to "first",
    "two" to "second",
    "three" to "third",
    "five" to "fifth",
    "eight" to "eighth",
    "nine" to "ninth",
    "twelve" to "twelfth"
)

fun String.toOrdinal(): String {
    if (this == "zero") return "zeroth"  // or alternatively 'zeroeth'
    val splits = this.split(' ', '-')
    val last = splits[splits.lastIndex]
    return if (irregOrdinals.containsKey(last)) this.dropLast(last.length) + irregOrdinals[last]!!
           else if (last.endsWith("y")) this.dropLast(1) + "ieth"
           else this + "th"
}

fun numToText(n: Long, uk: Boolean = false): String {
    if (n == 0L) return "zero"
    val neg = n < 0L
    val maxNeg = n == Long.MIN_VALUE
    var nn = if (maxNeg) -(n + 1) else if (neg) -n else n
    val digits3 = IntArray(7)
    for (i in 0..6) {  // split number into groups of 3 digits from the right
        digits3[i] = (nn % 1000).toInt()
        nn /= 1000
    }

    fun threeDigitsToText(number: Int) : String {
        val sb = StringBuilder()
        if (number == 0) return ""
        val hundreds = number / 100
        val remainder = number % 100
        if (hundreds > 0) {
            sb.append(names[hundreds], " hundred")
            if (remainder > 0) sb.append(if (uk) " and " else " ")
        }
        if (remainder > 0) {
            val tens = remainder / 10
            val units = remainder % 10
            if (tens > 1) {
                sb.append(names[tens * 10])
                if (units > 0) sb.append("-", names[units])
            }
            else sb.append(names[remainder])
        }
        return sb.toString()
    }

    val strings = Array(7) { threeDigitsToText(digits3[it]) }
    var text = strings[0]
    var andNeeded = uk && digits3[0] in 1..99
    var big = 1000L
    for (i in 1..6) {
        if (digits3[i] > 0) {
            var text2 = strings[i] + " " + bigNames[big]
            if (text.isNotEmpty()) {
                text2 += if (andNeeded) " and " else " "  // no commas inserted in this version
                andNeeded = false
            }
            else andNeeded = uk && digits3[i] in 1..99
            text = text2 + text
        }
        big *= 1000
    }
    if (maxNeg) text = text.dropLast(5) + "eight"
    if (neg) text = "minus " + text
    return text
}

val opening = "Four is the number of letters in the first word of this sentence,".split(' ')

val String.adjustedLength get() = this.replace(",", "").replace("-", "").length  // no ',' or '-'

fun getWords(n: Int): List<String> {
    val words = mutableListOf<String>()
    words.addAll(opening)
    if (n > opening.size) {
        var k = 2
        while (true) {
            val len = words[k - 1].adjustedLength
            val text = numToText(len.toLong())
            val splits = text.split(' ')
            words.addAll(splits)
            words.add("in")
            words.add("the")
            val text2 = numToText(k.toLong()).toOrdinal() + ","  // add trailing comma
            val splits2 = text2.split(' ')
            words.addAll(splits2)
            if (words.size >= n) break
            k++
        }
    }
    return words
}

fun getLengths(n: Int): Pair<List<Int>, Int> {
    val words = getWords(n)
    val lengths = words.take(n).map { it.adjustedLength }
    val sentenceLength = words.sumBy { it.length } + words.size - 1  // includes hyphens, commas & spaces
    return Pair(lengths, sentenceLength)
}

fun getLastWord(n: Int): Triple<String, Int, Int> {
    val words = getWords(n)
    val nthWord = words[n - 1]
    val nthWordLength = nthWord.adjustedLength
    val sentenceLength = words.sumBy { it.length } + words.size - 1  // includes hyphens, commas & spaces
    return Triple(nthWord, nthWordLength, sentenceLength)
}

fun main(args: Array<String>) {
    var n = 201
    println("The lengths of the first $n words are:\n")
    val (list, sentenceLength) = getLengths(n)
    for (i in 0 until n) {
        if (i % 25 == 0) {
            if (i > 0) println()
            print("${"%3d".format(i + 1)}: ")
        }
        print("%3d".format(list[i]))
    }
    println("\n\nLength of sentence = $sentenceLength\n")
    n = 1_000
    do {
        var (word, wLen, sLen) = getLastWord(n)
        if (word.endsWith(",")) word = word.dropLast(1)  // strip off any trailing comma
        println("The length of word $n [$word] is $wLen")
        println("Length of sentence = $sLen\n")
        n *= 10
    }
    while (n <= 10_000_000)
}


(*==Number names==*)

(*Mathematica has a built-in function for getting the name of an integer. It's a semantically rich function (dealing with many languages and grammatical variants), and consequently it would slow down our algorithm significantly. So, I've used the built-in function to seed/memoize special-purpose functions. Furthermore, the problem is suited to using a representation of the sentence that is an array of strings rather than a single monolithic string, and so these integer name functions will return arrays of strings.*)

(*We'll define the function for small integers to use the built-in function and to trigger memoization on the first invocation. After that, the basic strategy is to chunk up an integer into groups of three digits, apply the name to what those digits usually represent and then add the 'scaling' term (e.g. 'thousand', 'million'). Just for laziness, I'll skip trying to handle the 'thousand and zero' case and just fall back to the built-in function--it shouldn't be called often enough to matter. Since this problem won't need number names exceeding the 'million' scale, I won't optimize beyond that.*)
IntNameWords[n_]:=(IntNameWords[n]=StringSplit[IntegerName[n,"Words"]])/;n<1000;
IntNameWords[n_]:=StringSplit[IntegerName[n,"Words"]]/;Divisible[n,1000];
IntNameWords[n_]:=Flatten[Riffle[IntNameWords/@QuotientRemainder[n,1000],"thousand"]]/;n<1000000;
IntNameWords[n_]:=Flatten[Riffle[IntNameWords/@QuotientRemainder[n,1000000],"million"]]/;n<1000000000;
IntNameWords[n_]:=StringSplit[IntegerName[n,"Words"]];
(*I'm using Scan to trigger the memoization.*)
Scan[IntNameWords,Range[999]];

(*The strategy is similar for ordinals. Note that I'm tacking on a comma to the ordinals. This makes this function quite specialized to this specific problem.*)
OrdNameWords[n_]:=(OrdNameWords[n]=StringSplit[IntegerName[n,"Ordinal"]<>","])/;n<1000;
OrdNameWords[n_]:=StringSplit[IntegerName[n,"Ordinal"]<>","]/;Divisible[n,1000];
OrdNameWords[n_]:=Flatten[Riffle[[email protected]@@Thread[{{IntNameWords,OrdNameWords},QuotientRemainder[n,1000]}],"thousand"]]/;n<1000000;
OrdNameWords[n_]:=Flatten[Riffle[[email protected]@@Thread[{{IntNameWords,OrdNameWords},QuotientRemainder[n,1000000]}],"million"]]/;n<1000000000;
OrdNameWords[n_]:=StringSplit[IntegerName[n,"Ordinal"]<>","];
(*Triggering memoization again.*)
Scan[OrdNameWords,Range[999]];

(*==Helper/driver functions==*)

(*This could be generalized, but for this problem, the '-' and ',' are the only non-letter characters we need to worry about.*)
LetterCount[str_]:=StringLength[StringDelete[str,"-"|","]];

(*The seed/initial part of the sentence.*)
SentenceHeadWords=StringSplit["Four is the number of letters in the first word of this sentence,"];

(*Output formatters*)
DisplayWordLengthSequence[wordSeq_]:=StringRiffle[{"First "<>StringRiffle[IntNameWords[[email protected]]]<>" numbers in sequence:",LetterCount/@wordSeq},"\n"];
DisplayCharacterCount[wordSeq_]:=StringRiffle[{"String length of sentence with "<>StringRiffle[IntNameWords[[email protected]]]<>" words:",SentenceCharacterCount[wordSeq]}];
DisplayWordInfo[wordSeq_,wordIdx_]:=StringForm["The `` word is '``' consisting of `` letters.",StringRiffle[OrdNameWords[[email protected]]],wordSeq[[wordIdx]],StringRiffle[IntNameWords[[email protected][[wordIdx]]]]];

(*There is a space between each 'word', so we can just add 1 less than the number of 'words' to get total characters in the full string representation of the sentence (if we were to create it). I could also subract another 1 for the trailing comma, but the requirements weren't precise in this regard.*)
SentenceCharacterCount[chunks:{__String}]:=Total[StringLength[chunks]]+Length[chunks]-1;

(*==Implementation A==*)

(*A simple functional implementation that continues to extend the 'sentence' one fragment at a time until the number of words exceeds the requested number. This implementation takes several seconds to complete the 100,000 word case.*)
ExtendCharChunks[{0,0,{}}]={1,Length[SentenceHeadWords],SentenceHeadWords};
ExtendCharChunks[{fragCt_,wordCt_,chunks_}]:=
With[
{nextFrag=Flatten[{IntNameWords[LetterCount[chunks[[1+fragCt]]]],"in","the",OrdNameWords[1+fragCt]}]},
{1+fragCt,wordCt+Length[nextFrag],Flatten[{chunks,nextFrag}]}
];
SentenceChunksFun[chunkCt_]:=Take[Last[NestWhile[ExtendCharChunks,ExtendCharChunks[{0,0,{}}],#[[2]]<chunkCt&]],chunkCt];

(*==Implementation B==*)

(*This implementation uses a pre-allocated array, an iterative strategy, and inlining of the fragment construction. It performs much better than the previous implementation but still takes about 20 seconds for the 10 million word case. One could try compiling the function for greater performance.*)
SentenceChunksArray[targetCount_]:=
Block[
{
chunks=ConstantArray["",targetCount],
wordIdx=0,
fragmentIdx=0
},
Scan[(chunks[[++wordIdx]]=#)&,SentenceHeadWords];
++fragmentIdx;
While[
(*Since each new fragment is longer than one word, it is likely that we will try to insert more words into the array than it has been allocated to hold. This generates and error message, but does not otherwise interfere with processing (the insertion simply fails). I could include more checks, but it didn't seem necessary for this task.*)
wordIdx<targetCount,
Scan[(chunks[[++wordIdx]]=#)&,{Splice[IntNameWords[LetterCount[chunks[[++fragmentIdx]]]]],"in","the",Splice[OrdNameWords[fragmentIdx]]}]
];
chunks
];

(*==Output==*)

StringRiffle[
{
DisplayWordLengthSequence[SentenceChunksArray[201]],
DisplayCharacterCount[SentenceChunksArray[201]],
DisplayWordInfo[SentenceChunksArray[1000],1000],
DisplayWordInfo[SentenceChunksArray[10000],10000],
DisplayWordInfo[SentenceChunksArray[100000],100000],
DisplayWordInfo[SentenceChunksArray[1000000],1000000],
DisplayWordInfo[SentenceChunksArray[10000000],10000000]
},
"\n\n"
]


import strutils, strformat, tables

####################################################################################################
# Cardinal and ordinal strings.

const

  Small = ["zero",    "one",     "two",       "three",    "four",
           "five",    "six",     "seven",     "eight",    "nine",
           "ten",     "eleven",  "twelve",    "thirteen", "fourteen",
           "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]

  Tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]

  Illions = ["", " thousand", " million", " billion", " trillion", " quadrillion", " quintillion"]

  IrregularOrdinals = {"one": "first", "two": "second", "three": "third", "five": "fifth",
                       "eight": "eighth", "nine": "ninth", "twelve": "twelfth"}.toTable()

#---------------------------------------------------------------------------------------------------

func spellCardinal(n: int64): string =
  ## Spell an integer as a cardinal.

  var n = n

  if n < 0:
    result = "negative "
    n = -n

  if n < 20:
    result &= Small[n]

  elif n < 100:
    result &= Tens[n div 10]
    let m = n mod 10
    if m != 0: result &= '-' & Small[m]

  elif n < 1000:
    result &= Small[n div 100] & " hundred"
    let m = n mod 100
    if m != 0: result &= ' ' & m.spellCardinal()

  else:
    # Work from right to left.
    var sx = ""
    var i = 0
    while n > 0:
      let m = n mod 1000
      n = n div 1000
      if m != 0:
        var ix = m.spellCardinal() & Illions[i]
        if sx.len > 0: ix &= " " & sx
        sx = ix
      inc i
    result &= sx

#---------------------------------------------------------------------------------------------------

func spellOrdinal(n: int64): string =
  ## Spell an integer as an ordinal.

  result = n.spellCardinal()
  var parts = result.rsplit({' ', '-'}, maxsplit = 1)
  let tail = parts[^1]
  if tail in IrregularOrdinals:
    result[^tail.len..^1] = IrregularOrdinals[tail]
  elif tail.endsWith('y'):
    result[^1..^1]= "ieth"
  else:
    result &= "th"

####################################################################################################
# Sentence building.

type Sentence = seq[string]

#---------------------------------------------------------------------------------------------------

iterator words(sentence: var Sentence): tuple[idx: int; word: string] =
  ## Yield the successive words of the sentence with their index.

  yield (0, "Four")
  var idx = 1
  var last = 0
  while true:
    yield (idx, sentence[idx])
    inc idx
    if idx == sentence.len:
      inc last
      sentence.add([sentence[last].count(Letters).spellCardinal(), "in", "the"])
      # For the position, we need to split the ordinal as it may contain spaces.
      sentence.add(((last + 1).spellOrdinal() & ',').splitWhitespace())

#---------------------------------------------------------------------------------------------------

iterator letterCounts(sentence: var Sentence): tuple[idx: int; word: string; count: int] =
  ## Secondary iterator used to yield the number of letters in addition to the index and the word.

  for i, word in sentence.words():
    yield (i, word, word.count(Letters))

####################################################################################################
# Drivers.

# Constant to initialize the sentence.
const Init = "Four is the number of letters in the first word of this sentence,".splitWhitespace()

#---------------------------------------------------------------------------------------------------

proc displayLetterCounts(pos: Positive) =
  ## Display the number of letters of the word at position "pos".

  var sentence = Init
  echo fmt"Number of letters in first {pos} words in the sequence:"
  var valcount = 0   # Number of values displayed in the current line.
  var length = 0

  for i, word, letterCount in sentence.letterCounts():
    if i == pos:
      # Terminated.
      dec length  # Adjust space count.
      echo ""
      break

    if valcount == 0: stdout.write fmt"{i+1:>3}:"
    stdout.write fmt"{letterCount:>3}"
    inc valcount
    inc length, word.len + 1  # +1 for space.

    if valcount == 12:
      # Terminate line.
      echo ""
      valcount = 0

  echo fmt"Length of sentence: {length}"

#---------------------------------------------------------------------------------------------------

proc displayWord(pos: Positive) =
  ## Display the word at position "pos".

  var sentence = Init
  let idx = pos - 1
  var length = 0
  for i, word in sentence.words():
    length += word.len + 1
    if i == idx:
      dec length    # Adjust space count.
      let w = word.strip(leading = false, chars = {','})  # Remove trailing ',' if needed.
      echo fmt"Word {pos} is ""{w}"" with {w.count(Letters)} letters."
      echo fmt"Length of sentence: {length}"
      break

#———————————————————————————————————————————————————————————————————————————————————————————————————

displayLetterCounts(201)
for n in [1_000, 10_000, 100_000, 1_000_000, 10_000_000]:
  echo ""
  displayWord(n)


use feature 'state';
use Lingua::EN::Numbers qw(num2en num2en_ordinal);

my @sentence = split / /, 'Four is the number of letters in the first word of this sentence, ';

sub extend_to {
    my($last) = @_;
    state $index = 1;
    until ($#sentence > $last) {
        push @sentence, split ' ', num2en(alpha($sentence[$index])) . ' in the ' . no_c(num2en_ordinal(1+$index)) . ',';
        $index++;
    }
}

sub alpha { my($s) = @_; $s =~ s/\W//gi; length $s }
sub no_c  { my($s) = @_; $s =~ s/\ and|,//g;   return $s }
sub count { length(join ' ', @sentence[0..-1+$_[0]]) . " characters in the sentence, up to and including this word.\n" }

print "First 201 word lengths in the sequence:\n";
extend_to(201);
for (0..200) {
    printf "%3d", alpha($sentence[$_]);
    print "\n" unless ($_+1) % 32;
}
print "\n" . count(201) . "\n";

for (1e3, 1e4, 1e5, 1e6, 1e7) {
    extend_to($_);
    print
        ucfirst(num2en_ordinal($_)) .  " word, '$sentence[$_-1]' has " . alpha($sentence[$_-1]) .  " characters. \n" .
        count($_) . "\n";
}


with javascript_semantics
include demo\rosetta\number_names.exw -- see note

-- as per Spelling_of_ordinal_numbers#Phix:
constant {irregs,ordinals} = columnize({{"one","first"},
                                        {"two","second"},
                                        {"three","third"},
                                        {"five","fifth"},
                                        {"eight","eighth"},
                                        {"nine","ninth"},
                                        {"twelve","twelfth"}})

function ordinl(string s)
    integer i
    for i=length(s) to 1 by -1 do
        integer ch = s[i]
        if ch=' ' or ch='-' then exit end if
    end for
    integer k = find(s[i+1..$],irregs)
    if k then
        s = s[1..i]&ordinals[k]
    elsif s[$]='y' then
        s[$..$] = "ieth"
    else
        s &= "th"
    end if
    return s
end function
--/copy of Spelling_of_ordinal_numers#Phix

function count_letters(string s)
    integer res = 0
    for i=1 to length(s) do
        integer ch = s[i]
        if (ch>='A' and ch<='Z')
        or (ch>='a' and ch<='z') then
            res += 1
        end if
    end for
    return res
end function

sequence words = split("Four is the number of letters in the first word of this sentence,")
integer fi = 1

function kill_and(sequence s)
--grr...
    for i=length(s) to 1 by -1 do
        if s[i] = "and" then
            s[i..i] = {}
        end if
    end for
    return s
end function

function word_len(integer w)
-- Returns the w'th word and its length (only counting letters).
    while length(words)<w do
        fi += 1
        integer n = count_letters(words[fi])
        sequence ns = kill_and(split(spell(n)))
        sequence os = kill_and(split(ordinl(spell(fi)) & ","))
        -- append eg {"two","in","the","second,"}
        words &= ns&{"in","the"}&os
    end while
    string word = words[w]
    return {word, count_letters(word)}
end function

function total_length()
-- Returns the total number of characters (including blanks,
-- commas, and punctuation) of the sentence so far constructed.
    integer res = 0
    for i=1 to length(words) do
        res += length(words[i])+1
    end for
    return res
end function

procedure main()
    printf(1,"The lengths of the first 201 words are:\n")
    for i=1 to 201 do
        if mod(i,25)==1 then
            printf(1,"\n%3d: ", i)
        end if
        printf(1," %2d", word_len(i)[2])
    end for
    printf(1,"\nLength of sentence so far:%d\n", total_length())
    for p=3 to iff(platform()=JS?5:7) do
        integer i = power(10,p)
        {string w, integer n} = word_len(i)
        printf(1,"Word %8d is \"%s\", with %d letters.", {i, w, n})
        printf(1,"  Length of sentence so far:%d\n", total_length())
    end for
end procedure
main()


# Python implementation of Rosetta Code Task
# http://rosettacode.org/wiki/Four_is_the_number_of_letters_in_the_...
# Uses inflect
# https://pypi.org/project/inflect/

import inflect

def count_letters(word):
    """
    count letters ignore , or -, or space
    """
    count = 0
    for letter in word:
        if letter != ',' and letter !='-' and letter !=' ':
            count += 1

    return count

def split_with_spaces(sentence):
    """
    Takes string with partial sentence and returns
    list of words with spaces included.

    Leading space is attached to first word.
    Later spaces attached to prior word.
    """
    sentence_list = []
    curr_word = ""
    for c in sentence:
        if c == " " and curr_word != "":
            # append space to end of non-empty words
            # assumed no more than 1 consecutive space.
            sentence_list.append(curr_word+" ")
            curr_word = ""
        else:
            curr_word += c

    # add trailing word that does not end with a space

    if len(curr_word) > 0:
        sentence_list.append(curr_word)

    return sentence_list

def my_num_to_words(p, my_number):
    """
    Front end to inflect's number_to_words

    Get's rid of ands and commas in large numbers.
    """

    number_string_list = p.number_to_words(my_number, wantlist=True, andword='')

    number_string = number_string_list[0]

    for i in range(1,len(number_string_list)):
        number_string += " " + number_string_list[i]

    return number_string

def build_sentence(p, max_words):
    """

    Builds at most max_words of the task following the pattern:

    Four is the number of letters in the first word of this sentence, two in the second,
    three in the third, six in the fourth, two in the fifth, seven in the sixth,

    """

    # start with first part of sentence up first comma as a list

    sentence_list = split_with_spaces("Four is the number of letters in the first word of this sentence,")

    num_words = 13

    # which word number we are doing next
    # two/second is first one in loop

    word_number = 2

    # loop until sentance is at least as long as needs be

    while num_words < max_words:
        # Build something like
        # ,two in the second

        # get second or whatever we are on

        ordinal_string = my_num_to_words(p, p.ordinal(word_number))

        # get two or whatever the length is of the word_number word

        word_number_string = my_num_to_words(p, count_letters(sentence_list[word_number - 1]))

        # sentence addition

        new_string = " "+word_number_string+" in the "+ordinal_string+","

        new_list = split_with_spaces(new_string)

        sentence_list += new_list

        # add new word count

        num_words += len(new_list)

        # increment word number

        word_number += 1

    return sentence_list, num_words

def word_and_counts(word_num):
    """

    Print's lines like this:

    Word     1000 is "in", with 2 letters.  Length of sentence so far: 6279

    """

    sentence_list, num_words = build_sentence(p, word_num)

    word_str = sentence_list[word_num - 1].strip(' ,')

    num_letters = len(word_str)

    num_characters = 0

    for word in sentence_list:
       num_characters += len(word)

    print('Word {0:8d} is "{1}", with {2} letters.  Length of the sentence so far: {3}  '.format(word_num,word_str,num_letters,num_characters))

p = inflect.engine()

sentence_list, num_words = build_sentence(p, 201)

print(" ")
print("The lengths of the first 201 words are:")
print(" ")

print('{0:3d}:  '.format(1),end='')

total_characters = 0

for word_index in range(201):

    word_length = count_letters(sentence_list[word_index])

    total_characters += len(sentence_list[word_index])

    print('{0:2d}'.format(word_length),end='')
    if (word_index+1) % 20 == 0:
        # newline every 20
        print(" ")
        print('{0:3d}:  '.format(word_index + 2),end='')
    else:
        print(" ",end='')

print(" ")
print(" ")
print("Length of the sentence so far: "+str(total_characters))
print(" ")

"""

Expected output this part:

Word     1000 is "in", with 2 letters.  Length of the sentence so far: 6279
Word    10000 is "in", with 2 letters.  Length of the sentence so far: 64140
Word   100000 is "one", with 3 letters.  Length of the sentence so far: 659474
Word  1000000 is "the", with 3 letters.  Length of the sentence so far: 7113621
Word 10000000 is "thousand", with 8 letters.  Length of the sentence so far: 70995756

"""

word_and_counts(1000)
word_and_counts(10000)
word_and_counts(100000)
word_and_counts(1000000)
word_and_counts(10000000)


*        FRACTRAN                  17/02/2019
FRACTRAN CSECT
         USING  FRACTRAN,R13       base register
         B      72(R15)            skip savearea
         DC     17F'0'             savearea
         SAVE   (14,12)            save previous context
         ST     R13,4(R15)         link backward
         ST     R15,8(R13)         link forward
         LR     R13,R15            set addressability
         LA     R6,1               i=1
       DO WHILE=(C,R6,LE,TERMS)    do i=1 to terms
         LA     R7,1                 j=1
       DO WHILE=(C,R7,LE,=A(NF))     do j=1 to nfracs
         LR     R1,R7                  j
         SLA    R1,3                   ~
         L      R2,FRACS-4(R1)         d(j)
         L      R4,NN                  nn
         SRDA   R4,32                  ~
         DR     R4,R2                  nn/d(j)
       IF       LTR,R4,Z,R4 THEN       if mod(nn,d(j))=0 then
         XDECO  R6,XDEC                  edit i
         MVC    PG(3),XDEC+9             output i
         L      R1,NN                    nn
         XDECO  R1,PG+5                  edit & output nn
         XPRNT  PG,L'PG                  print buffer
         LR     R1,R7                    j
         SLA    R1,3                     ~
         L      R3,FRACS-8(R1)           n(j)
         MR     R4,R3                    *n(j)
         ST     R5,NN                    nn=nn/d(j)*n(j)
         B      LEAVEJ                   leave j
       ENDIF    ,                      end if
         LA     R7,1(R7)               j++
       ENDDO    ,                    end do j
LEAVEJ   LA     R6,1(R6)             i++
       ENDDO    ,                  end do i
         L      R13,4(0,R13)       restore previous savearea pointer
         RETURN (14,12),RC=0       restore registers from calling sav
NF       EQU    (TERMS-FRACS)/8    number of fracs
NN       DC     F'2'               nn
FRACS  DC  F'17',F'91',F'78',F'85',F'19',F'51',F'23',F'38',F'29',F'33'
       DC  F'77',F'29',F'95',F'23',F'77',F'19',F'1',F'17',F'11',F'13'
       DC  F'13',F'11',F'15',F'14',F'15',F'2',F'55',F'1'
TERMS    DC     F'100'             terms
PG       DC     CL80'*** :'        buffer
XDEC     DS     CL12               temp
         REGEQU
         END    FRACTRAN


with Ada.Text_IO;

procedure Fractan is

   type Fraction is record Nom: Natural; Denom: Positive; end record;
   type Frac_Arr is array(Positive range <>) of Fraction;

   function "/" (N: Natural; D: Positive) return Fraction is
      Frac: Fraction := (Nom => N, Denom => D);
   begin
      return Frac;
   end "/";

   procedure F(List: Frac_Arr; Start: Positive; Max_Steps: Natural) is
      N: Positive := Start;
      J: Positive;
   begin
      Ada.Text_IO.Put(" 0:" & Integer'Image(N) & "   ");
      for I in 1 .. Max_Steps loop
	 J := List'First;
	 loop
	    if N mod List(J).Denom = 0 then
	       N := (N/List(J).Denom) * List(J).Nom;
	       exit; -- found fraction
	    elsif J >= List'Last then
	       return; -- did try out all fractions
	    else
	       J := J + 1; -- try the next fraction
	    end if;
	 end loop;
	 Ada.Text_IO.Put(Integer'Image(I) & ":" & Integer'Image(N) & "   ");
      end loop;
   end F;

begin
   -- F((2/3, 7/2, 1/5, 1/7, 1/9, 1/4, 1/8), 2, 100);
   -- output would be "0: 2    1: 7    2: 1" and then terminate

   F((17/91, 78/85, 19/51, 23/38, 29/33, 77/29, 95/23,
      77/19,  1/17, 11/13, 13/11, 15/14,  15/2, 55/1),
     2, 15);
   -- output is "0: 2    1: 15    2: 825    3: 725   ...   14: 132    15: 116"
end Fractan;


use Lingua::EN::Numbers;
no-commas(True);

my $index = 1;
my @sentence = flat 'Four is the number of letters in the first word of this sentence, '.words,
  { @sentence[$index++].&alpha.&cardinal, 'in', 'the', |($index.&ordinal ~ ',').words } ... * ;

sub alpha ( $str ) { $str.subst(/\W/, '', :g).chars }
sub count ( $index ) { @sentence[^$index].join(' ').chars ~ " characters in the sentence, up to and including this word.\n" }

say 'First 201 word lengths in the sequence:';
put ' ', map { @sentence[$_].&alpha.fmt("%2d") ~ (((1+$_) %% 25) ?? "\n" !! '') }, ^201;
say 201.&count;

for 1e3, 1e4, 1e5, 1e6, 1e7 {
    say "{.&ordinal.tc} word, '{@sentence[$_ - 1]}', has {@sentence[$_ - 1].&alpha} characters. ", .&count
}


/*REXX pgm finds/shows the number of letters in the  Nth  word in a constructed sentence*/
@= 'Four is the number of letters in the first word of this sentence,'             /*···*/
                                                 /* [↑]   the start of a long sentence. */
parse arg N M                                    /*obtain optional argument from the CL.*/
if N='' | N="," then N= 201                      /*Not specified?  Then use the default.*/
if M='' | M="," then M=1000 10000 100000 1000000 /* "      "         "   "   "     "    */
@abcU= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'              /*define the uppercase Latin alphabet. */
!.=.;     #.=.;      q=1;       w=length(N)      /* [↓]  define some helpful low values.*/
call tell N
if N<0  then say y     ' is the length of word '         a          "  ["word(@, a)"]"
say                                              /* [↑]  N negative?  Just show 1 number*/
say 'length of sentence= '   length(@)           /*display the length of the @ sentence.*/

if M\==''  then do k=1  for words(M)  while M\=0 /*maybe handle counts  (if specified). */
                x=word(M, k)                     /*obtain the  Kth  word of the M list. */
                call tell  -x                    /*invoke subroutine (with negative arg)*/
                say
                say y     ' is the length of word '      x       "  ["word(@, x)"]"
                say 'length of sentence= '  length(@)    /*display length of @ sentence.*/
                end   /*k*/
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
wordLen: arg ?;         return length(?) - length( space( translate(?, , @abcU), 0) )
/*──────────────────────────────────────────────────────────────────────────────────────*/
tell: parse arg z,,$;   idx=1;    a=abs(z);     group=25     /*show 25 numbers per line.*/
                                                 /*Q is the last number spelt by $SPELL#*/
        do j=1  for a                            /*traipse through all the numbers to N.*/
          do 2                                   /*perform loop twice  (well ··· maybe).*/
          y=wordLen( word(@, j) )                /*get the  Jth  word from the sentence.*/
          if y\==0  then leave                   /*Is the word spelt?   Then we're done.*/
          q=q + 1                                /*bump the on─going (moving) # counter.*/
          if #.q==.  then #.q=$spell#(q 'Q ORD') /*need to spell A as an ordinal number?*/
               _=wordLen( word(@, q) )           /*use the length of the ordinal number.*/
          if !._==.  then !._=$spell#(_ 'Q')     /*Not spelled?   Then go and spell it. */
          @[email protected]  !._   'in the'    #.q","          /*append words to never─ending sentence*/
          end   /*2*/                            /* [↑]   Q ≡ Quiet      ORD ≡ ORDinal  */

        $=$ || right(y, 3)                       /* [↓]  append a justified # to a line.*/
        if j//group==0 & z>0  then do; say right(idx, w)'►'$;   idx=idx+group;   $=;   end
        end   /*j*/                              /* [↑]  show line if there's enough #s.*/

      if $\=='' & z>0 then say right(idx, w)'►'$ /*display if there are residual numbers*/
      return


# as the numbers required for finding the first 20 primes are quite large, #
# we use Algol 68G's LONG LONG INT with a precision of 100 digits          #
PR precision 100 PR

# mode to hold fractions #
MODE FRACTION = STRUCT( INT numerator, INT denominator );

# define / between two INTs to yield a FRACTION #
OP / = ( INT a, b )FRACTION: ( a, b );

# mode to define a FRACTRAN progam #
MODE FRACTRAN = STRUCT( FLEX[0]FRACTION data
                      , LONG LONG INT   n
                      , BOOL            halted
                      );
# prepares a FRACTRAN program for use - sets the initial value of n and halted to FALSE #
PRIO STARTAT = 1;
OP   STARTAT = ( REF FRACTRAN f, INT start )REF FRACTRAN:
BEGIN
    halted OF f := FALSE;
         n OF f := start;
    f
END;

# sets n OF f to the next number in the sequence or sets halted OF f to TRUE if the sequence has ended #
OP NEXT = ( REF FRACTRAN f )LONG LONG INT:
    IF halted OF f
    THEN n OF f := 0
    ELSE
        BOOL          found  := FALSE;
        LONG LONG INT result := 0;
        FOR pos FROM LWB data OF f TO UPB data OF f WHILE NOT found DO
            LONG LONG INT value       = n OF f * numerator OF ( ( data OF f )[ pos ] );
            INT           denominator = denominator OF ( ( data OF f )[ pos ] );
            IF found := ( value MOD denominator = 0 ) THEN result := value OVER denominator FI
        OD;
        IF NOT found THEN halted OF f := TRUE FI;
        n OF f := result
    FI ;

# generate and print the sequence of numbers from a FRACTRAN pogram #
PROC print fractran sequence = ( REF FRACTRAN f, INT start, INT limit )VOID:
BEGIN
    VOID( f STARTAT start );
    print( ( "0: ", whole( start, 0 ) ) );
    FOR i TO limit
    WHILE VOID( NEXT f );
          NOT halted OF f
    DO
        print( ( " " + whole( i, 0 ) + ": " + whole( n OF f, 0 ) ) )
    OD;
    print( ( newline ) )
END ;

# print the first 16 elements from the primes FRACTRAN program #
FRACTRAN pf := ( ( 17/91, 78/85, 19/51, 23/38, 29/33, 77/29, 95/23, 77/19, 1/17, 11/13, 13/11, 15/14,  15/2, 55/1 ), 0, FALSE );
print fractran sequence( pf, 2, 15 );

# find some primes using the pf FRACTRAN progam - n is prime for the members in the sequence that are 2^n #
INT primes found := 0;
VOID( pf STARTAT 2 );
INT pos := 0;
print( ( "seq position  prime sequence value", newline ) );
WHILE primes found < 20 AND NOT halted OF pf DO
    LONG LONG INT value      := NEXT pf;
    INT      power of 2 := 0;
    pos +:= 1;
    WHILE value MOD 2 = 0 AND value > 0 DO power of 2 PLUSAB 1; value OVERAB 2 OD;
    IF value = 1 THEN
        # found a prime #
        primes found +:= 1;
        print( ( whole( pos, -12 ) + " " + whole( power of 2, -6 ) + " (" + whole( n OF pf, 0 ) + ")", newline ) )
    FI
OD


F indexOf(haystack, needle)
   V idx = 0
   L(straw) haystack
      I straw == needle
         R idx
      E
         idx++
   R -1

F getDigits(=n, =le, &digits)
   L n > 0
      V r = n % 10
      I r == 0 | indexOf(digits, r) >= 0
         R 0B
      le--
      digits[le] = r
      n = Int(n / 10)
   R 1B

F removeDigit(digits, le, idx)
   V pows = [1, 10, 100, 1000, 10000]
   V sum = 0
   V pow = pows[le - 2]
   V i = 0
   L i < le
      I i == idx
         i++
         L.continue
      sum = sum + digits[i] * pow
      pow = Int(pow / 10)
      i++
   R sum

V lims = [ [ 12, 97 ], [ 123, 986 ], [ 1234, 9875 ], [ 12345, 98764 ] ]
V count = [0] * 5
V omitted = [[0] * 10] * 5

V i = 0
L i < lims.len
   V n = lims[i][0]
   L n < lims[i][1]
      V nDigits = [0] * (i + 2)
      V nOk = getDigits(n, i + 2, &nDigits)
      I !nOk
         n++
         L.continue
      V d = n + 1
      L d <= lims[i][1] + 1
         V dDigits = [0] * (i + 2)
         V dOk = getDigits(d, i + 2, &dDigits)
         I !dOk
            d++
            L.continue
         V nix = 0
         L nix < nDigits.len
            V digit = nDigits[nix]
            V dix = indexOf(dDigits, digit)
            I dix >= 0
               V rn = removeDigit(nDigits, i + 2, nix)
               V rd = removeDigit(dDigits, i + 2, dix)
               I (1.0 * n / d) == (1.0 * rn / rd)
                  count[i]++
                  omitted[i][digit]++
                  I count[i] <= 12
                     print(‘#./#. = #./#. by omitting #.'s’.format(n, d, rn, rd, digit))
            nix++
         d++
      n++
   print()
   i++

i = 2
L i <= 5
   print(‘There are #. #.-digit fractions of which:’.format(count[i - 2], i))
   V j = 1
   L j <= 9
      I omitted[i - 2][j] == 0
         j++
         L.continue
      print(‘#6 have #.'s omitted’.format(omitted[i - 2][j], j))
      j++
   print()
   i++


struct NumberNames {
    cardinal: &'static str,
    ordinal: &'static str,
}

impl NumberNames {
    fn get_name(&self, ordinal: bool) -> &'static str {
        if ordinal {
            return self.ordinal;
        }
        self.cardinal
    }
}

const SMALL_NAMES: [NumberNames; 20] = [
    NumberNames {
        cardinal: "zero",
        ordinal: "zeroth",
    },
    NumberNames {
        cardinal: "one",
        ordinal: "first",
    },
    NumberNames {
        cardinal: "two",
        ordinal: "second",
    },
    NumberNames {
        cardinal: "three",
        ordinal: "third",
    },
    NumberNames {
        cardinal: "four",
        ordinal: "fourth",
    },
    NumberNames {
        cardinal: "five",
        ordinal: "fifth",
    },
    NumberNames {
        cardinal: "six",
        ordinal: "sixth",
    },
    NumberNames {
        cardinal: "seven",
        ordinal: "seventh",
    },
    NumberNames {
        cardinal: "eight",
        ordinal: "eighth",
    },
    NumberNames {
        cardinal: "nine",
        ordinal: "ninth",
    },
    NumberNames {
        cardinal: "ten",
        ordinal: "tenth",
    },
    NumberNames {
        cardinal: "eleven",
        ordinal: "eleventh",
    },
    NumberNames {
        cardinal: "twelve",
        ordinal: "twelfth",
    },
    NumberNames {
        cardinal: "thirteen",
        ordinal: "thirteenth",
    },
    NumberNames {
        cardinal: "fourteen",
        ordinal: "fourteenth",
    },
    NumberNames {
        cardinal: "fifteen",
        ordinal: "fifteenth",
    },
    NumberNames {
        cardinal: "sixteen",
        ordinal: "sixteenth",
    },
    NumberNames {
        cardinal: "seventeen",
        ordinal: "seventeenth",
    },
    NumberNames {
        cardinal: "eighteen",
        ordinal: "eighteenth",
    },
    NumberNames {
        cardinal: "nineteen",
        ordinal: "nineteenth",
    },
];

const TENS: [NumberNames; 8] = [
    NumberNames {
        cardinal: "twenty",
        ordinal: "twentieth",
    },
    NumberNames {
        cardinal: "thirty",
        ordinal: "thirtieth",
    },
    NumberNames {
        cardinal: "forty",
        ordinal: "fortieth",
    },
    NumberNames {
        cardinal: "fifty",
        ordinal: "fiftieth",
    },
    NumberNames {
        cardinal: "sixty",
        ordinal: "sixtieth",
    },
    NumberNames {
        cardinal: "seventy",
        ordinal: "seventieth",
    },
    NumberNames {
        cardinal: "eighty",
        ordinal: "eightieth",
    },
    NumberNames {
        cardinal: "ninety",
        ordinal: "ninetieth",
    },
];

struct NamedNumber {
    cardinal: &'static str,
    ordinal: &'static str,
    number: usize,
}

impl NamedNumber {
    fn get_name(&self, ordinal: bool) -> &'static str {
        if ordinal {
            return self.ordinal;
        }
        self.cardinal
    }
}

const N: usize = 7;
const NAMED_NUMBERS: [NamedNumber; N] = [
    NamedNumber {
        cardinal: "hundred",
        ordinal: "hundredth",
        number: 100,
    },
    NamedNumber {
        cardinal: "thousand",
        ordinal: "thousandth",
        number: 1000,
    },
    NamedNumber {
        cardinal: "million",
        ordinal: "millionth",
        number: 1000000,
    },
    NamedNumber {
        cardinal: "billion",
        ordinal: "billionth",
        number: 1000000000,
    },
    NamedNumber {
        cardinal: "trillion",
        ordinal: "trillionth",
        number: 1000000000000,
    },
    NamedNumber {
        cardinal: "quadrillion",
        ordinal: "quadrillionth",
        number: 1000000000000000,
    },
    NamedNumber {
        cardinal: "quintillion",
        ordinal: "quintillionth",
        number: 1000000000000000000,
    },
];

fn big_name(n: usize) -> &'static NamedNumber {
    for i in 1..N {
        if n < NAMED_NUMBERS[i].number {
            return &NAMED_NUMBERS[i - 1];
        }
    }
    &NAMED_NUMBERS[N - 1]
}

fn count_letters(s: &str) -> usize {
    let mut count = 0;
    for c in s.chars() {
        if c.is_alphabetic() {
            count += 1;
        }
    }
    count
}

struct WordList {
    words: Vec<(usize, usize)>,
    string: String,
}

impl WordList {
    fn new() -> WordList {
        WordList {
            words: Vec::new(),
            string: String::new(),
        }
    }
    fn append(&mut self, s: &str) {
        let offset = self.string.len();
        self.string.push_str(s);
        self.words.push((offset, offset + s.len()));
    }
    fn extend(&mut self, s: &str) {
        let len = self.words.len();
        let mut w = &mut self.words[len - 1];
        w.1 += s.len();
        self.string.push_str(s);
    }
    fn len(&self) -> usize {
        self.words.len()
    }
    fn sentence_length(&self) -> usize {
        let n = self.words.len();
        if n == 0 {
            return 0;
        }
        self.string.len() + n - 1
    }
    fn get_word(&self, index: usize) -> &str {
        let w = &self.words[index];
        &self.string[w.0..w.1]
    }
}

fn append_number_name(words: &mut WordList, n: usize, ordinal: bool) -> usize {
    let mut count = 0;
    if n < 20 {
        words.append(SMALL_NAMES[n].get_name(ordinal));
        count += 1;
    } else if n < 100 {
        if n % 10 == 0 {
            words.append(TENS[n / 10 - 2].get_name(ordinal));
        } else {
            words.append(TENS[n / 10 - 2].get_name(false));
            words.extend("-");
            words.extend(SMALL_NAMES[n % 10].get_name(ordinal));
        }
        count += 1;
    } else {
        let big = big_name(n);
        count += append_number_name(words, n / big.number, false);
        if n % big.number == 0 {
            words.append(big.get_name(ordinal));
            count += 1;
        } else {
            words.append(big.get_name(false));
            count += 1;
            count += append_number_name(words, n % big.number, ordinal);
        }
    }
    count
}

fn sentence(count: usize) -> WordList {
    let mut result = WordList::new();
    const WORDS: &'static [&'static str] = &[
        "Four",
        "is",
        "the",
        "number",
        "of",
        "letters",
        "in",
        "the",
        "first",
        "word",
        "of",
        "this",
        "sentence,",
    ];
    for s in WORDS {
        result.append(s);
    }
    let mut n = result.len();
    let mut i = 1;
    while count > n {
        let count = count_letters(result.get_word(i));
        n += append_number_name(&mut result, count, false);
        result.append("in");
        result.append("the");
        n += 2;
        n += append_number_name(&mut result, i + 1, true);
        result.extend(",");
        i += 1;
    }
    result
}

fn main() {
    let mut n = 201;
    let s = sentence(n);
    println!("Number of letters in first {} words in the sequence:", n);
    for i in 0..n {
        if i != 0 {
            if i % 25 == 0 {
                println!();
            } else {
                print!(" ");
            }
        }
        print!("{:2}", count_letters(s.get_word(i)));
    }
    println!();
    println!("Sentence length: {}", s.sentence_length());
    n = 1000;
    while n <= 10000000 {
        let s = sentence(n);
        let word = s.get_word(n - 1);
        print!(
            "The {}th word is '{}' and has {} letters. ",
            n,
            word,
            count_letters(word)
        );
        println!("Sentence length: {}", s.sentence_length());
        n *= 10;
    }
}


import "/fmt" for Fmt

var names = {
     1: "one",
     2: "two",
     3: "three",
     4: "four",
     5: "five",
     6: "six",
     7: "seven",
     8: "eight",
     9: "nine",
    10: "ten",
    11: "eleven",
    12: "twelve",
    13: "thirteen",
    14: "fourteen",
    15: "fifteen",
    16: "sixteen",
    17: "seventeen",
    18: "eighteen",
    19: "nineteen",
    20: "twenty",
    30: "thirty",
    40: "forty",
    50: "fifty",
    60: "sixty",
    70: "seventy",
    80: "eighty",
    90: "ninety"
}

var bigNames = {
    1e3 : "thousand",
    1e6 : "million",
    1e9 : "billion",
    1e12: "trillion",
    1e15: "quadrillion"
}

var irregOrdinals = {
    "one"   : "first",
    "two"   : "second",
    "three" : "third",
    "five"  : "fifth",
    "eight" : "eighth",
    "nine"  : "ninth",
    "twelve": "twelfth"
}

var strToOrd = Fn.new { |s|
    if (s == "zero") return "zeroth" // or alternatively 'zeroeth'
    var splits = s.replace("-", " ").split(" ")
    var last = splits[-1]
    return irregOrdinals.containsKey(last) ? s[0...-last.count] + irregOrdinals[last] :
           last.endsWith("y") ? s[0...-1] + "ieth" : s + "th"
}

var numToText = Fn.new { |n, uk|
    if (n == 0) return "zero"
    var neg = n < 0
    var nn = neg ? - n : n
    var digits3 = List.filled(6, 0)
    for (i in 0..5) {  // split number into groups of 3 digits from the right
        digits3[i] = nn % 1000
        nn = (nn / 1000).truncate
    }

    var threeDigitsToText = Fn.new { |number|
        var sb = ""
        if (number == 0) return ""
        var hundreds = (number / 100).truncate
        var remainder = number % 100
        if (hundreds > 0) {
            sb = sb + names[hundreds] + " hundred"
            if (remainder > 0) sb = sb + (uk ? " and " : " ")
        }
        if (remainder > 0) {
            var tens = (remainder / 10).truncate
            var units = remainder % 10
            if (tens > 1) {
                sb = sb + names[tens * 10]
                if (units > 0) sb = sb + "-" + names[units]
            } else {
                sb = sb + names[remainder]
            }
        }
        return sb
    }

    var strings = List.filled(6, 0)
    for (i in 0..5) strings[i] = threeDigitsToText.call(digits3[i])
    var text = strings[0]
    var andNeeded = uk && 1 <= digits3[0] && digits3[0] <= 99
    var big = 1000
    for (i in 1..5) {
        if (digits3[i] > 0) {
            var text2 = strings[i] + " " + bigNames[big]
            if (!text.isEmpty) {
                text2 = text2 + (andNeeded ? " and " : " ")  // no commas inserted in this version
                andNeeded = false
            } else {
                andNeeded = uk && 1 <= digits3[i] && digits3[i] <= 99
            }
            text = text2 + text
        }
        big = big * 1000
    }
    if (neg) text = "minus " + text
    return text
}

var opening = "Four is the number of letters in the first word of this sentence,".split(" ")

var adjustedLength = Fn.new { |s|  s.replace(",", "").replace("-", "").count } // no ',' or '-'

var getWords = Fn.new { |n|
    var words = []
    words.addAll(opening)
    if (n > opening.count) {
        var k = 2
        while (true) {
            var len = adjustedLength.call(words[k - 1])
            var text = numToText.call(len, false)
            var splits = text.split(" ")
            words.addAll(splits)
            words.add("in")
            words.add("the")
            var text2 = strToOrd.call(numToText.call(k, false)) + ","  // add trailing comma
            var splits2 = text2.split(" ")
            words.addAll(splits2)
            if (words.count >= n) break
            k = k + 1
        }
    }
    return words
}

var getLengths = Fn.new { |n|
    var words = getWords.call(n)
    var lengths = words.take(n).map { |w| adjustedLength.call(w) }.toList
    // includes hyphens, commas & spaces
    var sentenceLength = words.reduce(0) { |acc, w| acc + w.count } + words.count - 1
    return [lengths, sentenceLength]
}

var getLastWord = Fn.new { |n|
    var words = getWords.call(n)
    var nthWord = words[n - 1]
    var nthWordLength = adjustedLength.call(nthWord)
    // includes hyphens, commas & spaces
    var sentenceLength = words.reduce(0) { |acc, w| acc + w.count } + words.count - 1
    return [nthWord, nthWordLength, sentenceLength]
}

var n = 201
System.print("The lengths of the first %(n) words are:\n")
var res = getLengths.call(n)
var list = res[0]
var sentenceLength = res[1]
for (i in 0...n) {
    if (i % 25 == 0) {
        if (i > 0) System.print()
        Fmt.write("$3d: ", i + 1)
    }
    Fmt.write("$3d", list[i])
}
Fmt.print("\n\nLength of sentence = $,d\n", sentenceLength)

n = 1000
while (true) {
    var res = getLastWord.call(n)
    var word = res[0]
    var wLen = res[1]
    var sLen = res[2]
    if (word.endsWith(",")) word = word[0...-1]  // strip off any trailing comma
    Fmt.print("The length of word $,d [$s] is $d", n, word, wLen)
    Fmt.print("Length of sentence = $,d\n", sLen)
    n = n * 10
    if (n > 1e7) break
}


n := 2, steplimit := 15, numerator := [], denominator := []
s := "17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1"

Loop, Parse, s, % A_Space
    if (!RegExMatch(A_LoopField, "^(\d+)/(\d+)$", m))
        MsgBox, % "Invalid input string (" A_LoopField ")."
    else
        numerator[A_Index] := m1, denominator[A_Index] := m2

SetFormat, FloatFast, 0.0
Gui, Add, ListView, R10 W100 -Hdr, |
SysGet, VSBW, 2
LV_ModifyCol(1, 95 - VSBW), LV_Add( , 0 ": " n)
Gui, Show

Loop, % steplimit {
    i := A_Index
    Loop, % numerator.MaxIndex()
        if (!Mod(nn := n * numerator[A_Index] / denominator[A_Index], 1)) {
            LV_Modify(LV_Add( , i ": " (n := nn)), "Vis")
            continue, 2
        }
    break
}


with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Text_IO; use Ada.Text_IO;
procedure Fraction_Reduction is

type Int_Array is array (Natural range <>) of Integer;

function indexOf(haystack : Int_Array; needle : Integer) return Integer is
    idx : Integer := 0;
begin
    for straw of haystack loop
        if straw = needle then
            return idx;
        else
            idx := idx + 1;
        end if;
    end loop;
    return -1;
end IndexOf;

function getDigits(n, le : in Integer;
                   digit_array : in out Int_Array) return Boolean is
    n_local : Integer := n;
    le_local : Integer := le;
    r : Integer;
begin
    while n_local > 0 loop
        r := n_local mod 10;
        if r = 0 or indexOf(digit_array, r) >= 0 then
            return False;
        end if;
        le_local := le_local - 1;
        digit_array(le_local) := r;
        n_local := n_local / 10;
    end loop;
    return True;
end getDigits;

function removeDigit(digit_array : Int_Array;
                     le, idx : Integer) return Integer is
    sum : Integer := 0;
    pow : Integer := 10 ** (le - 2);
begin
    for i in 0 .. le - 1 loop
        if i /= idx then
            sum := sum + digit_array(i) * pow;
            pow := pow / 10;
        end if;
    end loop;
    return sum;
end removeDigit;

    lims : constant array (0 .. 3) of Int_Array (0 .. 1) :=
       ((12, 97), (123, 986), (1234, 9875), (12345, 98764));
    count : Int_Array (0 .. 4) := (others => 0);
    omitted : array (0 .. 4) of Int_Array (0 .. 9) :=
       (others => (others => 0));
begin
    Ada.Integer_Text_IO.Default_Width := 0;
    for i in lims'Range loop
        declare
            nDigits, dDigits : Int_Array (0 .. i + 1);
            digit, dix, rn, rd : Integer;
        begin
            for n in lims(i)(0) .. lims(i)(1) loop
                nDigits := (others => 0);
                if getDigits(n, i + 2, nDigits) then
                    for d in n + 1 .. lims(i)(1) + 1 loop
                        dDigits := (others => 0);
                        if getDigits(d, i + 2, dDigits) then
                            for nix in nDigits'Range loop
                                digit := nDigits(nix);
                                dix := indexOf(dDigits, digit);
                                if dix >= 0 then
                                    rn := removeDigit(nDigits, i + 2, nix);
                                    rd := removeDigit(dDigits, i + 2, dix);
                                    -- 'n/d = rn/rd' is same as 'n*rd = rn*d'
                                    if n*rd = rn*d then
                                        count(i) := count(i) + 1;
                                        omitted(i)(digit) :=
                                           omitted(i)(digit) + 1;
                                        if count(i) <= 12 then
                                            Put (n);
                                            Put ("/");
                                            Put (d);
                                            Put (" = ");
                                            Put (rn);
                                            Put ("/");
                                            Put (rd);
                                            Put (" by omitting ");
                                            Put (digit);
                                            Put_Line ("'s");
                                        end if;
                                    end if;
                                end if;
                            end loop;
                        end if;
                    end loop;
                end if;
            end loop;
        end;
        New_Line;
    end loop;
    for i in 2 .. 5 loop
        Put ("There are ");
        Put (count(i - 2));
        Put (" ");
        Put (i);
        Put_Line ("-digit fractions of which:");
        for j in 1 .. 9 loop
            if omitted(i - 2)(j) /= 0 then
                Put (omitted(i - 2)(j), Width => 6);
                Put (" have ");
                Put (j);
                Put_Line ("'s omitted");
            end if;
        end loop;
        New_Line;
    end loop;

end Fraction_Reduction;


   // Built the sentence in little chucks but only save the last one
   // Save the word counts
fcn fourIsThe(text,numWords){
   const rmc="-,";
   seq:=(text - rmc).split().apply("len").copy();  // (4,2,3,6...)
   szs:=Data(numWords + 100,Int).howza(0).extend(seq);	// bytes
   cnt,lastWords := seq.len(),"";
   total:=seed.len() - 1;  // don't count trailing space

   foreach idx in ([1..]){
      sz:=szs[idx];
      a,b := nth(sz,False),nth(idx+1);  // "two","three hundred sixty-seventh"
      lastWords="%s in the %s, ".fmt(a,b);
      ws:=lastWords.counts(" ")[1];  // "five in the forty-ninth " --> 4
      cnt+=ws; total+=lastWords.len();
      lastWords.split().pump(szs.append,'-(rmc),"len");
      if(cnt>=numWords){
	 if(cnt>numWords){
	    z,n:=lastWords.len(),z-2;
	    do(cnt - numWords){ n=lastWords.rfind(" ",n) - 1; }
	    lastWords=lastWords[0,n+1]; total-=(z - n);
	 }
	 break;
      }
   }
   return(lastWords.strip(),szs,total);
}
fcn lastWord(sentence){ sentence[sentence.rfind(" ")+1,*] }


#! /bin/bash
program="1/1 455/33 11/13 1/11 3/7 11/2 1/3"
echo $program | tr " " "\n" | cut -d"/" -f1 | tr "\n" " " > "data"
read -a ns < "data"
echo $program | tr " " "\n" | cut -d"/" -f2 | tr "\n" " " > "data"
read -a ds < "data"

t=0
n=72
echo "steps of computation" > steps.csv
while [ $t -le 6 ]; do
	if [ $(($n*${ns[$t]}%${ds[$t]})) -eq 0 ]; then
		let "n=$(($n*${ns[$t]}/${ds[$t]}))"
		let "t=0"
		factor $n >> steps.csv
	fi
	let "t=$t+1"
done


@echo off
setlocal enabledelayedexpansion

	::Set the inputs
set "code=17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1"
set "n=2"

	::Basic validation of code
for %%. in (!code!) do (
	echo.%%.|findstr /r /c:"^[0-9][0-9]*/[1-9][0-9]*$">nul||goto error_code
)
	::Validate the input
set /a "tst=1*!n!" 2>nul
if !tst! lss 0 goto error_input
if !tst! equ 0 (if not "!n!"=="0" (goto error_input))

	::Set the limit outputs
	set limit=20

	::Execute the code
echo.Input:
echo.	!n!
echo.Output:
for /l %%? in (1,1,!limit!) do (
	set shouldwehalt=1
	for %%A in (!code!) do (
		for /f "tokens=1,2 delims=/" %%B in ("%%A") do (
			set /a "tst=!n! %% %%C"
			if !tst! equ 0 (
				if !shouldwehalt! equ 1 (
					set shouldwehalt=0
					set /a "n=n*%%B/%%C"
					echo.	!n!
				)
			)
		)
	)
	if !shouldwehalt! equ 1 goto halt
)

:halt
echo.
pause
exit /b 0

:error_code
echo.Syntax error in code.
echo.
pause
exit /b 1

:error_input
echo.Invalid input.
echo.
pause
exit /b 1


#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct IntArray_t {
    int *ptr;
    size_t length;
} IntArray;

IntArray make(size_t size) {
    IntArray temp;
    temp.ptr = calloc(size, sizeof(int));
    temp.length = size;
    return temp;
}

void destroy(IntArray *ia) {
    if (ia->ptr != NULL) {
        free(ia->ptr);

        ia->ptr = NULL;
        ia->length = 0;
    }
}

void zeroFill(IntArray dst) {
    memset(dst.ptr, 0, dst.length * sizeof(int));
}

int indexOf(const int n, const IntArray ia) {
    size_t i;
    for (i = 0; i < ia.length; i++) {
        if (ia.ptr[i] == n) {
            return i;
        }
    }
    return -1;
}

bool getDigits(int n, int le, IntArray digits) {
    while (n > 0) {
        int r = n % 10;
        if (r == 0 || indexOf(r, digits) >= 0) {
            return false;
        }
        le--;
        digits.ptr[le] = r;
        n /= 10;
    }
    return true;
}

int removeDigit(IntArray digits, size_t le, size_t idx) {
    static const int POWS[] = { 1, 10, 100, 1000, 10000 };
    int sum = 0;
    int pow = POWS[le - 2];
    size_t i;
    for (i = 0; i < le; i++) {
        if (i == idx) continue;
        sum += digits.ptr[i] * pow;
        pow /= 10;
    }
    return sum;
}

int main() {
    int lims[4][2] = { { 12, 97 }, { 123, 986 }, { 1234, 9875 }, { 12345, 98764 } };
    int count[5] = { 0 };
    int omitted[5][10] = { {0} };
    size_t upperBound = sizeof(lims) / sizeof(lims[0]);
    size_t i;

    for (i = 0; i < upperBound; i++) {
        IntArray nDigits = make(i + 2);
        IntArray dDigits = make(i + 2);
        int n;

        for (n = lims[i][0]; n <= lims[i][1]; n++) {
            int d;
            bool nOk;

            zeroFill(nDigits);
            nOk = getDigits(n, i + 2, nDigits);
            if (!nOk) {
                continue;
            }
            for (d = n + 1; d <= lims[i][1] + 1; d++) {
                size_t nix;
                bool dOk;

                zeroFill(dDigits);
                dOk = getDigits(d, i + 2, dDigits);
                if (!dOk) {
                    continue;
                }
                for (nix = 0; nix < nDigits.length; nix++) {
                    int digit = nDigits.ptr[nix];
                    int dix = indexOf(digit, dDigits);
                    if (dix >= 0) {
                        int rn = removeDigit(nDigits, i + 2, nix);
                        int rd = removeDigit(dDigits, i + 2, dix);
                        if ((double)n / d == (double)rn / rd) {
                            count[i]++;
                            omitted[i][digit]++;
                            if (count[i] <= 12) {
                                printf("%d/%d = %d/%d by omitting %d's\n", n, d, rn, rd, digit);
                            }
                        }
                    }
                }
            }
        }

        printf("\n");

        destroy(&nDigits);
        destroy(&dDigits);
    }

    for (i = 2; i <= 5; i++) {
        int j;

        printf("There are %d %d-digit fractions of which:\n", count[i - 2], i);

        for (j = 1; j <= 9; j++) {
            if (omitted[i - 2][j] == 0) {
                continue;
            }
            printf("%6d have %d's omitted\n", omitted[i - 2][j], j);
        }

        printf("\n");
    }

    return 0;
}


# Fractran interpreter

# Helpers
_while_ ← {𝔽⍟𝔾∘𝔽_𝕣_𝔾∘𝔽⍟𝔾𝕩}
ToInt ← 10⊸×⊸+˜´·⌽-⟜'0'
ToFrac ← {
  i ← ⊑/'/'=𝕩
  ToInt¨i(↑⋈1⊸+⊸↓)𝕩
}
Split ← ((¬-˜⊢×·+`»⊸>)∘≠⊔⊢)

Fractran ← {
  𝕊 n‿num‿den:
  ind ← ⊑/0=den|num×n
  ⟨(n×ind⊑num)÷ind⊑den ⋄ num ⋄ den⟩
}

RunFractran ← {
  steps 𝕊 inp‿prg:
  num‿den ← <˘⍉>ToFrac¨' 'Split prg
  step ← 1
  list ← ⟨inp⟩
  {
    step +↩ 1
    out ← Fractran 𝕩
    list ∾↩ ⊑out
    out
  } _while_ {𝕊 n‿num‿den: (step<steps)∧ ∨´0=den|num} inp‿num‿den
  list
}

seq ← 200 RunFractran 2‿"17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1"
•Out "Generated numbers: "∾•Repr seq
•Out "Primes: "∾•Repr 1↓⌊2⋆⁼(⌈=⌊)∘(2⊸(⋆⁼))⊸/ seq


using System;

namespace FractionReduction {
    class Program {
        static int IndexOf(int n, int[] s) {
            for (int i = 0; i < s.Length; i++) {
                if (s[i] == n) {
                    return i;
                }
            }
            return -1;
        }

        static bool GetDigits(int n, int le, int[] digits) {
            while (n > 0) {
                var r = n % 10;
                if (r == 0 || IndexOf(r, digits) >= 0) {
                    return false;
                }
                le--;
                digits[le] = r;
                n /= 10;
            }
            return true;
        }

        static int RemoveDigit(int[] digits, int le, int idx) {
            int[] pows = { 1, 10, 100, 1000, 10000 };

            var sum = 0;
            var pow = pows[le - 2];
            for (int i = 0; i < le; i++) {
                if (i == idx) continue;
                sum += digits[i] * pow;
                pow /= 10;

            }
            return sum;
        }

        static void Main() {
            var lims = new int[,] { { 12, 97 }, { 123, 986 }, { 1234, 9875 }, { 12345, 98764 } };
            var count = new int[5];
            var omitted = new int[5, 10];
            var upperBound = lims.GetLength(0);
            for (int i = 0; i < upperBound; i++) {
                var nDigits = new int[i + 2];
                var dDigits = new int[i + 2];
                var blank = new int[i + 2];
                for (int n = lims[i, 0]; n <= lims[i, 1]; n++) {
                    blank.CopyTo(nDigits, 0);
                    var nOk = GetDigits(n, i + 2, nDigits);
                    if (!nOk) {
                        continue;
                    }
                    for (int d = n + 1; d <= lims[i, 1] + 1; d++) {
                        blank.CopyTo(dDigits, 0);
                        var dOk = GetDigits(d, i + 2, dDigits);
                        if (!dOk) {
                            continue;
                        }
                        for (int nix = 0; nix < nDigits.Length; nix++) {
                            var digit = nDigits[nix];
                            var dix = IndexOf(digit, dDigits);
                            if (dix >= 0) {
                                var rn = RemoveDigit(nDigits, i + 2, nix);
                                var rd = RemoveDigit(dDigits, i + 2, dix);
                                if ((double)n / d == (double)rn / rd) {
                                    count[i]++;
                                    omitted[i, digit]++;
                                    if (count[i] <= 12) {
                                        Console.WriteLine("{0}/{1} = {2}/{3} by omitting {4}'s", n, d, rn, rd, digit);
                                    }
                                }
                            }
                        }
                    }
                }
                Console.WriteLine();
            }

            for (int i = 2; i <= 5; i++) {
                Console.WriteLine("There are {0} {1}-digit fractions of which:", count[i - 2], i);
                for (int j = 1; j <= 9; j++) {
                    if (omitted[i - 2, j] == 0) {
                        continue;
                    }
                    Console.WriteLine("{0,6} have {1}'s omitted", omitted[i - 2, j], j);
                }
                Console.WriteLine();
            }
        }
    }
}


(fractran=
  np n fs A Z fi P p N L M
.   !arg:(?N,?n,?fs)                 {Number of iterations, start n, fractions}
  & :?P:?L                           {Initialise accumulators.}
  &   whl
    ' ( -1+!N:>0:?N                  {Stop when counted down to zero.}
      & !n !L:?L                     {Prepend all numbers to result list.}
      & (2\L!n:#?p&!P !p:?P|)        {If log2(n) is rational, append it to list of primes.}
      & !fs:? (/?fi&!n*!fi:~/:?n) ?  {This line does the following (See task description):
                                      "for the first fraction, fi, in the list for which
                                       nfi is an integer, replace n by nfi ;"}
      )
  & :?M
  & whl'(!L:%?n ?L&!n !M:?M)         {Invert list of numbers. (append to long list is
                                      very expensive. Better to prepend and finally invert.}
  & (!M,!P)                          {Return the two lists}
);

( clk$:?t0
& fractran$(430000, 2, 17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1)
  : (?numbers,?primes)
& lst$(numbers,"numbers.lst",NEW)
& put$("
FRACTRAN found these primes:"
  !primes
  "\nThe list of numbers is saved in numbers.txt
The biggest number in the list is"
  (   0:?max
    & !numbers:? (>%@!max:?max&~) ?
  | !max
  )
str$("\ntime: " flt$(clk$+-1*!t0,4) " sec\n")
, "FRACTRAN.OUT",NEW)
);


#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>

typedef struct frac_s *frac;
struct frac_s {
	int n, d;
	frac next;
};

frac parse(char *s)
{
	int offset = 0;
	struct frac_s h = {0}, *p = &h;

	while (2 == sscanf(s, "%d/%d%n", &h.n, &h.d, &offset)) {
		s += offset;
		p = p->next = malloc(sizeof *p);
		*p = h;
		p->next = 0;
	}

	return h.next;
}

int run(int v, char *s)
{
	frac n, p = parse(s);
	mpz_t val;
	mpz_init_set_ui(val, v);

loop:	n = p;
	if (mpz_popcount(val) == 1)
		gmp_printf("\n[2^%d = %Zd]", mpz_scan1(val, 0), val);
	else
		gmp_printf(" %Zd", val);

	for (n = p; n; n = n->next) {
		// assuming the fractions are not reducible
		if (!mpz_divisible_ui_p(val, n->d)) continue;

		mpz_divexact_ui(val, val, n->d);
		mpz_mul_ui(val, val, n->n);
		goto loop;
	}

	gmp_printf("\nhalt: %Zd has no divisors\n", val);

	mpz_clear(val);
	while (p) {
		n = p->next;
		free(p);
		p = n;
	}

	return 0;
}

int main(void)
{
	run(2,	"17/91 78/85 19/51 23/38 29/33 77/29 95/23 "
		"77/19 1/17 11/13 13/11 15/14 15/2 55/1");

	return 0;
}


#include <array>
#include <iomanip>
#include <iostream>
#include <vector>

int indexOf(const std::vector<int> &haystack, int needle) {
    auto it = haystack.cbegin();
    auto end = haystack.cend();
    int idx = 0;
    for (; it != end; it = std::next(it)) {
        if (*it == needle) {
            return idx;
        }
        idx++;
    }
    return -1;
}

bool getDigits(int n, int le, std::vector<int> &digits) {
    while (n > 0) {
        auto r = n % 10;
        if (r == 0 || indexOf(digits, r) >= 0) {
            return false;
        }
        le--;
        digits[le] = r;
        n /= 10;
    }
    return true;
}

int removeDigit(const std::vector<int> &digits, int le, int idx) {
    static std::array<int, 5> pows = { 1, 10, 100, 1000, 10000 };

    int sum = 0;
    auto pow = pows[le - 2];
    for (int i = 0; i < le; i++) {
        if (i == idx) continue;
        sum += digits[i] * pow;
        pow /= 10;
    }
    return sum;
}

int main() {
    std::vector<std::pair<int, int>> lims = { {12, 97}, {123, 986}, {1234, 9875}, {12345, 98764} };
    std::array<int, 5> count;
    std::array<std::array<int, 10>, 5> omitted;

    std::fill(count.begin(), count.end(), 0);
    std::for_each(omitted.begin(), omitted.end(),
        [](auto &a) {
            std::fill(a.begin(), a.end(), 0);
        }
    );

    for (size_t i = 0; i < lims.size(); i++) {
        std::vector<int> nDigits(i + 2);
        std::vector<int> dDigits(i + 2);

        for (int n = lims[i].first; n <= lims[i].second; n++) {
            std::fill(nDigits.begin(), nDigits.end(), 0);
            bool nOk = getDigits(n, i + 2, nDigits);
            if (!nOk) {
                continue;
            }
            for (int d = n + 1; d <= lims[i].second + 1; d++) {
                std::fill(dDigits.begin(), dDigits.end(), 0);
                bool dOk = getDigits(d, i + 2, dDigits);
                if (!dOk) {
                    continue;
                }
                for (size_t nix = 0; nix < nDigits.size(); nix++) {
                    auto digit = nDigits[nix];
                    auto dix = indexOf(dDigits, digit);
                    if (dix >= 0) {
                        auto rn = removeDigit(nDigits, i + 2, nix);
                        auto rd = removeDigit(dDigits, i + 2, dix);
                        if ((double)n / d == (double)rn / rd) {
                            count[i]++;
                            omitted[i][digit]++;
                            if (count[i] <= 12) {
                                std::cout << n << '/' << d << " = " << rn << '/' << rd << " by omitting " << digit << "'s\n";
                            }
                        }
                    }
                }
            }
        }

        std::cout << '\n';
    }

    for (int i = 2; i <= 5; i++) {
        std::cout << "There are " << count[i - 2] << ' ' << i << "-digit fractions of which:\n";
        for (int j = 1; j <= 9; j++) {
            if (omitted[i - 2][j] == 0) {
                continue;
            }
            std::cout << std::setw(6) << omitted[i - 2][j] << " have " << j << "'s omitted\n";
        }
        std::cout << '\n';
    }

    return 0;
}


#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <cmath>

using namespace std;

class fractran
{
public:
    void run( std::string p, int s, int l  )
    {
        start = s; limit = l;
        istringstream iss( p ); vector<string> tmp;
        copy( istream_iterator<string>( iss ), istream_iterator<string>(), back_inserter<vector<string> >( tmp ) );

        string item; vector< pair<float, float> > v;
	pair<float, float> a;
	for( vector<string>::iterator i = tmp.begin(); i != tmp.end(); i++ )
	{
	    string::size_type pos = ( *i ).find( '/', 0 );
	    if( pos != std::string::npos )
	    {
		a = make_pair( atof( ( ( *i ).substr( 0, pos ) ).c_str() ), atof( ( ( *i ).substr( pos + 1 ) ).c_str() ) );
		v.push_back( a );
	    }
	}

	exec( &v );
    }

private:
    void exec( vector< pair<float, float> >* v )
    {
	int cnt = 0;
	while( cnt < limit )
	{
	    cout << cnt << " : " << start << "\n";
	    cnt++;
	    vector< pair<float, float> >::iterator it = v->begin();
	    bool found = false; float r;
	    while( it != v->end() )
	    {
		r  = start * ( ( *it ).first / ( *it ).second );
		if( r == floor( r ) )
		{
		    found = true;
		    break;
		}
		++it;
	    }

	    if( found ) start = ( int )r;
	    else break;
	}
    }
    int start, limit;
};
int main( int argc, char* argv[] )
{
    fractran f; f.run( "17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1", 2, 15 );
    cin.get();
    return 0;
}


import std.range;
import std.stdio;

int indexOf(Range, Element)(Range haystack, scope Element needle)
if (isInputRange!Range) {
    int idx;
    foreach (straw; haystack) {
        if (straw == needle) {
            return idx;
        }
        idx++;
    }
    return -1;
}

bool getDigits(int n, int le, int[] digits) {
    while (n > 0) {
        auto r = n % 10;
        if (r == 0 || indexOf(digits, r) >= 0) {
            return false;
        }
        le--;
        digits[le] = r;
        n /= 10;
    }
    return true;
}

int removeDigit(int[] digits, int le, int idx) {
    enum pows = [ 1, 10, 100, 1_000, 10_000 ];

    int sum = 0;
    auto pow = pows[le - 2];
    for (int i = 0; i < le; i++) {
        if (i == idx) continue;
        sum += digits[i] * pow;
        pow /= 10;
    }
    return sum;
}

void main() {
    auto lims = [ [ 12, 97 ], [ 123, 986 ], [ 1234, 9875 ], [ 12345, 98764 ] ];
    int[5] count;
    int[10][5] omitted;
    for (int i = 0; i < lims.length; i++) {
        auto nDigits = new int[i + 2];
        auto dDigits = new int[i + 2];
        for (int n = lims[i][0]; n <= lims[i][1]; n++) {
            nDigits[] = 0;
            bool nOk = getDigits(n, i + 2, nDigits);
            if (!nOk) {
                continue;
            }
            for (int d = n + 1; d <= lims[i][1] + 1; d++) {
                dDigits[] = 0;
                bool dOk = getDigits(d, i + 2, dDigits);
                if (!dOk) {
                    continue;
                }
                for (int nix = 0; nix < nDigits.length; nix++) {
                    auto digit = nDigits[nix];
                    auto dix = indexOf(dDigits, digit);
                    if (dix >= 0) {
                        auto rn = removeDigit(nDigits, i + 2, nix);
                        auto rd = removeDigit(dDigits, i + 2, dix);
                        if (cast(double)n / d == cast(double)rn / rd) {
                            count[i]++;
                            omitted[i][digit]++;
                            if (count[i] <= 12) {
                                writefln("%d/%d = %d/%d by omitting %d's", n, d, rn, rd, digit);
                            }
                        }
                    }
                }
            }
        }
        writeln;
    }

    for (int i = 2; i <= 5; i++) {
        writefln("There are %d %d-digit fractions of which:", count[i - 2], i);
        for (int j = 1; j <= 9; j++) {
            if (omitted[i - 2][j] == 0) {
                continue;
            }
            writefln("%6s have %d's omitted", omitted[i - 2][j], j);
        }
        writeln;
    }
}


(defun fractran (n frac-list)
  (lambda ()
    (prog1
      n
      (when n
        (let ((f (find-if (lambda (frac)
                            (integerp (* n frac)))
                          frac-list)))
          (when f (setf n (* f n))))))))

;; test

(defvar *primes-ft* '(17/91 78/85 19/51 23/38 29/33 77/29 95/23
                      77/19 1/17 11/13 13/11 15/14 15/2 55/1))

(loop with fractran-instance = (fractran 2 *primes-ft*)
      repeat 20
      for next = (funcall fractran-instance)
      until (null next)
      do (print next))


import std.stdio, std.algorithm, std.conv, std.array;

void fractran(in string prog, int val, in uint limit) {
    const fracts = prog.split.map!(p => p.split("/").to!(int[])).array;

    foreach (immutable n; 0 .. limit) {
        writeln(n, ": ", val);
        const found = fracts.find!(p => val % p[1] == 0);
        if (found.empty)
            break;
        val = found.front[0] * val / found.front[1];
    }
}

void main() {
    fractran("17/91 78/85 19/51 23/38 29/33 77/29 95/23
              77/19 1/17 11/13 13/11 15/14 15/2 55/1", 2, 15);
}


package main

import (
    "fmt"
    "time"
)

func indexOf(n int, s []int) int {
    for i, j := range s {
        if n == j {
            return i
        }
    }
    return -1
}

func getDigits(n, le int, digits []int) bool {
    for n > 0 {
        r := n % 10
        if r == 0 || indexOf(r, digits) >= 0 {
            return false
        }
        le--
        digits[le] = r
        n /= 10
    }
    return true
}

var pows = [5]int{1, 10, 100, 1000, 10000}

func removeDigit(digits []int, le, idx int) int {
    sum := 0
    pow := pows[le-2]
    for i := 0; i < le; i++ {
        if i == idx {
            continue
        }
        sum += digits[i] * pow
        pow /= 10
    }
    return sum
}

func main() {
    start := time.Now()
    lims := [5][2]int{
        {12, 97},
        {123, 986},
        {1234, 9875},
        {12345, 98764},
        {123456, 987653},
    }
    var count [5]int
    var omitted [5][10]int
    for i, lim := range lims {
        nDigits := make([]int, i+2)
        dDigits := make([]int, i+2)
        blank := make([]int, i+2)
        for n := lim[0]; n <= lim[1]; n++ {
            copy(nDigits, blank)
            nOk := getDigits(n, i+2, nDigits)
            if !nOk {
                continue
            }
            for d := n + 1; d <= lim[1]+1; d++ {
                copy(dDigits, blank)
                dOk := getDigits(d, i+2, dDigits)
                if !dOk {
                    continue
                }
                for nix, digit := range nDigits {
                    if dix := indexOf(digit, dDigits); dix >= 0 {
                        rn := removeDigit(nDigits, i+2, nix)
                        rd := removeDigit(dDigits, i+2, dix)
                        if float64(n)/float64(d) == float64(rn)/float64(rd) {
                            count[i]++
                            omitted[i][digit]++
                            if count[i] <= 12 {
                                fmt.Printf("%d/%d = %d/%d by omitting %d's\n", n, d, rn, rd, digit)
                            }
                        }
                    }
                }
            }
        }
        fmt.Println()
    }

    for i := 2; i <= 6; i++ {
        fmt.Printf("There are %d %d-digit fractions of which:\n", count[i-2], i)
        for j := 1; j <= 9; j++ {
            if omitted[i-2][j] == 0 {
                continue
            }
            fmt.Printf("%6d have %d's omitted\n", omitted[i-2][j], j)
        }
        fmt.Println()
    }
    fmt.Printf("Took %s\n", time.Since(start))
}


program FractranTest;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.RegularExpressions;

type
  TFractan = class
  private
    limit: Integer;
    num, den: TArray<Integer>;
    procedure compile(prog: string);
    procedure exec(val: Integer);
    function step(val: Integer): integer;
    procedure dump();
  public
    constructor Create(prog: string; val: Integer);
  end;

{ TFractan }

constructor TFractan.Create(prog: string; val: Integer);
begin
  limit := 15;
  compile(prog);
  dump();
  exec(2);
end;

procedure TFractan.compile(prog: string);
var
  reg: TRegEx;
  m: TMatch;
begin
  reg := TRegEx.Create('\s*(\d*)\s*\/\s*(\d*)\s*');
  m := reg.Match(prog);
  while m.Success do
  begin
    SetLength(num, Length(num) + 1);
    num[high(num)] := StrToIntDef(m.Groups[1].Value, 0);

    SetLength(den, Length(den) + 1);
    den[high(den)] := StrToIntDef(m.Groups[2].Value, 0);

    m := m.NextMatch;
  end;
end;

procedure TFractan.exec(val: Integer);
var
  n: Integer;
begin
  n := 0;
  while (n < limit) and (val <> -1) do
  begin
    Writeln(n, ': ', val);
    val := step(val);
    inc(n);
  end;
end;

function TFractan.step(val: Integer): integer;
var
  i: integer;
begin
  i := 0;
  while (i < length(den)) and (val mod den[i] <> 0) do
    inc(i);
  if i < length(den) then
    exit(round(num[i] * val / den[i]));
  result := -1;
end;

procedure TFractan.dump();
var
  i: Integer;
begin
  for i := 0 to high(den) do
    Write(num[i], '/', den[i], ' ');
  Writeln;
end;

const
  DATA =
    '17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1';

begin
  TFractan.Create(DATA, 2).Free;
  Readln;
end.


defmodule Fractran do
  use Bitwise

  defp binary_to_ratio(b) do
    [_, num, den] = Regex.run(~r/(\d+)\/(\d+)/, b)
    {String.to_integer(num), String.to_integer(den)}
  end

  def load(program) do
    String.split(program) |> Enum.map(&binary_to_ratio(&1))
  end

  defp step(_, []), do: :halt
  defp step(n, [f|fs]) do
    {p, q} = mulrat(f, {n, 1})
    case q do
        1 -> p
        _ -> step(n, fs)
    end
  end

  def exec(k, n, program) do
    exec(k-1, n, fn (_) -> true end, program, [n]) |> Enum.reverse
  end

  def exec(k, n, pred, program) do
    exec(k-1, n, pred, program, [n]) |> Enum.reverse
  end

  defp exec(0, _, _, _, steps), do: steps
  defp exec(k, n, pred, program, steps) do
    case step(n, program) do
        :halt -> steps
        m -> if pred.(m), do: exec(k-1, m, pred, program, [m|steps]),
                        else: exec(k, m, pred, program, steps)
    end
  end

  def is_pow2(n), do: band(n, n-1) == 0

  def lowbit(n), do: lowbit(n, 0)

  defp lowbit(n, k) do
    case band(n, 1) do
        0 -> lowbit(bsr(n, 1), k + 1)
        1 -> k
    end
  end

  # rational multiplication
  defp mulrat({a, b}, {c, d}) do
    {p, q} = {a*c, b*d}
    g = gcd(p, q)
    {div(p, g), div(q, g)}
  end

  defp gcd(a, 0), do: a
  defp gcd(a, b), do: gcd(b, rem(a, b))
end

primegen = Fractran.load("17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1")
IO.puts "The first few states of the Fractran prime automaton are:\n#{inspect Fractran.exec(20, 2, primegen)}\n"
prime = Fractran.exec(26, 2, &Fractran.is_pow2/1, primegen)
        |> Enum.map(&Fractran.lowbit/1)
        |> tl
IO.puts "The first few primes are:\n#{inspect prime}"


#! /usr/bin/escript

-mode(native).
-import(lists, [map/2, reverse/1]).

binary_to_ratio(B) ->
    {match, [_, Num, Den]} = re:run(B, "([0-9]+)/([0-9]+)"),
    {binary_to_integer(binary:part(B, Num)),
     binary_to_integer(binary:part(B, Den))}.

load(Program) ->
    map(fun binary_to_ratio/1, re:split(Program, "[ ]+")).

step(_, []) -> halt;
step(N, [F|Fs]) ->
    {P, Q} = mulrat(F, {N, 1}),
    case Q of
        1 -> P;
        _ -> step(N, Fs)
    end.

exec(K, N, Program) -> reverse(exec(K - 1, N, fun (_) -> true end, Program, [N])).
exec(K, N, Pred, Program) -> reverse(exec(K - 1, N, Pred, Program, [N])).

exec(0, _, _, _, Steps) -> Steps;
exec(K, N, Pred, Program, Steps) ->
    case step(N, Program) of
        halt -> Steps;
        M -> case Pred(M) of
                true  -> exec(K - 1, M, Pred, Program, [M|Steps]);
                false -> exec(K, M, Pred, Program, Steps)
            end
    end.

is_pow2(N) -> N band (N - 1) =:= 0.

lowbit(N) -> lowbit(N, 0).
lowbit(N, K) ->
    case N band 1 of
        0 -> lowbit(N bsr 1, K + 1);
        1 -> K
    end.

main(_) ->
    PrimeGen = load("17/91 78/85 19/51 23/38 29/33 77/29 95/23 77/19 1/17 11/13 13/11 15/14 15/2 55/1"),
    io:format("The first few states of the Fractran prime automaton are: ~p~n~n", [exec(20, 2, PrimeGen)]),
    io:format("The first few primes are: ~p~n", [tl(map(fun lowbit/1, exec(26, 2, fun is_pow2/1, PrimeGen)))]).

% rational multiplication

mulrat({A, B}, {C, D}) ->
    {P, Q} = {A*C, B*D},
    G = gcd(P, Q),
    {P div G, Q div G}.

gcd(A, 0) -> A;
gcd(A, B) -> gcd(B, A rem B).


class FractionReduction {
    static void main(String[] args) {
        for (int size = 2; size <= 5; size++) {
            reduce(size)
        }
    }

    private static void reduce(int numDigits) {
        System.out.printf("Fractions with digits of length %d where cancellation is valid.  Examples:%n", numDigits)

        //  Generate allowed numerator's and denominator's
        int min = (int) Math.pow(10, numDigits - 1)
        int max = (int) Math.pow(10, numDigits) - 1
        List<Integer> values = new ArrayList<>()
        for (int number = min; number <= max; number++) {
            if (isValid(number)) {
                values.add(number)
            }
        }

        Map<Integer, Integer> cancelCount = new HashMap<>()
        int size = values.size()
        int solutions = 0
        for (int nIndex = 0; nIndex < size - 1; nIndex++) {
            int numerator = values.get(nIndex)
            //  Must be proper fraction
            for (int dIndex = nIndex + 1; dIndex < size; dIndex++) {
                int denominator = values.get(dIndex)
                for (int commonDigit : digitsInCommon(numerator, denominator)) {
                    int numRemoved = removeDigit(numerator, commonDigit)
                    int denRemoved = removeDigit(denominator, commonDigit)
                    if (numerator * denRemoved == denominator * numRemoved) {
                        solutions++
                        cancelCount.merge(commonDigit, 1, { v1, v2 -> v1 + v2 })
                        if (solutions <= 12) {
                            println("    When $commonDigit is removed, $numerator/$denominator = $numRemoved/$denRemoved")
                        }
                    }
                }
            }
        }
        println("Number of fractions where cancellation is valid = $solutions.")
        List<Integer> sorted = new ArrayList<>(cancelCount.keySet())
        Collections.sort(sorted)
        for (int removed : sorted) {
            println("    The digit $removed was removed ${cancelCount.get(removed)} times.")
        }
        println()
    }

    private static int[] powers = [1, 10, 100, 1000, 10000, 100000]

    //  Remove the specified digit.
    private static int removeDigit(int n, int removed) {
        int m = 0
        int pow = 0
        while (n > 0) {
            int r = n % 10
            if (r != removed) {
                m = m + r * powers[pow]
                pow++
            }
            n /= 10
        }
        return m
    }

    //  Assumes no duplicate digits individually in n1 or n2 - part of task
    private static List<Integer> digitsInCommon(int n1, int n2) {
        int[] count = new int[10]
        List<Integer> common = new ArrayList<>()
        while (n1 > 0) {
            int r = n1 % 10
            count[r] += 1
            n1 /= 10
        }
        while (n2 > 0) {
            int r = n2 % 10
            if (count[r] > 0) {
                common.add(r)
            }
            n2 /= 10
        }
        return common
    }

    //  No repeating digits, no digit is zero.
    private static boolean isValid(int num) {
        int[] count = new int[10]
        while (num > 0) {
            int r = num % 10
            if (r == 0 || count[r] == 1) {
                return false
            }
            count[r] = 1
            num /= 10
        }
        return true
    }
}


USING: io kernel math math.functions math.parser multiline
prettyprint sequences splitting ;
IN: rosetta-code.fractran

STRING: fractran-string
17/91 78/85 19/51 23/38 29/33 77/29 95/23
77/19 1/17 11/13 13/11 15/14 15/2 55/1
;

: fractran-parse ( str -- seq )
    " \n" split [ string>number ] map ;

: fractran-step ( seq n -- seq n'/f )
    2dup [ * integer? ] curry find nip dup [ * ] [ nip ] if ;

: fractran-run-full ( seq n -- )
    [ dup ] [ dup . fractran-step ] while 2drop ;

: fractran-run-limited ( seq n steps -- )
    [ dup pprint bl fractran-step ] times 2drop nl ;

: fractran-primes ( #primes seq n -- )
    [ pick zero? ] [
        dup 2 logn dup [ floor 1e-9 ~ ] [ 1. = not ] bi and [
            dup 2 logn >integer pprint bl [ 1 - ] 2dip
        ] when fractran-step
    ] until 3drop nl ;

: main ( -- )
    fractran-string fractran-parse 2
    [ "First 20 numbers: " print 20 fractran-run-limited nl ]
    [ "First 20 primes: " print [ 20 ] 2dip fractran-primes ]
    2bi ;

MAIN: main


Func FT( arr, n, m ) =
    ;{executes John H. Conway's FRACTRAN language for a program stored in [arr], an}
    ;{input integer stored in n, for a maximum of m steps}
    ;{To allow the program to run indefinitely, give it negative or noninteger m}
    exec:=1;                 {boolean to track whether the program needs to halt}
    len:=Cols[arr];          {length of the input program}
    while exec=1 and m<>0 do
        m:-;
        !!n;                 {output the memory}
        i:=1;                {index variable}
        exec:=0;
        while i<=len and exec=0 do
            nf:=n*arr[i];
            if Denom(nf) = 1 then
                n:=nf;       {did we find an instruction to execute?}
                exec:=1
            fi;
            i:+;
        od;
    od;
.;

;{Here is the program to run}
[arr]:=[( 17/91,78/85,19/51,23/38,29/33,77/29,95/23,77/19,1/17,11/13,13/11,15/14,15/2,55/1 )];

FT( [arr], 2, 20 );