-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
115 lines (98 loc) · 3.97 KB
/
Program.cs
File metadata and controls
115 lines (98 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
using System.Text.Json;
using System.Text.Json.Serialization;
// QuickSheet Margin Extension — break-even & contribution margin calculator
// Protocol: JSON-lines on stdin/stdout
//
// Usage:
// margin: price, variableCost, fixedCosts
// margin: 50, 20, 9000
//
// Output (3 rows × 2 cols):
// Row 0: Contribution margin per unit + margin %
// Row 1: Break-even units + break-even revenue
// Row 2: Status indicator + formula summary
var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
var register = new { type = "register", prefix = "margin", name = "Margin Calculator", version = "1.0.0" };
Console.WriteLine(JsonSerializer.Serialize(register, options));
Console.Out.Flush();
while (Console.ReadLine() is { } line)
{
if (string.IsNullOrWhiteSpace(line)) continue;
try
{
using var doc = JsonDocument.Parse(line);
var root = doc.RootElement;
string msgType = root.GetProperty("type").GetString() ?? "";
if (msgType == "activate")
{
string id = root.GetProperty("id").GetString() ?? "";
var parms = root.GetProperty("params");
HandleActivate(id, parms, options);
}
}
catch { /* ignore malformed messages */ }
}
static void HandleActivate(string id, JsonElement parms, JsonSerializerOptions options)
{
string[] args = new string[parms.GetArrayLength()];
for (int i = 0; i < args.Length; i++)
args[i] = parms[i].GetString()?.Trim() ?? "";
if (args.Length < 3)
{
SendError(id, "Usage: margin: sellingPrice, variableCostPerUnit, totalFixedCosts", options);
return;
}
if (!double.TryParse(args[0], out double price) ||
!double.TryParse(args[1], out double variableCost) ||
!double.TryParse(args[2], out double fixedCosts))
{
SendError(id, "All three values must be numbers", options);
return;
}
if (price <= 0)
{
SendError(id, "Selling price must be > 0", options);
return;
}
double cm = price - variableCost; // contribution margin per unit
double cmRatio = (cm / price) * 100; // contribution margin ratio %
var cells = new List<object>();
if (cm <= 0)
{
// Negative or zero margin — break-even impossible
cells.Add(new { r = 0, c = 0, v = "🔴 Negative Margin" });
cells.Add(new { r = 0, c = 1, v = $"CM: ${cm:N2}/unit ({cmRatio:F1}%)" });
cells.Add(new { r = 1, c = 0, v = "Break-even: impossible" });
cells.Add(new { r = 1, c = 1, v = "Variable cost ≥ price" });
cells.Add(new { r = 2, c = 0, v = $"Price: ${price:N2}" });
cells.Add(new { r = 2, c = 1, v = $"Var cost: ${variableCost:N2}" });
}
else
{
double beUnits = Math.Ceiling(fixedCosts / cm); // break-even units (round up)
double beRevenue = beUnits * price; // break-even revenue
// Status based on margin ratio
string status = cmRatio switch
{
>= 60 => "🟢 Strong",
>= 40 => "🟡 Moderate",
>= 20 => "🟠 Thin",
_ => "🔴 Razor-thin"
};
cells.Add(new { r = 0, c = 0, v = $"{status} margin" });
cells.Add(new { r = 0, c = 1, v = $"CM: ${cm:N2}/unit ({cmRatio:F1}%)" });
cells.Add(new { r = 1, c = 0, v = $"Break-even: {beUnits:N0} units" });
cells.Add(new { r = 1, c = 1, v = $"BE revenue: ${beRevenue:N2}" });
cells.Add(new { r = 2, c = 0, v = $"Fixed: ${fixedCosts:N2}" });
cells.Add(new { r = 2, c = 1, v = $"Price ${price:N2} − Var ${variableCost:N2}" });
}
var write = new { type = "write", id, cells };
Console.WriteLine(JsonSerializer.Serialize(write, options));
Console.Out.Flush();
}
static void SendError(string id, string message, JsonSerializerOptions options)
{
var error = new { type = "error", id, message };
Console.WriteLine(JsonSerializer.Serialize(error, options));
Console.Out.Flush();
}