public Timetable(Timetable other)
        {
            #region Clone subject/type/stream/class data

            // copy across all the subject data
            SubjectList = new List <Subject>();
            foreach (var subject in other.SubjectList)
            {
                SubjectList.Add(subject.Clone());
            }
            // copy all the type data
            TypeList = new List <Type>();
            foreach (var type in other.TypeList)
            {
                TypeList.Add(type.Clone());
            }
            // copy all the stream data
            StreamList = new List <Stream>();
            foreach (var stream in other.StreamList)
            {
                StreamList.Add(stream.Clone());
            }
            // copy all the session data
            ClassList = new List <Session>();
            foreach (var session in other.ClassList)
            {
                ClassList.Add(session.Clone());
            }

            // update reference to subjects
            for (var i = 0; i < SubjectList.Count; i++)
            {
                foreach (var type in TypeList)
                {
                    if (type.Subject == other.SubjectList[i])
                    {
                        type.Subject = SubjectList[i];
                    }
                }
            }
            // update references to types
            for (var i = 0; i < TypeList.Count; i++)
            {
                // parent references
                foreach (var stream in StreamList)
                {
                    if (stream.Type == other.TypeList[i])
                    {
                        stream.Type = TypeList[i];
                    }
                }
                // child references
                for (var j = 0; j < TypeList[i].Subject.Types.Count; j++)
                {
                    if (TypeList[i].Subject.Types[j] == other.TypeList[i])
                    {
                        TypeList[i].Subject.Types[j] = TypeList[i];
                        break;
                    }
                }
                // unique streams
                for (var j = 0; j < TypeList[i].UniqueStreams.Count; j++)
                {
                    if (StreamList[i].Type.Streams[j] == other.StreamList[i])
                    {
                        StreamList[i].Type.Streams[j] = StreamList[i];
                        break;
                    }
                }
            }
            // update references to streams
            for (var i = 0; i < StreamList.Count; i++)
            {
                // parent references
                foreach (var session in ClassList)
                {
                    if (session.Stream == other.StreamList[i])
                    {
                        session.Stream = StreamList[i];
                    }
                }
                // child references
                for (var j = 0; j < StreamList[i].Type.Streams.Count; j++)
                {
                    if (StreamList[i].Type.Streams[j] == other.StreamList[i])
                    {
                        StreamList[i].Type.Streams[j] = StreamList[i];
                        break;
                    }
                }
                // find all equivalent streams set to other.StreamList[i]
                foreach (Stream t in StreamList)
                {
                    for (var k = 0; k < t.Equivalent.Count; k++)
                    {
                        if (t.Equivalent[k] == other.StreamList[i])
                        {
                            t.Equivalent[k] = StreamList[i];
                            break;
                        }
                    }
                }
                // find all incompatible streams set to other.StreamList[i]
                foreach (Stream t in StreamList)
                {
                    for (var k = 0; k < t.Incompatible.Count; k++)
                    {
                        if (t.Incompatible[k] == other.StreamList[i])
                        {
                            t.Incompatible[k] = StreamList[i];
                            break;
                        }
                    }
                }
                // find all unique streams set to other.StreamList[i]
                foreach (var type in TypeList)
                {
                    for (var j = 0; j < type.UniqueStreams.Count; j++)
                    {
                        if (type.UniqueStreams[j] == other.StreamList[i])
                        {
                            type.UniqueStreams[j] = StreamList[i];
                            break;
                        }
                    }
                }
            }
            // update references to the sessions
            for (var i = 0; i < ClassList.Count; i++)
            {
                // child references
                for (var j = 0; j < ClassList[i].Stream.Classes.Count; j++)
                {
                    if (ClassList[i].Stream.Classes[j] == other.ClassList[i])
                    {
                        ClassList[i].Stream.Classes[j] = ClassList[i];
                        break;
                    }
                }
            }

            #endregion

            // a shallow copy will do for boolean (value type)
            // TODO: copies both levels or just first level?
            _streamClashTable = (bool[][])other._streamClashTable.Clone();

            // clone unavailable data
            UnavailableList = new List <Unavailability>();
            foreach (var unavail in other.UnavailableList)
            {
                UnavailableList.Add(unavail.Clone());
            }

            // copy status of solution recomputation required
            RecomputeSolutions = other.RecomputeSolutions;
        }