|
@@ -0,0 +1,546 @@
|
|
|
+package models
|
|
|
+
|
|
|
+import (
|
|
|
+ "github.com/shopspring/decimal"
|
|
|
+ "hongze/hongze_edb_lib/utils"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRule1(edbInfoId int, dataValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData) {
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+
|
|
|
+ dayList := getPredictEdbDayList(startDate, endDate, frequency)
|
|
|
+ predictEdbInfoData = make([]*EdbInfoSearchData, 0)
|
|
|
+ for k, v := range dayList {
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, &EdbInfoSearchData{
|
|
|
+ EdbDataId: edbInfoId + 10000000000 + k,
|
|
|
+ DataTime: v.Format(utils.FormatDate),
|
|
|
+ Value: dataValue,
|
|
|
+ })
|
|
|
+ existMap[v.Format(utils.FormatDate)] = dataValue
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRuleTb(edbInfoId int, tbValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+ index := len(predictEdbInfoData)
|
|
|
+
|
|
|
+ dayList := getPredictEdbDayList(startDate, endDate, frequency)
|
|
|
+ predictEdbInfoData = make([]*EdbInfoSearchData, 0)
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+
|
|
|
+ tmpData := &EdbInfoSearchData{
|
|
|
+ EdbDataId: edbInfoId + 10000000000 + index + k,
|
|
|
+ DataTime: currentDate.Format(utils.FormatDate),
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var val float64
|
|
|
+ var calculateStatus bool
|
|
|
+
|
|
|
+
|
|
|
+ preDate := currentDate.AddDate(-1, 0, 0)
|
|
|
+ preDateStr := preDate.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[preDateStr]; ok {
|
|
|
+ val = PredictTbzDiv(preValue, tbValue)
|
|
|
+ calculateStatus = true
|
|
|
+ } else {
|
|
|
+ switch frequency {
|
|
|
+ case "月度":
|
|
|
+
|
|
|
+ nextDateDay := preDate
|
|
|
+ preDateDay := preDate
|
|
|
+ for i := 0; i <= 35; i++ {
|
|
|
+ nextDateDayStr := nextDateDay.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[nextDateDayStr]; ok {
|
|
|
+ val = PredictTbzDiv(preValue, tbValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ preDateDayStr := preDateDay.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[preDateDayStr]; ok {
|
|
|
+ val = PredictTbzDiv(preValue, tbValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ nextDateDay = nextDateDay.AddDate(0, 0, 1)
|
|
|
+ preDateDay = preDateDay.AddDate(0, 0, -1)
|
|
|
+ }
|
|
|
+
|
|
|
+ case "季度", "年度":
|
|
|
+ if preValue, ok := existMap[preDateStr]; ok {
|
|
|
+ val = PredictTbzDiv(preValue, tbValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ nextDateDay := preDate
|
|
|
+ preDateDay := preDate
|
|
|
+
|
|
|
+ for i := 0; i < 35; i++ {
|
|
|
+ nextDateDayStr := nextDateDay.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[nextDateDayStr]; ok {
|
|
|
+ val = PredictTbzDiv(preValue, tbValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ preDateDayStr := preDateDay.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[preDateDayStr]; ok {
|
|
|
+ val = PredictTbzDiv(preValue, tbValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ nextDateDay = nextDateDay.AddDate(0, 0, 1)
|
|
|
+ preDateDay = preDateDay.AddDate(0, 0, -1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if calculateStatus {
|
|
|
+ tmpData.Value = val
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
+
|
|
|
+
|
|
|
+ if val < minValue {
|
|
|
+ minValue = val
|
|
|
+ }
|
|
|
+ if val < maxValue {
|
|
|
+ maxValue = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func PredictTbzDiv(a, b float64) (result float64) {
|
|
|
+ if b != 0 {
|
|
|
+
|
|
|
+ af := decimal.NewFromFloat(a)
|
|
|
+
|
|
|
+
|
|
|
+ bf := decimal.NewFromFloat(b)
|
|
|
+
|
|
|
+
|
|
|
+ cf := decimal.NewFromFloat(1)
|
|
|
+
|
|
|
+
|
|
|
+ val := bf.Add(cf)
|
|
|
+
|
|
|
+
|
|
|
+ result, _ = val.Mul(af).RoundCeil(4).Float64()
|
|
|
+ } else {
|
|
|
+ result = 0
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRuleTc(edbInfoId int, tcValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+ index := len(predictEdbInfoData)
|
|
|
+
|
|
|
+ dayList := getPredictEdbDayList(startDate, endDate, frequency)
|
|
|
+ predictEdbInfoData = make([]*EdbInfoSearchData, 0)
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+
|
|
|
+ tmpData := &EdbInfoSearchData{
|
|
|
+ EdbDataId: edbInfoId + 10000000000 + index + k,
|
|
|
+ DataTime: currentDate.Format(utils.FormatDate),
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ var val float64
|
|
|
+ var calculateStatus bool
|
|
|
+
|
|
|
+
|
|
|
+ preDate := currentDate.AddDate(-1, 0, 0)
|
|
|
+ preDateStr := preDate.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[preDateStr]; ok {
|
|
|
+ val = PredictTczDiv(preValue, tcValue)
|
|
|
+ calculateStatus = true
|
|
|
+ } else {
|
|
|
+ switch frequency {
|
|
|
+ case "月度":
|
|
|
+
|
|
|
+ nextDateDay := preDate
|
|
|
+ preDateDay := preDate
|
|
|
+ for i := 0; i <= 35; i++ {
|
|
|
+ nextDateDayStr := nextDateDay.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[nextDateDayStr]; ok {
|
|
|
+ val = PredictTczDiv(preValue, tcValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ preDateDayStr := preDateDay.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[preDateDayStr]; ok {
|
|
|
+ val = PredictTczDiv(preValue, tcValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ nextDateDay = nextDateDay.AddDate(0, 0, 1)
|
|
|
+ preDateDay = preDateDay.AddDate(0, 0, -1)
|
|
|
+ }
|
|
|
+
|
|
|
+ case "季度", "年度":
|
|
|
+ if preValue, ok := existMap[preDateStr]; ok {
|
|
|
+ val = PredictTczDiv(preValue, tcValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ nextDateDay := preDate
|
|
|
+ preDateDay := preDate
|
|
|
+
|
|
|
+ for i := 0; i < 35; i++ {
|
|
|
+ nextDateDayStr := nextDateDay.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[nextDateDayStr]; ok {
|
|
|
+ val = PredictTczDiv(preValue, tcValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ preDateDayStr := preDateDay.Format(utils.FormatDate)
|
|
|
+ if preValue, ok := existMap[preDateDayStr]; ok {
|
|
|
+ val = PredictTczDiv(preValue, tcValue)
|
|
|
+ calculateStatus = true
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ nextDateDay = nextDateDay.AddDate(0, 0, 1)
|
|
|
+ preDateDay = preDateDay.AddDate(0, 0, -1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if calculateStatus {
|
|
|
+ tmpData.Value = val
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
+
|
|
|
+
|
|
|
+ if val < minValue {
|
|
|
+ minValue = val
|
|
|
+ }
|
|
|
+ if val < maxValue {
|
|
|
+ maxValue = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func PredictTczDiv(a, b float64) (result float64) {
|
|
|
+ if b != 0 {
|
|
|
+
|
|
|
+ af := decimal.NewFromFloat(a)
|
|
|
+
|
|
|
+
|
|
|
+ bf := decimal.NewFromFloat(b)
|
|
|
+
|
|
|
+
|
|
|
+ result, _ = af.Add(bf).RoundCeil(4).Float64()
|
|
|
+ } else {
|
|
|
+ result = 0
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRuleHb(edbInfoId int, hbValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+ index := len(predictEdbInfoData)
|
|
|
+
|
|
|
+ dayList := getPredictEdbDayList(startDate, endDate, frequency)
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+ tmpK := index + k - 1
|
|
|
+
|
|
|
+
|
|
|
+ val := PredictHbzDiv(newPredictEdbInfoData[tmpK].Value, hbValue)
|
|
|
+
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, &EdbInfoSearchData{
|
|
|
+ EdbDataId: edbInfoId + 10000000000 + index + k,
|
|
|
+ DataTime: currentDateStr,
|
|
|
+ Value: val,
|
|
|
+ })
|
|
|
+ existMap[currentDateStr] = val
|
|
|
+
|
|
|
+
|
|
|
+ if val < minValue {
|
|
|
+ minValue = val
|
|
|
+ }
|
|
|
+ if val < maxValue {
|
|
|
+ maxValue = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func PredictHbzDiv(a, b float64) (result float64) {
|
|
|
+ if b != 0 {
|
|
|
+
|
|
|
+ af := decimal.NewFromFloat(a)
|
|
|
+
|
|
|
+
|
|
|
+ bf := decimal.NewFromFloat(b)
|
|
|
+
|
|
|
+
|
|
|
+ cf := decimal.NewFromFloat(1)
|
|
|
+
|
|
|
+
|
|
|
+ val := bf.Add(cf)
|
|
|
+
|
|
|
+
|
|
|
+ result, _ = val.Mul(af).RoundCeil(4).Float64()
|
|
|
+ } else {
|
|
|
+ result = 0
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRuleHc(edbInfoId int, hcValue float64, startDate, endDate time.Time, frequency string, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+ index := len(predictEdbInfoData)
|
|
|
+
|
|
|
+ dayList := getPredictEdbDayList(startDate, endDate, frequency)
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+ tmpK := index + k - 1
|
|
|
+
|
|
|
+
|
|
|
+ val := PredictHczDiv(newPredictEdbInfoData[tmpK].Value, hcValue)
|
|
|
+
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, &EdbInfoSearchData{
|
|
|
+ EdbDataId: edbInfoId + 10000000000 + index + k,
|
|
|
+ DataTime: currentDateStr,
|
|
|
+ Value: val,
|
|
|
+ })
|
|
|
+ existMap[currentDateStr] = val
|
|
|
+
|
|
|
+
|
|
|
+ if val < minValue {
|
|
|
+ minValue = val
|
|
|
+ }
|
|
|
+ if val < maxValue {
|
|
|
+ maxValue = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func PredictHczDiv(a, b float64) (result float64) {
|
|
|
+ if b != 0 {
|
|
|
+
|
|
|
+ af := decimal.NewFromFloat(a)
|
|
|
+
|
|
|
+
|
|
|
+ bf := decimal.NewFromFloat(b)
|
|
|
+
|
|
|
+
|
|
|
+ result, _ = af.Add(bf).RoundCeil(4).Float64()
|
|
|
+ } else {
|
|
|
+ result = 0
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRuleNMoveMeanValue(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
|
|
|
+ allDataList := make([]*EdbInfoSearchData, 0)
|
|
|
+ allDataList = append(allDataList, realPredictEdbInfoData...)
|
|
|
+ allDataList = append(allDataList, predictEdbInfoData...)
|
|
|
+
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+
|
|
|
+ lenAllData := len(allDataList)
|
|
|
+ if lenAllData < nValue || lenAllData <= 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if nValue <= 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ decimalN := decimal.NewFromInt(int64(nValue))
|
|
|
+
|
|
|
+
|
|
|
+ dayList := getPredictEdbDayList(startDate, endDate, frequency)
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+ tmpIndex := lenAllData + k - 1
|
|
|
+
|
|
|
+
|
|
|
+ tmpDecimalVal := decimal.NewFromFloat(allDataList[tmpIndex].Value)
|
|
|
+ for tmpK := 2; tmpK <= nValue; tmpK++ {
|
|
|
+ tmpIndex2 := tmpIndex - tmpK
|
|
|
+ tmpDecimalVal2 := decimal.NewFromFloat(allDataList[tmpIndex2].Value)
|
|
|
+ tmpDecimalVal = tmpDecimalVal.Add(tmpDecimalVal2)
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ val, _ := tmpDecimalVal.Div(decimalN).RoundCeil(4).Float64()
|
|
|
+
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ tmpData := &EdbInfoSearchData{
|
|
|
+ EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
|
|
|
+ DataTime: currentDateStr,
|
|
|
+ Value: val,
|
|
|
+ }
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
+ allDataList = append(allDataList, tmpData)
|
|
|
+ existMap[currentDateStr] = val
|
|
|
+
|
|
|
+
|
|
|
+ if val < minValue {
|
|
|
+ minValue = val
|
|
|
+ }
|
|
|
+ if val < maxValue {
|
|
|
+ maxValue = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func GetChartPredictEdbInfoDataListByRuleNLinearRegression(edbInfoId int, nValue int, startDate, endDate time.Time, frequency string, realPredictEdbInfoData, predictEdbInfoData []*EdbInfoSearchData, existMap map[string]float64) (newPredictEdbInfoData []*EdbInfoSearchData, minValue, maxValue float64) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ allDataList := make([]*EdbInfoSearchData, 0)
|
|
|
+ allDataList = append(allDataList, realPredictEdbInfoData...)
|
|
|
+ allDataList = append(allDataList, predictEdbInfoData...)
|
|
|
+
|
|
|
+ newPredictEdbInfoData = predictEdbInfoData
|
|
|
+
|
|
|
+ lenAllData := len(allDataList)
|
|
|
+ if lenAllData < nValue || lenAllData <= 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if nValue <= 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ coordinateData := make([]Coordinate, 0)
|
|
|
+ for tmpK := nValue; tmpK > 0; tmpK-- {
|
|
|
+ tmpIndex2 := lenAllData - tmpK
|
|
|
+ tmpCoordinate := Coordinate{
|
|
|
+ X: float64(nValue - tmpK + 1),
|
|
|
+ Y: allDataList[tmpIndex2].Value,
|
|
|
+ }
|
|
|
+ coordinateData = append(coordinateData, tmpCoordinate)
|
|
|
+ }
|
|
|
+ a, b := getLinearResult(coordinateData)
|
|
|
+
|
|
|
+
|
|
|
+ dayList := getPredictEdbDayList(startDate, endDate, frequency)
|
|
|
+ for k, currentDate := range dayList {
|
|
|
+ tmpK := nValue + k + 1
|
|
|
+
|
|
|
+ aDecimal := decimal.NewFromFloat(a)
|
|
|
+ xDecimal := decimal.NewFromInt(int64(tmpK))
|
|
|
+ bDecimal := decimal.NewFromFloat(b)
|
|
|
+
|
|
|
+ val, _ := aDecimal.Mul(xDecimal).Add(bDecimal).RoundCeil(4).Float64()
|
|
|
+
|
|
|
+ currentDateStr := currentDate.Format(utils.FormatDate)
|
|
|
+ tmpData := &EdbInfoSearchData{
|
|
|
+ EdbDataId: edbInfoId + 10000000000 + lenAllData + k,
|
|
|
+ DataTime: currentDateStr,
|
|
|
+ Value: val,
|
|
|
+ }
|
|
|
+ newPredictEdbInfoData = append(newPredictEdbInfoData, tmpData)
|
|
|
+ allDataList = append(allDataList, tmpData)
|
|
|
+ existMap[currentDateStr] = val
|
|
|
+
|
|
|
+
|
|
|
+ if val < minValue {
|
|
|
+ minValue = val
|
|
|
+ }
|
|
|
+ if val < maxValue {
|
|
|
+ maxValue = val
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type Series []Coordinate
|
|
|
+
|
|
|
+
|
|
|
+type Coordinate struct {
|
|
|
+ X, Y float64
|
|
|
+}
|
|
|
+
|
|
|
+func getLinearResult(s []Coordinate) (gradient, intercept float64) {
|
|
|
+ if len(s) == 0 {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ var sum [5]float64
|
|
|
+
|
|
|
+
|
|
|
+ i := 0
|
|
|
+ for ; i < len(s); i++ {
|
|
|
+ sum[0] += s[i].X
|
|
|
+ sum[1] += s[i].Y
|
|
|
+ sum[2] += s[i].X * s[i].X
|
|
|
+ sum[3] += s[i].X * s[i].Y
|
|
|
+ sum[4] += s[i].Y * s[i].Y
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ f := float64(i)
|
|
|
+ gradient = (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0])
|
|
|
+ intercept = (sum[1] / f) - (gradient * sum[0] / f)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ return
|
|
|
+}
|