1f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas/*
2f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas * Copyright 2016 Google Inc.
3f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas *
4f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas * Use of this source code is governed by a BSD-style license that can be
5f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas * found in the LICENSE file.
6f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas */
7941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas
8f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#ifndef SKSL_GLSLCODEGENERATOR
9f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#define SKSL_GLSLCODEGENERATOR
10f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
11f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include <stack>
12f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include <tuple>
13f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include <unordered_map>
14f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
15f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "SkSLCodeGenerator.h"
16762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas#include "SkSLStringStream.h"
17f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLBinaryExpression.h"
18f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLBoolLiteral.h"
19f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLConstructor.h"
20f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLDoStatement.h"
21f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLExtension.h"
22f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLFloatLiteral.h"
23f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLIfStatement.h"
24f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLIndexExpression.h"
25f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLInterfaceBlock.h"
26f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLIntLiteral.h"
27f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLFieldAccess.h"
28f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLForStatement.h"
29f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLFunctionCall.h"
30f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLFunctionDeclaration.h"
31f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLFunctionDefinition.h"
32f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLPrefixExpression.h"
33f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLPostfixExpression.h"
34f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLProgramElement.h"
35f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLReturnStatement.h"
36762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas#include "ir/SkSLSetting.h"
37f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLStatement.h"
38af19769831f1c4c3b90c85aa9f8851cd8bbf86d5Ethan Nicholas#include "ir/SkSLSwitchStatement.h"
39f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLSwizzle.h"
40f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLTernaryExpression.h"
4122f939e849013b7fc51374c289b5bf37e63dfdb1ethannicholas#include "ir/SkSLVarDeclarations.h"
4222f939e849013b7fc51374c289b5bf37e63dfdb1ethannicholas#include "ir/SkSLVarDeclarationsStatement.h"
43f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLVariableReference.h"
44f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#include "ir/SkSLWhileStatement.h"
45f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
46f789b3893579b773bb4d7be6c2c65311500b53bbethannicholasnamespace SkSL {
47f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
48f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#define kLast_Capability SpvCapabilityMultiViewport
49f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
50f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas/**
51f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas * Converts a Program into GLSL code.
52f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas */
53f789b3893579b773bb4d7be6c2c65311500b53bbethannicholasclass GLSLCodeGenerator : public CodeGenerator {
54f789b3893579b773bb4d7be6c2c65311500b53bbethannicholaspublic:
55f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    enum Precedence {
56f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kParentheses_Precedence    =  1,
57f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kPostfix_Precedence        =  2,
58f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kPrefix_Precedence         =  3,
59f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kMultiplicative_Precedence =  4,
60f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kAdditive_Precedence       =  5,
61f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kShift_Precedence          =  6,
62f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kRelational_Precedence     =  7,
63f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kEquality_Precedence       =  8,
64f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kBitwiseAnd_Precedence     =  9,
65f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kBitwiseXor_Precedence     = 10,
66f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kBitwiseOr_Precedence      = 11,
67f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kLogicalAnd_Precedence     = 12,
68f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kLogicalXor_Precedence     = 13,
69f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kLogicalOr_Precedence      = 14,
70f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kTernary_Precedence        = 15,
71f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kAssignment_Precedence     = 16,
72f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas        kSequence_Precedence       = 17,
734b330dfd3334bf24bf93043acfcd31590a3cdbbfEthan Nicholas        kTopLevel_Precedence       = kSequence_Precedence
74f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    };
75f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
76941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    GLSLCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors,
770df1b04db87c3d86ee0b0bd6aa2cb5b6be32cac2Ethan Nicholas                      OutputStream* out)
78941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    : INHERITED(program, errors, out)
79762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    , fLineEnding("\n")
80941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    , fContext(*context) {}
81f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
82762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    bool generateCode() override;
83f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
84762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholasprotected:
85f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void write(const char* s);
86f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
87f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeLine();
88f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
89f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeLine(const char* s);
90f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
910df1b04db87c3d86ee0b0bd6aa2cb5b6be32cac2Ethan Nicholas    void write(const String& s);
92f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
935b5f096a038259b8d9084834f877588a0db80250Ethan Nicholas    void write(StringFragment s);
945b5f096a038259b8d9084834f877588a0db80250Ethan Nicholas
950df1b04db87c3d86ee0b0bd6aa2cb5b6be32cac2Ethan Nicholas    void writeLine(const String& s);
96f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
97762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    virtual void writeHeader();
98762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas
99f7b8820dc813d1eb0b6b43fe4581dded0da38cafEthan Nicholas    virtual bool usesPrecisionModifiers() const;
100762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas
101f7b8820dc813d1eb0b6b43fe4581dded0da38cafEthan Nicholas    virtual String getTypeName(const Type& type);
102f7b8820dc813d1eb0b6b43fe4581dded0da38cafEthan Nicholas
103f7b8820dc813d1eb0b6b43fe4581dded0da38cafEthan Nicholas    void writeType(const Type& type);
104f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
105f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeExtension(const Extension& ext);
106f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
107f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeInterfaceBlock(const InterfaceBlock& intf);
108f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
109f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeFunctionStart(const FunctionDeclaration& f);
1100df1b04db87c3d86ee0b0bd6aa2cb5b6be32cac2Ethan Nicholas
111f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeFunctionDeclaration(const FunctionDeclaration& f);
112f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
113762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    virtual void writeFunction(const FunctionDefinition& f);
114f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
115f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeLayout(const Layout& layout);
116f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
1175961bc9278a00e56dacdd9408d0744b5a0a3b493ethannicholas    void writeModifiers(const Modifiers& modifiers, bool globalContext);
118941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas
11982a62d2d4ef55e53730a4f194a82d4e5da0c4a64Ethan Nicholas    void writeGlobalVars(const VarDeclaration& vs);
12082a62d2d4ef55e53730a4f194a82d4e5da0c4a64Ethan Nicholas
121762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    virtual void writeVarInitializer(const Variable& var, const Expression& value);
122762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas
123f7b8820dc813d1eb0b6b43fe4581dded0da38cafEthan Nicholas    const char* getTypePrecision(const Type& type);
124f7b8820dc813d1eb0b6b43fe4581dded0da38cafEthan Nicholas
125dcba08e891f1766b047cf0dbe8bbd275d9f55d2bEthan Nicholas    void writeTypePrecision(const Type& type);
126dcba08e891f1766b047cf0dbe8bbd275d9f55d2bEthan Nicholas
1275961bc9278a00e56dacdd9408d0744b5a0a3b493ethannicholas    void writeVarDeclarations(const VarDeclarations& decl, bool global);
128f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
129941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    void writeFragCoord();
130941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas
131762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    virtual void writeVariableReference(const VariableReference& ref);
132f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
133f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeExpression(const Expression& expr, Precedence parentPrecedence);
134941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas
135f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeIntrinsicCall(const FunctionCall& c);
136f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
1375961bc9278a00e56dacdd9408d0744b5a0a3b493ethannicholas    void writeMinAbsHack(Expression& absExpr, Expression& otherExpr);
1385961bc9278a00e56dacdd9408d0744b5a0a3b493ethannicholas
1396e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas    void writeDeterminantHack(const Expression& mat);
1406e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas
1416e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas    void writeInverseHack(const Expression& mat);
1426e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas
1436e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas    void writeTransposeHack(const Expression& mat);
1446e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas
1456e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas    void writeInverseSqrtHack(const Expression& x);
1466e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas
147ceb4d48ef4839aab9d99d0200dcfe403ccd0cdf3Ethan Nicholas    virtual void writeFunctionCall(const FunctionCall& c);
148f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
149f7b8820dc813d1eb0b6b43fe4581dded0da38cafEthan Nicholas    void writeConstructor(const Constructor& c, Precedence parentPrecedence);
150f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
151f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeFieldAccess(const FieldAccess& f);
152f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
153823994624aa5e805e16833ecd3d748fc769a164dEthan Nicholas    virtual void writeSwizzle(const Swizzle& swizzle);
154f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
155762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    static Precedence GetBinaryPrecedence(Token::Kind op);
156762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas
157762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    virtual void writeBinaryExpression(const BinaryExpression& b, Precedence parentPrecedence);
158f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
159f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeTernaryExpression(const TernaryExpression& t, Precedence parentPrecedence);
160f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
161762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    virtual void writeIndexExpression(const IndexExpression& expr);
162f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
163f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writePrefixExpression(const PrefixExpression& p, Precedence parentPrecedence);
164f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
165f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writePostfixExpression(const PostfixExpression& p, Precedence parentPrecedence);
166f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
167f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeBoolLiteral(const BoolLiteral& b);
168f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
169dcba08e891f1766b047cf0dbe8bbd275d9f55d2bEthan Nicholas    virtual void writeIntLiteral(const IntLiteral& i);
170f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
171f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeFloatLiteral(const FloatLiteral& f);
172f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
173762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    virtual void writeSetting(const Setting& s);
174762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas
175f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeStatement(const Statement& s);
176f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
177cb67096b61f699b047fe8635984db1ac708a7b99Ethan Nicholas    void writeStatements(const std::vector<std::unique_ptr<Statement>>& statements);
178cb67096b61f699b047fe8635984db1ac708a7b99Ethan Nicholas
179f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeBlock(const Block& b);
180f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
1816e1cbc012b10e99d9caed19eef43939778d1d8ffEthan Nicholas    virtual void writeIfStatement(const IfStatement& stmt);
182f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
183f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeForStatement(const ForStatement& f);
184f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
185f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeWhileStatement(const WhileStatement& w);
186f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
187f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeDoStatement(const DoStatement& d);
188f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
1896e1cbc012b10e99d9caed19eef43939778d1d8ffEthan Nicholas    virtual void writeSwitchStatement(const SwitchStatement& s);
190af19769831f1c4c3b90c85aa9f8851cd8bbf86d5Ethan Nicholas
191f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    void writeReturnStatement(const ReturnStatement& r);
192f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
193762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    virtual void writeProgramElement(const ProgramElement& e);
194762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas
195762466e9fe0478bcf11fba532998e81e33b3069eEthan Nicholas    const char* fLineEnding;
196f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    const Context& fContext;
1970df1b04db87c3d86ee0b0bd6aa2cb5b6be32cac2Ethan Nicholas    StringStream fHeader;
1986e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas    StringStream fExtraFunctions;
1990df1b04db87c3d86ee0b0bd6aa2cb5b6be32cac2Ethan Nicholas    String fFunctionHeader;
2005961bc9278a00e56dacdd9408d0744b5a0a3b493ethannicholas    Program::Kind fProgramKind;
201ddb37d67ba4db42fa5c6012b58d0f4985b454dc0ethannicholas    int fVarCount = 0;
202ddb37d67ba4db42fa5c6012b58d0f4985b454dc0ethannicholas    int fIndentation = 0;
203ddb37d67ba4db42fa5c6012b58d0f4985b454dc0ethannicholas    bool fAtLineStart = false;
204941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    // Keeps track of which struct types we have written. Given that we are unlikely to ever write
205941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    // more than one or two structs per shader, a simple linear search will be faster than anything
206f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    // fancier.
207f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas    std::vector<const Type*> fWrittenStructs;
2086e6525c930b683e6dcd5551bd7a3b68ecc68e45eEthan Nicholas    std::set<String> fWrittenIntrinsics;
209ddb37d67ba4db42fa5c6012b58d0f4985b454dc0ethannicholas    // true if we have run into usages of dFdx / dFdy
210ddb37d67ba4db42fa5c6012b58d0f4985b454dc0ethannicholas    bool fFoundDerivatives = false;
2112a51de82ceb6790f329b9f4cc85e61f34fc2d0d4Brian Salomon    bool fFoundImageDecl = false;
212f1b47bb299e72456d70cf3eae8086a5e01766c67Chris Dalton    bool fFoundGSInvocations = false;
213941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    bool fSetupFragPositionGlobal = false;
214941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    bool fSetupFragPositionLocal = false;
215dba65f95e40fb1a4ea936b453b9bcb3fdbe178e1Brian Salomon    bool fSetupFragCoordWorkaround = false;
216941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas
217941e7e2c9567ab1d8a3b2d1b0e3db71ee5eb75c9Ethan Nicholas    typedef CodeGenerator INHERITED;
218f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas};
219f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
220f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas}
221f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas
222f789b3893579b773bb4d7be6c2c65311500b53bbethannicholas#endif
223