Increasing your range in Terraform

Day 1 Part 2

Continuing from my previous blog post where I solved the challenge of counting the amount of numbers who’s value is higher than it’s predecessor in a list. The next part of the challenge was to sum a sliding window of three values and count the number of sums that were higher than the predecessor.

My strategy for approaching this was first to identify the start index of each window, sum each each window, then find which sums are increments compared to the one before.

Counting the windows

The first part of this strategy appears easy enough by finding the length of the input, subtracting two as there will be two less windows than number of entries in the list, then making use of the range function to create a new list containing just the start indices of each window.

  
  window_start_indexes = range(length(local.input_as_number_list) - 2)
  

Using the example input this results in the following list

  
  0
  1
  2
  3
  4
  5
  6
  7

Sum the windows

Now I can use a for expression to iterate over the list of start indexes, lookup the corresponding value in the input list, and the two following values and sum them into a new list, like so;

  
  window_sums = [for i in local.window_start_indexes : sum([
    local.input_as_number_list[i],
    local.input_as_number_list[i + 1],
    local.input_as_number_list[i + 2],
  ])]
  

Rinse and repeat

At this point I now have a new list of numbers and can run the same logic on it as I did for the first part;

  
  window_increased_or_not = [for i, v in local.window_sums :
    i == 0 ? "" : (v - local.window_sums[i - 1] > 0 ? "increased" : "")
  ]
  output_2 = length(compact(local.window_increased_or_not))

Computer says no

While my solution worked fine on the small example input, when I tested on the real input file terraform gave me a little surprise;

  
  $ terraform refresh
  ╷
  │ Error: Error in function call
  │ 
  │   on main.tf line 20, in locals:
  │   20:   window_start_indexes = range(length(local.input_as_number_list) - 2)
  │     ├────────────────
  │     │ local.input_as_number_list is tuple with 2000 elements
  │ 
  │ Call to function "range" failed: more than 1024 values were generated; either decrease the difference between start and end
  │ or use a smaller step.
  ╵

Investigating the documentation it says

Terraform imposes an artificial limit of 1024 numbers in the resulting sequence in order to avoid unbounded memory usage

Which is unfortunate as the input file contains 2000 numbers so I need a list of 1998 start indices.

But wait, there’s more!

While this artificial limit makes sense to implement, it was quite confusing to stumble upon in the moment, as I already have a list containing 2000 numbers in memory from the parsing of the file. Why shouldn’t I be able to generate a list with 1998 numbers?

This realisation got me wondering if I can use the existing list to generate the start index list instead of using the range function. So first I use slice to create a list containing only the first 1998 values;

  
  window_start_values = slice(
    local.input_as_number_list,
    0,
    length(local.input_as_number_list) - 2
  )

But I only want the indexes, not the actual values. I can use another for expression for that, only returning the index for each value;

  
  window_start_indexes = [for i, v in local.window_start_values : i]

We’re back in business

With that last change Terraform finally spits out the correct answers

$ terraform refresh
╷
│ Warning: Empty or non-existent state
│ 
│ There are currently no resources tracked in the state, so there is nothing to refresh.
╵

Outputs:

output_1 = 1139
output_2 = 1103

I’m not sure how useful this trick will be in the future, but it was fun to break out of the artificial limits imposed by Terraform’s range function.