Friday, December 29, 2017

Calculator in C#

Problem: Create a calculator that can handle multiplication, division, subtraction, addition, power of, and parentheses. This does the proper order of operations (i.e. multiply before addition).
Important: operators/operands can be separated by ZERO or MORE spaces.

Approach: TDD. I solved Addition (+) first, then Subtraction (-). By working on just a few operators, I was able to expose and solve many hard problems using simple input (like "1-1").


//Code using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using System.Linq; namespace Solution { public static class Kata { private static bool isNumeric(string s) { double tmp = 0; return Double.TryParse(s, out tmp); } private static bool needToSplitNegative(string token, List<string> tokens) { //check for the case where we have like 1-1 //the regex will split this into 1 and -1 //so we need to split it if (isNumeric(token) && token.StartsWith("-") && tokens.Count > 0 && isNumeric(tokens.Last())) return true; return false; } public static List<string> Tokenizer(string s) { List<string> tokens = new List<string>(); //matches: //number //operator //or outer parentheses with stuff inside //for outer parens, i got it wrong at first. //then i found this: https://stackoverflow.com/a/35271017/1538717 foreach (Match match in Regex.Matches(s.Replace(" ", ""), @"([-]{0,1}[\d]*[\.]{0,1}[\d]+)|([\/\*\+\-\^])|\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)")) { string token = match.Value; if(needToSplitNegative(match.Value, tokens)) { tokens.Add("-"); tokens.Add(token.Substring(1)); } else tokens.Add(match.Value); } return tokens; } private static double doOp(double left, double right, string op) { switch (op) { case "+": return left + right; case "-": return left - right; case "*": return left * right; case "/": return left / right; case "^": return Math.Pow(left, right); default: return 0; } } private static int getSmallest(List<string> tokens, string op1, string op2) { int useIndex = -1; int addIndex = tokens.IndexOf(op1); int subIndex = tokens.IndexOf(op2); if (addIndex > -1 && subIndex > -1) { useIndex = Math.Min(addIndex, subIndex); } else if (addIndex > -1) useIndex = addIndex; else if (subIndex > -1) useIndex = subIndex; else useIndex = -1; return useIndex; } public static double calculate(string s) { if (string.IsNullOrEmpty(s)) throw new ArgumentException("Please send proper input!"); var tokens = Tokenizer(s); Console.WriteLine(string.Join(" ", tokens)); if (!tokens.Any()) throw new ArgumentException("There are no numbers or operators!"); while (true) { int useIndex = tokens.FindIndex(t=>t.StartsWith("(")); if(useIndex != -1) { tokens[useIndex] = calculate(tokens[useIndex].Substring(1, tokens[useIndex].Length - 2)).ToString(); continue; } useIndex = tokens.IndexOf("^"); if(useIndex == -1) useIndex = getSmallest(tokens, "*", "/"); if (useIndex == -1) useIndex = getSmallest(tokens, "+", "-"); //there are no operators if (useIndex == -1) break; double left = Convert.ToDouble(tokens[useIndex - 1]); double right = Convert.ToDouble(tokens[useIndex + 1]); double result = doOp(left, right, tokens[useIndex]); tokens[useIndex + 1] = result.ToString(); tokens.RemoveRange(useIndex - 1, 2); } return Convert.ToDouble(tokens[0]); } } } //Tests using NUnit.Framework; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; //TODO: Replace examples and use TDD development by writing your own tests namespace Solution { [TestFixture] public class CalculatorTest { public bool close(double a, double b) { if (Math.Abs(a - b) < 0.000000001) return true; return false; } [TestCase("1 + 1", 2)] [TestCase("1+1", 2, Description="Deals with spaces")] [TestCase("1+1+1", 3, Description ="More than 2 operands")] [TestCase("1+1+1+1", 4, Description ="Duplicate expression")] [TestCase("1.05+0.95", 2, Description ="Deals with decimals")] [TestCase("-1.05+-0.95", -2, Description = "Deals with negatives")] public void AddTest(string input, double expected) { Assert.AreEqual(expected, Kata.calculate(input), 0.000000001); } [TestCase("1 - 1", 0)] [TestCase("1-1", 0, Description = "Deals with spaces")] [TestCase("1-1-1", -1, Description = "More than 2 operands")] [TestCase("1-1-1-1", -2, Description = "Duplicate expression")] [TestCase("1.05-0.05", 1, Description = "Deals with decimals")] [TestCase("-1.05--0.95", -0.1, Description = "Deals with negatives")] public void SubtractTest(string input, double expected) { Assert.AreEqual(expected, Kata.calculate(input), 0.000000001); } [TestCase("1 * 1", 1)] [TestCase("1+4*2", 9, Description ="Testing precedence")] public void MultiplyTest(string input, double expected) { Assert.AreEqual(expected, Kata.calculate(input), 0.000000001); } [TestCase("1 / 1", 1)] [TestCase("1/4*2", 0.5, Description = "Testing precedence")] public void DivideTest(string input, double expected) { Assert.AreEqual(expected, Kata.calculate(input), 0.000000001); } [TestCase("1 ^ 1", 1)] [TestCase("1/4*2^2",1, Description = "Testing precedence")] public void PowerTest(string input, double expected) { Assert.AreEqual(expected, Kata.calculate(input), 0.000000001); } [TestCase("(1 ^ 1)", 1)] [TestCase("1/4*2^(1+1)", 1, Description = "Testing precedence")] public void ParensTest(string input, double expected) { Assert.AreEqual(expected, Kata.calculate(input), 0.000000001); } [TestCase("1 + 1", new string[] { "1", "+", "1" })] [TestCase("1 * 1", new string[] { "1", "*", "1" })] [TestCase("1 / 1", new string[] { "1", "/", "1" })] [TestCase("1 ^ 1", new string[] { "1", "^", "1" })] [TestCase("1 - 1", new string[] { "1", "-", "1" })] [TestCase("-1 - -1", new string[] { "-1", "-", "-1" })] [TestCase("-1 - (-1 + 1)", new string[] { "-1", "-", "(-1+1)" })] [TestCase("1-1", new string[] { "1", "-", "1" })] [TestCase("(3-1)(3-2)", new string[] { "(3-1)","(3-2)" })] public void TokenizerTest(string input, string[] expectedArr) { List<string> expected = expectedArr.ToList<string>(); List<string> actual = new List<string>(); foreach(string s in Kata.Tokenizer(input)) { actual.Add(s); } CollectionAssert.AreEqual(expected, actual); } } }

Friday, December 15, 2017

Sum two big numbers without using BigInteger

Problem
You're given two large integers represented by strings a and b. Sum them without using BigInteger.

Approach
Do it like how you sum numbers on paper

Code
using System; using System.Text; public static class Kata { public static string sumStrings(string a, string b) { int maxLen = Math.Max(a.Length, b.Length); a = PadLeft(a, maxLen); b = PadLeft(b, maxLen); int[] digits = new int[maxLen]; bool carry = false; for(int i = a.Length - 1; i >= 0; i--) { int sum = AddChars(a[i], b[i]); if(carry) { sum++; carry = false; } if(sum > 9) { digits[i] = sum - 10; carry = true; } else { digits[i] = sum; } } string result = string.Join("", digits); if(carry) result = "1" + result; return result.TrimStart('0'); } public static int AddChars(char a, char b) { return (a - '0') + (b - '0'); } public static string PadLeft(string a, int len) { return a.PadLeft(len, '0'); } }

Thursday, December 14, 2017

Convert json to a dynamic anonymous object - using Newtonsoft


//This takes JSON and deserializes it into a dynamic object. //This means i don't have to declare the type anywhere string json = GetJsonFile(); var converter = new ExpandoObjectConverter(); dynamic storeConfig = JsonConvert.DeserializeObject<ExpandoObject>(json, converter);
var activePipelines = ((IEnumerable<dynamic>)storeConfig.Pipelines).Where(m => Convert.ToBoolean(m.IsActive));

//do stuff with the active pipelines








Saturday, December 9, 2017

Points earned per kata in CodeWars

How many points is each kata worth?







See your points earned per challenge here: https://www.codewars.com/users/<put your username here>/site-events



Ref:
1. https://airtable.com/embed/shrDt7USqZ7GStRb0/tbly10FthXIRqbd7L/viwJf0lpHrvPCKNfb

Count number of set bits in a 32-bit int




using System; public class Kata { public static int CountBits(int n) { int count = 0; int mask = 1; for(int i = 0; i < 32; i++) { if((mask & n) == mask) count++; mask = mask << 1; } return count; } }

Sunday, October 1, 2017

Solve the right problem

Often bugs will appear and programmers will attempt to solve the symptoms, rather than digging down and finding root causes.

Similarly, a misunderstanding of a single requirement leads to the wrong design - either over-engineered or simply wrong.

I have seen these problems happen countless times, and have committed them myself countless times. Solving the wrong problem is simply a waste of time and effort. Near the end of a project, with an impending deadline, these mistakes can be extremely costly. Everyone is already stressed and trying to go faster than they should. And then when you add in the mistake of solving the wrong problem it can result in missing the deadline.

How can I make sure to solve the right problem? Here's some ways

  • Understand the requirements, even if you aren't working on that specific section of the project
  • Write and read unit tests - this will help you understand the code and design intimiately
  • When a bug comes in, realize that it's probably a symptom. Don't just jump in and throw a band-aid over it.
  • Communicate with teammates. Chances are if you're doing something in isolation, you're probably solving the wrong problem. Talk to others, never assume

Saturday, September 9, 2017

Keep design simple - don't design ahead

Keep the design as simple as possible.

Your current design should only support your current features. 

The problem is when we start looking ahead at all of the features as a whole, and then try to create some grand architecture in order to support all of the features.

You know what WILL happen? The design will be overkill, will be wrong, and it'll be fragile. It may work for feature #1, but once you start on feature #2, the design does not support it all. 

I've seen this happen over and over and over.

Wouldn't it make more sense to do the following?
  1. Design and code feature #1 using the simplest design possible
  2. Add unit tests
  3. Design feature #2 based on the current design. This may require refactoring the existing design to support feature #2. Keep it as simple as possible.
  4. Add unit tests
  5. Rinse and repeat all the way until the end
At the end you have the simplest design possible that currently supports all of the features. Since your code is always backed by unit tests, you can safely refactor. The design has grown exactly as needed. It's a bottom-up design. 
Note: Evolution is a bottom-up design process. 


When you design ahead, you're doing top-down design. And it typically looks like this:
  1.  Look at all features and come up with a design that seems to support all the features
  2. Code feature #1
  3. Code feature #2 - oh, the requirements for this changed. I'll need to refactor the framework a little bit to force it to handle feature #2
  4. Code feature #3 - oh, now that i'm coding this, i realize that the current design won't exactly support this. I'll just tack on this little thing and it'll work.
  5. Rinse and repeat for hundreds of features
At the end you have a top-down design that was changed many times due to bottom-up forces. The result is a kludgey mess that no one can make sense out of.

Since requirements always change, and because you don't know everything about every feature until you really start to work on that feature, there will always be bottom-up forces on the design. If the design has been kept as simple as possible every step of the way, then the bottom-up forces can be absorbed painlessly, and the design can gradually evolve. 





There was an error in this gadget