Your task is to validate rhythm with a meter.

_________________________________________________
Rules:

1. Rhythmic division requires that in one whole note (1) there are two half notes (2) or four quarter notes (4) or eight eighth notes (8). 
Examples: 1 = 2 + 2, 1 = 4 + 4 + 4 + 4 ... 
Note that: 2 = 4 + 4, 4 = 8 + 8, 2 = 8 + 8 + 4 ... 

2. Meter gives an information how many rhythmic types of notes should be in one bar. Bar is the the primary section of a musical score.
Examples: 
          4/4 -> 4 quarter notes in a bar
          5/2 -> 5 half notes in a bar
          3/8 -> 3 eighth notes in a bar
Note that: 
for 4/4 valid bars are: '4444', '88888888', '2488' ...
for 5/2 valid bars are: '22222', '2244244', '8888244888844' ...
for 3/8 valid bars are: '888', '48' ...

3. Anacrusis occurs when all bars but the first and last are valid, and the notes in the first and last bars when combined would also make a valid bar.

Examples: 
for 4/4 valid anacrusis is -> 44|...|44 or 88|...|888888 or 2|...|488 
for 5/2 valid anacrusis is -> 22|...|222 or 222|...|22 or  2244|...|244
for 3/8 valid anacrusis is -> 8|...|88 or 4|...|8 or 8|...|4
Note:
When anacrusis is valid but other bars in score are not -> return 'Invalid rhythm'
________________________________________________

Input:
meter - array: eg. [4, 4],
score - string, bars separated with '|': eg. '4444|8484842|888'

Output:
string message: 'Valid rhythm', 'Valid rhythm with anacrusis' or 'Invalid rhythm'

Source: codewars | Difficulty: EASY
Tags: Fundamentals

Mathematics

Starter code: def validate_rhythm(meter, score):
	

Solutions:
def is_valid_bar(bar, meter):
	total = 0
	for n in bar:
		n = int(n)
		if n & n - 1:
			return False
		total += 1.0 / n
	return total == meter

def validate_rhythm(meter, score):
	(n, d) = meter
	if d & d - 1:
		return 'Invalid rhythm'
	bars = score.split('|')
	meter = float(n) / d
	if all((is_valid_bar(bar, meter) for bar in bars[1:-1])):
		if all((is_valid_bar(bar, meter) for bar in (bars[0], bars[-1]))):
			return 'Valid rhythm'
		if len(bars) > 1 and is_valid_bar(bars[0] + bars[-1], meter):
			return 'Valid rhythm with anacrusis'
	return 'Invalid rhythm'


from fractions import Fraction
VALID_CHARS = {'1', '2', '4', '8'}

def note_sum(s):
	return sum((Fraction(1, x) for x in map(int, s)))

def validate_rhythm(meter, score):
	if meter[1] not in [1, 2, 4, 8]:
		return 'Invalid rhythm'
	ss = score.split('|')
	if not all((s and all((x in VALID_CHARS for x in s)) for s in ss)):
		return 'Invalid rhythm'
	note = Fraction(*meter)
	if all((note_sum(s) == note for s in ss)):
		return 'Valid rhythm'
	ss[0] += ss.pop()
	if all((note_sum(s) == note for s in ss)):
		return 'Valid rhythm with anacrusis'
	return 'Invalid rhythm'


from functools import reduce

def validate_rhythm(meter, score):
	(num, den, meter) = (*meter, int.__truediv__(*meter))
	bars = [[int(d) for d in bar] for bar in score.split('|')]
	prod = den * reduce(int.__mul__, (n for bar in bars for n in bar))
	if not prod & prod - 1:
		divs = [sum((1 / n for n in bar)) for bar in bars]
		if all((div == meter for div in divs[1:-1])):
			if all((div == meter for div in (divs[0], divs[-1]))):
				return 'Valid rhythm'
			if divs[1:] and divs[0] + divs[-1] == meter:
				return 'Valid rhythm with anacrusis'
	return 'Invalid rhythm'


def validate_rhythm(meter, score):
	if [c for c in score if c not in '|1248'] or meter[1] not in [1, 2, 4, 8]:
		return 'Invalid rhythm'
	bar = meter[0] * 8 / meter[1]
	l = [len(b) for b in score.replace('4', '88').replace('2', '8' * 4).replace('1', '8' * 8).split('|')]
	if all((i == bar for i in l)):
		return 'Valid rhythm'
	if l[0] + l[-1] == bar and all((i == bar for i in l[1:-1])):
		return 'Valid rhythm with anacrusis'
	return 'Invalid rhythm'


def validate_rhythm(meter, score):
	scores = score.split('|')
	anacrusis = False
	if 16 % meter[1]:
		return 'Invalid rhythm'
	for (i, score) in enumerate(scores):
		sums = sum((16 / int(s) for s in score))
		if sums < meter[0] * 16 / meter[1]:
			if i == len(scores) - 1 or i == 0:
				anacrusis = True
			else:
				return 'Invalid rhythm'
		elif sums > meter[0] * 16 / meter[1]:
			return 'Invalid rhythm'
	if anacrusis:
		return 'Valid rhythm with anacrusis'
	else:
		return 'Valid rhythm'


def validate_rhythm(meter, score):
	from fractions import Fraction
	if meter[1] not in [1, 2, 4, 8]:
		return 'Invalid rhythm'
	time_signature = Fraction(*meter)
	bars = [sum((Fraction(1, int(c)) for c in bar)) for bar in score.split('|')]
	if all((bar == time_signature for bar in bars)):
		return 'Valid rhythm'
	if all((bar == time_signature for bar in bars[1:-1])) and bars[0] + bars[-1] == time_signature:
		return 'Valid rhythm with anacrusis'
	return 'Invalid rhythm'


def validate_rhythm(m, score):
	t = m[0] / m[1]
	if t == 1 and m[0] not in [1, 2, 4, 8]:
		return 'Invalid rhythm'
	a = score.split('|')
	for c in score:
		if c not in '|1248':
			return 'Invalid rhythm'
	for s in a[1:-1]:
		if sum([1 / int(c) for c in s]) != t:
			return 'Invalid rhythm'
	bool = True
	for s in [a[0], a[-1]]:
		if sum([1 / int(c) for c in s]) != t:
			bool = False
	if bool:
		return 'Valid rhythm'
	if sum([1 / int(c) for c in a[0] + a[-1]]) == t:
		return 'Valid rhythm with anacrusis'
	return 'Invalid rhythm'


def validate_rhythm(meter, score):
	(a, b) = meter
	if not set(score) <= set('1248|') or b not in (1, 2, 4, 8):
		return 'Invalid rhythm'
	bars = [sum((1 / int(x) for x in bar)) for bar in score.split('|')]
	m = a / b
	if all((x == m for x in bars)):
		return 'Valid rhythm'
	if len(bars) >= 2 and all((x == m for x in bars[1:-1] + [bars[0] + bars[-1]])):
		return 'Valid rhythm with anacrusis'
	return 'Invalid rhythm'


def validate_rhythm(meter, score):
	if meter[1] not in (1, 2, 4, 8):
		return 'Invalid rhythm'
	score = score.translate(score.maketrans('1248', '8421'))
	target = meter[0] * 8 // meter[1]

	def valid(s):
		return sum((int(c) for c in s)) == target
	bars = score.split('|')
	if any((not valid(bar) for bar in bars[1:-1])):
		return 'Invalid rhythm'
	if valid(bars[0]) and valid(bars[-1]):
		return 'Valid rhythm'
	if valid(bars[0] + bars[-1]):
		return 'Valid rhythm with anacrusis'
	else:
		return 'Invalid rhythm'


def validate_rhythm(meter, score):
	print(score)
	if meter[1] not in [1, 2, 4, 8]:
		return 'Invalid rhythm'
	goal = meter[0] * (1 / meter[1])
	print(goal)
	score = [[1 / int(f) for f in i] for i in score.split('|')]
	if len(score) == 1:
		if goal == sum([sum(i) for i in score]):
			return 'Valid rhythm'
		else:
			return 'Invalid rhythm'
	for i in score[1:-1]:
		if sum(i) != goal:
			return 'Invalid rhythm'
	if sum(score[0]) + sum(score[-1]) == goal:
		return 'Valid rhythm with anacrusis'
	if sum(score[0]) + sum(score[-1]) != goal * 2:
		return 'Invalid rhythm'
	return 'Valid rhythm'