// integer values from -2^191 .. 2^191-1
struct Large {
mut:
	l u64
	m u64
	h u64
}

fn (a Large) clone() Large {
	r := Large{
		l: a.l
		m: a.m
		h: a.h
	}
	return r
}

fn (mut a Large) add(b Large) {
	oldl := a.l
	a.l += b.l
	oldm := a.m
	if a.l < oldl {
		a.m++
	}
	a.m += b.m
	if a.m < oldm || (a.l < oldl && a.m <= oldm) {
		a.h++
	}
	a.h += b.h
}

fn doub_large(shared a Large, shared b Large, shared c Large, shared d Large, shared e Large) {
	for _ in 0 .. 50 {
		lock a, b; rlock c, d, e {
			// double the sum by adding all objects to a or b
			old_a := a.clone()
			a.add(b)
			b.add(old_a)
			a.add(c)
			b.add(d)
			a.add(e)
		}
	}
}

fn test_mixed_order_lock_rlock() {
	// initialize objects so that their sum = 1
	shared a := Large{
		l: 4
	}
	shared b := Large{
		l: u64(-7)
		m: u64(-1)
		h: u64(-1)
	}
	shared c := Large{
		l: 17
	}
	shared d := Large{
		l: u64(-11)
		m: u64(-1)
		h: u64(-1)
	}
	shared e := Large{
		l: u64(-2)
		m: u64(-1)
		h: u64(-1)
	}
	// spawn 3 threads for doubling with different orders of objects
	t1 := spawn doub_large(shared a, shared b, shared c, shared d, shared e)
	t2 := spawn doub_large(shared e, shared b, shared a, shared c, shared d)
	t3 := spawn doub_large(shared b, shared a, shared e, shared d, shared c)
	t1.wait()
	t2.wait()
	t3.wait()
	// calculate the sum after 3*50 doublings
	mut sum := Large{}
	rlock a, b, c, d, e {
		sum.add(a)
		sum.add(b)
		sum.add(c)
		sum.add(d)
		sum.add(e)
	}
	// the sum should be 2^150
	assert sum.l == 0
	assert sum.m == 0
	assert sum.h == 4194304
}
