4

I am bit confused on how to solve this problem. I need all the prime numbers to return true. If not return false--I see that my logic is including 2 and that returns 0 so it automatically returns false, because 2 has a remainder of 0.

  

  function isPrime(num, div = 2) {
      // BASE CASE: 
     
      if(num <= div ) return false; // IF num less than OR equal to  2 RETURN false 
      
      // IF num MOD has a remainder of zero   
         
      if(num % 2 === 0) return false  // RETURN false 
      
      return true; // RETURN true
     
      // RECURSIVE CALL:

      return isPrime(num)
    }

    console.log(isPrime(1)); //-> false
    console.log(isPrime(2)); //-> true
    console.log(isPrime(3)); //-> true
    console.log(isPrime(4)); //-> false
Will Ness
  • 62,652
  • 8
  • 86
  • 167

3 Answers3

3

There is no need for recursion. Just test all odd integers from 3 to the square root of the number as possible factors for the best performance.

function isPrime(num){
      if(num === 2) return true;
      if(num < 2 || num % 2 === 0) return false;
      for(let i = 3; i * i <= num; i += 2)
          if(num % i === 0) return false;
      return true;
}

If you really need to use recursion, the above idea can be implemented by accepting a factor as the second argument and incrementing it by 2 when recursing.

function isPrime(num, div = 3){
      if(num === 2) return true;
      if(num < 2 || num % 2 === 0)  return false;
      if(div * div > num) return true;
      if(num % div === 0) return false;
      return isPrime(num, div + 2);
}
iota
  • 34,586
  • 7
  • 32
  • 51
2

I came for the recursion, but Thankyou already has an excellent answer for how to deal with this recursively using some useful abstractions.

That answer used a version of the Sieve of Eratosthenes. I'd just like to point out another version of that sieve, one less elegant but I believe quite a bit more performant.

The basic implementation is this generator function:

const sieve = function * () {
    const C = {} // known composite numbers
    let q = 2
    while (q < Infinity) {
        if (C [q]) {
            C [q] .forEach (n => C [n + q] = [... (C [n + q] || []), n]  )
            delete C [q]
        } else {
            C [2 * q] = [q]
            yield q
        }
        q += 1
    }
}

The Sieve wants us to cross off all the multiples of a given prime. This is fine if we start with a fixed list of numbers, but here we want to deal with streams of values, with no fixed upper bound. Instead, at any multiple of a prime, we cross off only the next multiple that we would hit. We track this with the local variable C, which holds a bit of information about prime multiples.

Let's say we've reached a test value of 11. Then we've already seen the primes of 2, 3, 5, and 7. The next multiple of 2 is 12; the next multiple of 3 is also 12, the next multiple of 5 is 15 and the next multiple of 7 is 14. At this point C will look like this:

{
  12: [3, 2],
  14: [7],
  15: [5]
}

Because 11 is not in this list, we add 2 * 11 to C, and yield it back as our next prime. So when we check 12, C will look like this:

{
  12: [3, 2],
  14: [7],
  15: [5],
  22: [11]
}

But now 12 is on this object, so 12 is not prime. And we have to do some more adjusting. We're going to remove 12 from that list, but we have to place our 2 and 3 values on their next multiples. The next multiple of 2 after 12 is 14, so we'll update the value at 14 to include 2. The next value multiple of 3 after 12 is 15, so we'll update the value at 15 to include 3.

And now, when we hit 13, C holds

{
  14: [7, 2],
  15: [5, 3],
  22: [11]
}

And because 13 is not in C we know that it's prime. We will will yield it and add 26 (2 * 13) to C with the value 13. And this will lead us to a continuing stream of prime numbers. The state we will maintain is simply the collection of next multiples of each prime found so far.

A sharp eye might have noticed some extra work going on here. When adding the first multiple of, say, 7 to our list, we don't really need to start at 14; that will already be crossed off as a multiple of 2. And 21 is a multiple of 3, 28 a multiple of 4 (and hence of 2), 35 a multiple of 5, and 42 a multiple of 6 (and hence of 2 and 3.) The first multiple we need to check specifically is 49. And in general, when we add a prime, p, the first multiple we will need to check is p ** 2. We can fix that by replacing this:

            C [2 * q] = [q]

with this:

            C [q * q] = [q]

We can test this function like this:

const iterator = sieve()

console.log(iterator.next()) //=> {value: 2, done: false}
console.log(iterator.next()) //=> {value: 3, done: false}
console.log(iterator.next()) //=> {value: 5, done: false}
console.log(iterator.next()) //=> {value: 7, done: false}
console.log(iterator.next()) //=> {value: 11, done: false}
console.log(iterator.next()) //=> {value: 13, done: false}

And we could write a take function as seen in Thankyou's answer. And that's probably the best way to handle this. But we can also alter this function to end either after n results ("find the first n primes") or when the value exceeds n ("find all the primes up to n".)

The latter is trivial: just add the parameter max and replace our use of Infinity with it. The former is only slightly more difficult. Here's one version:

const sieve = function * (count) {
    const C = {} // known composite numbers
    let n = 0
    let q = 2
    while (n < count) {
         if (C [q]) {
            C [q] .forEach (n => C [n + q] = [... (C [n + q] || []), n]  )
            delete C [q]
        } else {
            C [q * q] = [q]
            n += 1
            yield q
        }
        q += 1
    }
};

console .log ([... sieve(1000)])
.as-console-wrapper {max-height: 100% !important; top: 0}

Logically, our composites internal data structure would not be an Object mapping integers to arrays of integers. Clearly the most appropriate structure would be a Map mapping integers to Sets of integers. We can certainly write it that way:

const sieve = function * (count) {
    const C = new Map () // known composite numbers
    let n = 0
    let q = 2
    while (n < count) {
        if (C .has (q)) {
            [... C .get (q)] .forEach (
              n => C .set (n + q, (C .get (n + q) || new Set ()) .add (n))
            )
            C .delete (q)
        } else {
            C .set (q * q, new Set ([q]))
            n += 1;
            yield q
        }
        q += 1
    }
};

For one reason or another I find this less readable than its Object-Array counterpart. I haven't tested performance, but I would be surprised if this is significantly faster (or slower.) But it works the same way.

Noe of this is written in my usual style. While we could probably achieve something that avoids mutating an internal structure by using recursion, no technique leaps to mind.


More information about this version of the Sieve and its relative complexity is in the wonderful The Genuine Sieve of Eratosthenes, by Melissa E. O’Neill. The paper is academic, but still quite readable. And if you can read Haskell it should be easy to take in.

Scott Sauyet
  • 37,179
  • 4
  • 36
  • 82
  • 1
    I love this implementation. The explanation is clear and, yes, it crushes in terms of performance, haha :D – Thank you Jul 28 '20 at 15:28
  • it's always more readable with syntax instead of words because we can just see syntax but words we must read and think and build a mental picture about. verbal thought is very limited but vision is massively parallel highly performant subsystem. FYI https://stackoverflow.com/a/10733621 gives further improvement to this algorithm, if you haven't seen it already. if you read Haskell, :) in terms of clarity nothing beats ``primes = 2 : 3 : [5,7..] `O.minus` O.unionAll [[p*p, p*p+2*p ..] | p – Will Ness Jul 28 '20 at 20:00
  • @WillNess: I decided for simplicity not to go with a wheel. If I wanted this for a practical purpose, I would look at further optimizations, such as your version or a 2-3-5-7 wheel. But that affects only the coefficients, correct, not the algorithmic size? As for clarity, I would say it's really hard to beat `primes = sieve [2..]` / `sieve (p : xs) = p : sieve [x | x 0]`. But I now that if I want to discuss it, I can just mention generating primes on SO and I will summon Will Ness! – Scott Sauyet Jul 28 '20 at 20:36
  • @ScottSauyet ah yes that *is* shorter, but its performance is terrible and it's not the sieve of Eratosthenes but rather of trial division, and sub-optimal (re: [sqrt](https://stackoverflow.com/a/8871918/849891)) at that. --- what I meant with the linked entry was not the wheel (which gives only a constant factor improvement, yes) but the postponement, to not overproduce the multiples by such a huge margin as they are right now, getting a significant ("reverse" quadratic) reduction in space complexity, and depending on your language's hash/map/lists performance, time as well. – Will Ness Jul 29 '20 at 04:38
  • @WillNess: Right. That was the point of the article I mentioned in the answer. At some point perhaps I will play with your version. Obviously the technique above uses memory somewhat below linear in the number of primes already seen. I know how to implement a wheel, and your code gives a sample for how to implement your postponed version. But I'm still curious about the space and time complexity of your algorithm. Is it substantially reduced from the above? – Scott Sauyet Jul 29 '20 at 12:38
  • No, as I understood it the article missed the postponement technique entirely. It states at one point that starting from square is a pointless optimization, which *is* true for the O(1) access arrays, but for list-based code it enables the postponement of multiples enumeration / filtering, so that the number of sieving streams goes down from pi(n) to pi(sqrt(n)) - i.e. *extremely drastically*. Yes, the space goes from O(n) to O(sqrt n), give or take some log factors (the "reverse quadratic" remark in my comment above). do consult the two answers, 8871918 and 10733621. :) (contd.) – Will Ness Jul 29 '20 at 15:24
  • specifically, that "unfaithful" sieve is *O(n^2)* in *n* primes produced, but with the postponement it becomes something like *O(n^1.5 / (log n)^0.5)* IIRC. For the true sieve of Eratosthenes starting from squares enables the code (which I showed) which seems to be only *log n* worse than the optimal *O(n log n log log n)*. M.O'Neil later added a variant which is equivalent to this "postponement". see e.g. [this](https://stackoverflow.com/a/13895347/849891) for more. in 10733621 I go further and instead of the inner loop make a tower of recursive calls (*log log n* of them). it doesn't 2/ – Will Ness Jul 29 '20 at 15:32
  • doesn't change the complexity but is kind of slick. :) /end --- (ah, you meant the point of the article was that the performance was terrible. right, but it didn't explain why it was _so_ terrible, i.e. quadratic where with the postponement it's roughly n^1.5; in practice this means *immense* speedup and no stack overflows) – Will Ness Jul 29 '20 at 15:41
  • @WillNess: At some point, I will have to dig into this to understand what you're doing, but as I'm trying to get everything in place at home and at work before leaving this weekend on a two-week vacation, it's not likely to be soon. – Scott Sauyet Jul 29 '20 at 16:08
  • 1
    mmmm vacation. :) happy trails! (when you do, start [here](https://stackoverflow.com/a/8871918/849891)). – Will Ness Jul 29 '20 at 16:36
1

Recursion is fun! Let's see how we can compute prime numbers by imagining an emptyStream, the ability to create a new stream and the ability to filter a stream -

import { stream, emptyStream, filter } from "./Stream"

const numbers = (n = 0) =>
  stream(n, _ => numbers(n + 1))

const sieve = (t = numbers(2)) =>
  t === emptyStream
    ? t
    : stream
        ( t.value
        , _ =>
            sieve
              ( filter
                  ( t.next
                  , v => v % t.value !== 0
                  )
              )
        )

This technique is known as the Sieve of Eratosthenes. Using additional stream functions we will take the first 1,000 items and convert the result toArray -

import { take, toArray } from "./Stream"

const result = 
  toArray(take(sieve(), 1000))
  
console.log(JSON.stringify(result))
// [2,3,5,7,11,13,17,19,23, ... ,7901,7907,7919]

You can implement Stream however you like. Here's one possible implementation -

// Stream.js

const emptyStream =
  Symbol('emptyStream')

const stream = (value, next) =>
  ( { value
    , get next ()
      { delete this.next
      ; return this.next = next()
      }
    }
  )

const filter = (t = emptyStream, f = identity) =>
  t === emptyStream
    ? t
: Boolean(f(t.value))
    ? stream
        ( t.value
        , _ => filter(t.next, f)
        )
: filter(t.next, f)

const take = (t = emptyStream, n = 0) =>
  t === emptyStream || n <= 0
    ? emptyStream
    : stream(t.value, _ => take(t.next, n - 1))
    
const toArray = (t = emptyStream) =>
  t === emptyStream
    ? []
    : [ t.value, ...toArray(t.next) ]

export { emptyStream, stream, filter, take, toArray }

Expand the snippet below to compute the first 1,000 primes in your browser -

// Stream.js
const emptyStream =
  Symbol('emptyStream')

const stream = (value, next) =>
  ( { value
    , get next ()
      { delete this.next
      ; return this.next = next()
      }
    }
  )
  
const filter = (t = emptyStream, f = identity) =>
  t === emptyStream
    ? t
: Boolean(f(t.value))
    ? stream
        ( t.value
        , _ => filter(t.next, f)
        )
: filter(t.next, f)

const take = (t = emptyStream, n = 0) =>
  t === emptyStream || n <= 0
    ? emptyStream
    : stream(t.value, _ => take(t.next, n - 1))
    
const toArray = (t = emptyStream) =>
  t === emptyStream
    ? []
    : [ t.value, ...toArray(t.next) ]

// Main.js
const numbers = (n = 0) =>
  stream(n, _ => numbers(n + 1))

const sieve = (t = numbers(2)) =>
  t === emptyStream
    ? t
    : stream
        ( t.value
        , _ =>
            sieve
              ( filter
                  ( t.next
                  , v => v % t.value !== 0
                  )
              )
        )

const result = 
  toArray(take(sieve(), 1000))
  
document.body.textContent = result.join(", ")

Output

2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001, 3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919, 3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051, 4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201, 4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327, 4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463, 4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603, 4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733, 4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903, 4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009, 5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153, 5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303, 5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441, 5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569, 5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701, 5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843, 5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987, 6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131, 6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269, 6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373, 6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553, 6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691, 6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829, 6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967, 6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109, 7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247, 7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451, 7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559, 7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687, 7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841, 7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919

Did that last program overflow your stack? No problem, because we used a module, our streams are separated by a barrier of abstraction. This allows us to make changes to Stream without affecting our Main module. The offenders can be modified to use a while loop if larger computations are necessary -

// Stream.js

const emptyStream = //...

const stream = //...

const take = // ...

function filter (t = emptyStream, f = identity)
{ while (t !== emptyStream)
    if (Boolean(f(t.value)))
      return stream
        ( t.value
        , _ => filter(t.next, f)
        )
    else
      t = t.next      // <-- advance stream t without recursion
  return t
}

function toArray (t = emptyStream)
{ let r = []
  while (t !== emptyStream)
    ( r.push(t.value)
    , t = t.next      // <-- advance stream t without recursion
    )
  return r
}
Thank you
  • 107,507
  • 28
  • 191
  • 224
  • This is an amazing technique. It's so efficient. I've never seen it like this before. Thanks – Woody P Lucas Jul 25 '20 at 14:44
  • I am baffled. Is that how modern Javascript should look like? I can read @hev1 answer but your code snippet looks like another language to me. – Poutrathor Jul 26 '20 at 12:20
  • Note that this can run the stack pretty quickly. In my browser (FF Dev, v77.0b6 on Ubuntu 18.04) this hits a recursion limit at `toArray(take(sieve(), 584))`. It works for 583. – Scott Sauyet Jul 26 '20 at 15:01
  • @Poutrathor: The layout of Thankyou's code is unusual, but if you spend any time with it, you will probably realize that it's quite logical. When looking at this version, remember that `stream`, `take`, `emptyStream`, `toArray` and `numbers` are all to be considered basic infrastructure. `filter` is too, but it bears investigation for the problem. The critical function here is `sieve`. If you understand that, it should all fall into place. – Scott Sauyet Jul 26 '20 at 15:09
  • @Poutrathor how your could _should_ look like is [entirely up to you](https://stackoverflow.com/a/46918344/633183). – Thank you Jul 26 '20 at 15:59
  • 1
    @ScottSauyet hmm, can't say I expected FF to overflow so quickly. I added an alternatively implementation of `Stream.filter` and `Stream.toArray` that may help. Thanks for the comment :) – Thank you Jul 26 '20 at 16:01
  • I added an answer with an alternate Sieve implementation. It's not nearly as elegant, but it's likely much more performant. – Scott Sauyet Jul 26 '20 at 18:50
  • 1
    to actually be the sieve of *Eratosthenes* it should be using something like `filter_multiples( t.next, t.value, t.value + t.value)`, implemented accordingly. (or with `t.value * t.value`, with further repercussions ...) – Will Ness Jul 28 '20 at 19:58
  • WillNess, interesting! I based mine off of my memory of the sieve presented in SICP, perhaps a simplified form? – Thank you Jul 28 '20 at 20:12
  • 1
    yes, simplified. compare with [this](https://rosettacode.org/wiki/Sieve_of_Eratosthenes#SICP-style_streams). the filter I had in mind is like [here](https://rosettacode.org/wiki/Sieve_of_Eratosthenes#Concurrent_Daisy-chain_sieve). see also [this](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#cite_note-12) and [this](https://stackoverflow.com/a/8871918/849891). – Will Ness Jul 28 '20 at 20:26