Fundamentals of Classical and Modern Error-Correcting Codes