# Combining Rings Using Boolean Operations

**URL:** <https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303>\
**Category:** Support, Help & Resources\
**Created:** [November 18, 2024, 7:30am UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303 "2024-11-18T07:30:35Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Skoddie](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/skoddie/32/333_2.png) [@Skoddie](https://in-thread.sonic-pi.net/u/Skoddie)\
**Post date:** [November 18, 2024, 7:30am UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/1 "2024-11-18T07:30:35Z")

</div>

Hey there! After a long break I’m fiddling with Sonic Pi again.

I’m currently looking into defining rhythmic patterns as a series of boolean rings, some handwritten, some generated using `spread`. I’d like to eventually combine the rings together to output a final rhythm using boolean operators, but from what I can tell there’s no easy way to do this.

I’d expect to be able to do something like

```ruby
a = (spread 1, 8)
b = (spread 1, 8, rotate: 2)

c = a & b

```

but this returns a ring with 2 values and I’m not sure how they’re calculated. I could likely initialize a new ring, then loop through it comparing values in the first two to get the final output but this feels hacky.

Is there a simpler way to create a ring using boolean operations on two existing rings?

---

<div class="post-metadata">

**Author:** ![Skoddie](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/skoddie/32/333_2.png) [@Skoddie](https://in-thread.sonic-pi.net/u/Skoddie)\
**Post date:** [November 18, 2024, 7:51am UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/2 "2024-11-18T07:51:52Z")

</div>

I’ve created a function that implements what I’m looking for manually, but I feel like there should be a language construct for this?

```ruby
define :ring_OR do |ring_a, ring_b|
  length = ring_a.length
  working_list = Array.new
  i = 0
  
  length.times do
    working_list[i] = ring_a[i] | ring_b[i]
    i = (inc i)
  end
  
  return working_list.ring
  
end

## Example
a = (spread 1, 8)
b = (spread 1, 8).rotate(4)

c = ring_OR(a, b)

puts a
puts b
puts c

```

---

<div class="post-metadata">

**Author:** ![Skoddie](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/skoddie/32/333_2.png) [@Skoddie](https://in-thread.sonic-pi.net/u/Skoddie)\
**Post date:** [November 18, 2024, 8:09am UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/3 "2024-11-18T08:09:04Z")

</div>

Realize I’m on a 3rd reply here, but here’s a more comprehensive implementation. Still feels clunky.

```ruby
define :ring_bitwise do |ring_a, ring_b, operator|
  length = ring_a.length
  working_list = Array.new
  i = 0
  
  length.times do
    case operator
    
    when "or"
      working_list[i] = ring_a[i] | ring_b[i]
      
    when "and"
      working_list[i] = ring_a[i] & ring_b[i]
      
    when "xor"
      working_list[i] = ring_a[i] ^ ring_b[i]
      
    end
    
    i = (inc i)
  end
  
  return working_list.ring
  
end

## Example
a = (spread 2, 4)
b = (spread 2, 4).rotate(1)

c = ring_bitwise(a, b, "or")

puts "or"
puts a
puts b
puts c

a = (spread 2, 4)
b = (spread 4, 4)

c = ring_bitwise(a, b, "and")

puts "and"
puts a
puts b
puts c

a = (spread 2, 4)
b = (spread 4, 4)

c = ring_bitwise(a, b, "xor")

puts "xor"
puts a
puts b
puts c

```

---

<div class="post-metadata">

**Author:** ![brendanmac](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/brendanmac/32/2809_2.png) [@brendanmac](https://in-thread.sonic-pi.net/u/brendanmac)\
**Post date:** [November 18, 2024, 1:36pm UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/4 "2024-11-18T13:36:40Z")

</div>

Hi @Skoddie  
I think your OR function is the most concise. As a musician, and novice coder, I often find myself looking for ways to ‘merge’, or hybridise, various arrays into one. I have exhausted stackoverflow, and other web resources, and tried zip, flatten, compact etc. without success.

This is a musical _and_ coding challenge for me/us: how to combine several arrays, of length x, into one array, of the same length. As a novice coder I realise this is not a trivial operation, and your first reply looks like a clear and concise solution.

& returns the elements common to both arrays

```ruby
a = [1,2,3,4]
b = [2,3,4,5]

puts a&b # ==> [2,3,4]

```

Happy to be corrected though 😉

PD-Pi

---

<div class="post-metadata">

**Author:** ![Relaxnow](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/relaxnow/32/2172_2.png) [@Relaxnow](https://in-thread.sonic-pi.net/u/Relaxnow)\
**Post date:** [November 18, 2024, 8:51pm UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/5 "2024-11-18T20:51:23Z")

</div>

I use “+” between two spread() usually. What does “&” do?

```ruby

/ Two fuctions that shortens "spread() to s()" and "rotate() to r()"/
define :s do |a,b|
  spread(a,b)
end

class SonicPi::Core::RingVector # r() only works on s() - doesnt work on an array like [].r()
  def r(n)
    self.rotate(n)
  end
end

#Goal
# a = (spread 1, 8)
# b = (spread 1, 8, rotate: 2)
# c = a & b

use_bpm 60
live_loop :a1 do
  tick
  
  #sample :bd_fat if (s(1,8)+s(0,8).r(2)).look
  #sample :bd_fat, rpitch: 24 if (s(0,8)+s(1,8).r(2)).look
  
  sample :bd_fat if (s(3,5)*3+s(0,7).r(2)).look
  sample :bd_fat, rpitch: 24 if (s(0,5)*3+s(5,7).r(2)).look
  
  sleep 0.25
end

```

---

<div class="post-metadata">

**Author:** ![Skoddie](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/skoddie/32/333_2.png) [@Skoddie](https://in-thread.sonic-pi.net/u/Skoddie)\
**Post date:** [November 18, 2024, 9:34pm UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/6 "2024-11-18T21:34:08Z")

</div>

& is a bitwise AND comparison. Essentially I’m using rings as bitmasks, and then doing bitwise operations to get a final bitmask that I can use as a rhythm.

Using + will concatenate the rings, and as @brendanmac noted above & will only return common values.

The more I investigate this the more it seems there aren’t builtins. I’m considering reimplementing this without using rings, and then translating into a ring at the end due to performance issues.

In other languages, like C this construct is fairly easy.  
0b1000100010001000 | 0b1001001001001000 = 0b1001101011001000

This is also available in Ruby, I just need to represent it as decimal integers rather than binary values. It, unfortunately, feels inelegant, but it doesn’t seem like it’s currently within the scope of the language.

---

<div class="post-metadata">

**Author:** ![brendanmac](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/brendanmac/32/2809_2.png) [@brendanmac](https://in-thread.sonic-pi.net/u/brendanmac)\
**Post date:** [November 18, 2024, 11:09pm UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/7 "2024-11-18T23:09:39Z")

</div>

> [@Skoddie](#):
>
> In other languages, like C this construct is fairly easy.  
> 0b1000100010001000 | 0b1001001001001000 = 0b1001101011001000
> 
> This is also available in Ruby

How would I do bitwise OR in Ruby/SonicPi? For example:

```ruby
a = [1,0,0,0]
b = [0,0,1,0]
c = a | b

```

also removes duplicates, but I would like [1,0,1,0] for c.

Apologies for hijacking your post, but I’ve been chasing a solution for months.

PD-Pi

---

<div class="post-metadata">

**Author:** ![Skoddie](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/skoddie/32/333_2.png) [@Skoddie](https://in-thread.sonic-pi.net/u/Skoddie)\
**Post date:** [November 19, 2024, 1:58am UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/8 "2024-11-19T01:58:33Z")

</div>

You can do it natively in Ruby with ints by converting from binary to an int.

To rewrite my example from above:

34952 | 37448 = 36924

Then you could populate a ring by using `% 2` to pull out the LSB, put it into a ring, right shift and do it again until you run out of bits.

---

<div class="post-metadata">

**Author:** ![pashultz](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/pashultz/32/1964_2.png) [@pashultz](https://in-thread.sonic-pi.net/u/pashultz)\
**Post date:** [November 25, 2024, 9:46pm UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/9 "2024-11-25T21:46:55Z")

</div>

Just seeing this thread—I’ve had the same general thought but never got around to implementing it. The bitwise approach is clever!

My first thought is to use Ruby’s `map` method, which is much more concise than the explicit iteration in most of the code posted above:

```ruby
define :ringand do | c, d |
  l = c.length() - 1
  return (0..l).map { |x| c[x] & d[x] }.ring
end

define :ringor do | c, d |
  l = c.length() - 1
  return (0..l).map { |x| c[x] | d[x] }.ring
end

a = (spread 2, 16)
b = (spread 1, 3)

puts a
puts b
puts ringand(a, b)
puts ringor(a, b)

```

output:

```ruby
 ├─ (ring true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false)
 ├─ (ring true, false, false)
 ├─ (ring true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false)
 └─ (ring true, false, false, true, false, false, true, false, true, true, false, false, true, false, false, true)

```

This isn’t as elegant as it could be. I’d prefer to write the function once and pass the Boolean operator as a symbol (similarly to how you’ve done it above, but without having to translate from an intermediate string like `"and"`), but Sonic Pi doesn’t seem to like any of my attempts to make that happen. Maybe someone better at Ruby can make it work. And ideally there would be an infix syntax, but [that seems like WAY more trouble than it’s worth](https://stackoverflow.com/questions/40888810/how-to-convert-any-method-to-infix-operator-in-ruby).

Note that this implementation works fine with rings of different sizes! And perhaps a little more intuitively than the equivalent using bitwise arithmetic. It’s not commutative in those cases, but otherwise I expect it’s mathematically well behaved.

---

<div class="post-metadata">

**Author:** ![emlyn](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/emlyn/32/1460_2.png) [@emlyn](https://in-thread.sonic-pi.net/u/emlyn)\
**Post date:** [November 26, 2024, 12:45am UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/10 "2024-11-26T00:45:32Z")

</div>

I had a little play around and managed to monkey-patch the ring class to add the logical operators to it:

```ruby
# Monkey-patch the base class of ring-like classes:
class SonicPi::Core::SPVector
  def |(other)
    self.class.new(@vec.zip(other.vec).map {|a,b| a | b})
  end
  def &(other)
    self.class.new(@vec.zip(other.vec).map {|a,b| a & b})
  end
  def ^(other)
    self.class.new(@vec.zip(other.vec).map {|a,b| a ^ b})
  end
  def !()
    self.class.new(@vec.map {|a| !a})
  end
end

# Create a couple of rings:
r1 = spread(3, 8)
puts "R1 ", r1
r2 = spread(5, 8)
puts "R2 ", r2

# Combine them:
puts "NOT", !r2
puts "AND", r1 & r2
puts "OR ", r1 | r2
puts "XOR", r1 ^ r2

```

Output:

```ruby
{run: 1, time: 0.0}
 ├─ "R1 " (ring true, false, false, true, false, false, true, false)
 ├─ "R2 " (ring true, false, true, true, false, true, true, false)
 ├─ "NOT" (ring false, true, false, false, true, false, false, true)
 ├─ "AND" (ring true, false, false, true, false, false, true, false)
 ├─ "OR " (ring true, false, true, true, false, true, true, false)
 └─ "XOR" (ring false, false, true, false, false, true, false, false)

```

---

<div class="post-metadata">

**Author:** ![pashultz](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/pashultz/32/1964_2.png) [@pashultz](https://in-thread.sonic-pi.net/u/pashultz)\
**Post date:** [November 26, 2024, 5:34pm UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/11 "2024-11-26T17:34:34Z")

</div>

💥 Wow, @emlyn, that is really clean! And much easier than I expected. Thanks for sharing that. Would this “monkey-patching” technique work to add new “data” elements to things like `scale` or `chord` too, or only methods? (I imagine it’s been illustrated here before, so I will also just go back and search at next opportunity.)

I notice that if the first ring is longer than the second, the `.zip` method will supply `nil` for the request of the sequence. So

```ruby
puts spread(1, 8) | spread(1, 3)

```

gives

```ruby
 └─ (ring true, false, false, false, false, false, false, false)

```

which is different from what my method did above, but no less intuitive.

---

<div class="post-metadata">

**Author:** ![emlyn](https://dub1.discourse-cdn.com/flex017/user_avatar/in-thread.sonic-pi.net/emlyn/32/1460_2.png) [@emlyn](https://in-thread.sonic-pi.net/u/emlyn)\
**Post date:** [November 26, 2024, 6:16pm UTC](https://in-thread.sonic-pi.net/t/combining-rings-using-boolean-operations/9303/12 "2024-11-26T18:16:22Z")

</div>

Good point about the different length rings.

You can add some data with a couple of methods to set and get it like this (but be aware all this is getting into highly unsupported territory, so could break in any new versions):

```ruby
class SonicPi::Core::SPVector
  def data()
    @data
  end
  def set_data(d)
    @data = d
  end
end

a = (spread 5, 8)
puts "before", a.data
a.set_data 42
puts "after", a.data

```

```ruby
{run: 2, time: 0.0}
 ├─ "before" nil
 └─ "after" 42

```
